Observed since 20 Oct, 22:00 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
@021.is/spine-lint
Spine-specific AST checks that biome can't express: enum-over-string, ResponseDto enforcement, endpoint documentation, i18n key parity, no raw SQL. Runs in CI as the spine-lint job + locally via `spine-lint .`.
Spine-specific AST checks that biome can't express. Runs in CI as the spine-lint job in the quality gate; also runs locally via spine-lint ..
| Rule | Severity | What |
|---|---|---|
spine/enum-over-string | error / warning | Forbids inline string-literal unions (: "a" | "b" | "c"). Warns on repeated string-literal comparisons (x === "foo" ≥2 times). Locked. |
spine/route-returns-response-dto | error | Every Next.js route handler must go through withErrorHandling from @021.is/spine-errors/next OR explicitly call ok() / err(). |
spine/endpoint-documented | warning | Every route handler must have a JSDoc block above it. |
spine/i18n-key-parity | error | Every t("namespace.key") exists in every locale catalog under src/i18n/. Prevents the silent UX bug where production renders raw keys in foreign locales. |
spine/no-raw-sql | error | Blocks $queryRawUnsafe / $executeRawUnsafe. Use Prisma or $queryRaw\…`` tagged-template. |
spine-lint . # run all rules on src/**
spine-lint src/feature/event # narrow to one path
spine-lint --rule spine/enum-over-string .
spine-lint --github . # emit GitHub Actions annotations
spine-lint --list-rules
Exit code 1 on any error-level violation. Used as a required check in the spine-quality-gate workflow.
// spine-lint-disable-next-line spine/no-raw-sql — needed for the pg_stat query
await prisma.$queryRawUnsafe(`SELECT * FROM pg_stat_activity`);
(Disables ALL rules on the next line if no rule id given.) You must always include the WHY comment. PR reviewers + future-you will look there first.
// ❌ inline union
function setStatus(s: "active" | "inactive" | "deleted") { ... }
// ✓ const-as-object enum
export const STATUS = { ACTIVE: "active", INACTIVE: "inactive", DELETED: "deleted" } as const;
export type Status = (typeof STATUS)[keyof typeof STATUS];
function setStatus(s: Status) { ... }
Why const-as-object instead of enum:
enum compiles to runtime objects).as const and Object.values().Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
cacEnvironment{
"local_name": "cac",
"alias": false,
"peer_metadata": null
} | ^6.7.14 | runtime |
kleurEnvironment{
"local_name": "kleur",
"alias": false,
"peer_metadata": null
} | ^4.1.5 | runtime |
fast-globEnvironment{
"local_name": "fast-glob",
"alias": false,
"peer_metadata": null
} | ^3.3.2 | runtime |
@typescript-eslint/typescript-estreeEnvironment{
"local_name": "@typescript-eslint/typescript-estree",
"alias": false,
"peer_metadata": null
} | ^8.10.0 | runtime |
Source distribution
{
"shasum": "8204739530f16637e41bebf52afe1ffa493881e1",
"tarball": "https://registry.npmjs.org/@021.is/spine-lint/-/spine-lint-0.4.2.tgz",
"fileCount": 7,
"integrity": "sha512-PvCsvq7VZhnvNF89VeB1BFlJtLTFAMdsT9S5nMgHmTJTNil5BMTZKMVidN6n//rJkViSXglhEAN2TSFJ+Gxn7Q==",
"signatures": [
{
"sig": "MEQCIDJkNpuLiWQ8xmM5Drb9bm4IVb3N4UJ3v23TOmtxm6U/AiARrZIjuKAJ8zvgAZg/uhlHrhq846MgX/JKrRc8TcbQAg==",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 23476
}Observed since 20 Oct, 22:00 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:70ae4d105a740d90b6028884e560ca27613193244bb78ccdfd34e2c0cd963bcf