@shivaedev/quality 0.32.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.33.0 - 2026-10-21
4
+
5
+ ### Breaking
6
+
7
+ - Remove the `tests/support-imports` rule and its module allowlist. Delete the `tests/support-imports` block from your quality config; configs that still name it fail validation. Story kits may import any workspace module; how tests use those modules belongs to testing guidance and review.
8
+
3
9
  ## 0.32.1 - 2026-10-21
4
10
 
5
11
  ### Changed
package/README.md CHANGED
@@ -126,7 +126,7 @@ export default defineConfig({
126
126
 
127
127
  ### Story tests
128
128
 
129
- Write setup in the domain kit under `test-support/` and keep each test specific to its story. [`@shivaedev/test-story`](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme) explains how to build the kit, traits and verbs over a real engine.
129
+ Keep story kits and fixtures in `src/test-support` and keep each spec specific to its story. [`@shivaedev/test-story`](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme) explains how to build the kit, traits and verbs over a real engine.
130
130
 
131
131
  Quality's `tests/story-specs` rule reports a test file that declares tests without a kit's `it`, including plain Vitest `it` and `test` declarations mixed with stories. Typecheck tests are exempt, as are test-story itself and its transitive workspace dependencies, including development, optional and peer dependencies: requiring those packages to depend on test-story would create a cycle.
132
132
 
@@ -140,8 +140,6 @@ Quality's `tests/story-setup` rule reports top-level test functions named with t
140
140
 
141
141
  Specs keep expectations visible. `tests/spec-helpers` moves all module-level helper functions out of specs and into the kit; `tests/no-expect-in-support` moves `expect` calls into the spec. `tests/named-values` rejects numeric literals passed to `toBe` or `toHaveLength` in specs: assert the observed items or give the value a domain name. `tests/no-limit-imports` rejects importing `MAX_*` constants from a `limits` module into specs, so a story proves behavior instead of copying an implementation cap.
142
142
 
143
- `tests/support-imports` lets a kit drive any workspace package's exported modules, including its own package's exports through package names or `#` aliases. Export blocks keep internal modules closed. `options.modules` opens additional repository-relative module paths or glob patterns, such as an app's host, schemas and queue definitions. Packages without exports still need that allowlist. Imports within test-support and imports of external packages remain available for the harness; aliases, dynamic imports and re-exports resolve through the same import graph as the other import rules.
144
-
145
143
  `boundaries/queue-sql` rejects SQL mentioning the private `pgboss.` schema. Apps use their queue definitions and let workers process jobs; there is no queue-purge API by design. `boundaries/auth-sql` rejects SQL accessing Better Auth's `user`, `account`, `session` and `verification` tables. Apps use platform's auth API; `options.owners` names platform's own auth implementation and its migrations. These rules inspect SQL templates, literal query calls and query variables, and inventoried `.sql` files. They do not evaluate arbitrary runtime SQL construction.
146
144
 
147
145
  `boundaries/http-status` rejects comparisons with HTTP error status numbers, switch cases and literal status-list membership outside `options.edge`. Domain code handles typed errors; the named HTTP edge translates them into transport responses. Process exit codes remain ordinary domain values.
@@ -153,7 +151,6 @@ export default defineConfig({
153
151
  rules: {
154
152
  "boundaries/auth-sql": { options: { owners: ["platform/auth/**", "platform/auth-migrations/**"] } },
155
153
  "boundaries/http-status": { options: { edge: ["src/http/**"] } },
156
- "tests/support-imports": { options: { modules: ["src/host.ts", "src/schemas.ts", "src/queues.ts"] } },
157
154
  },
158
155
  });
159
156
  ```
@@ -30,9 +30,7 @@ export declare const builtInRules: readonly [import("../rule.ts").Rule<"structur
30
30
  readonly suites?: readonly string[];
31
31
  }>, import("../rule.ts").Rule<"tests/story-setup", undefined>, import("../rule.ts").Rule<"tests/story-specs", undefined>, import("../rule.ts").Rule<"boundaries/queue-sql", undefined>, import("../rule.ts").Rule<"boundaries/auth-sql", {
32
32
  readonly owners?: readonly string[];
33
- }>, import("../rule.ts").Rule<"tests/no-expect-in-support", undefined>, import("../rule.ts").Rule<"tests/spec-helpers", undefined>, import("../rule.ts").Rule<"tests/support-imports", {
34
- readonly modules?: readonly string[];
35
- }>, import("../rule.ts").Rule<"boundaries/http-status", {
33
+ }>, import("../rule.ts").Rule<"tests/no-expect-in-support", undefined>, import("../rule.ts").Rule<"tests/spec-helpers", undefined>, import("../rule.ts").Rule<"boundaries/http-status", {
36
34
  readonly edge?: readonly string[];
37
35
  }>, import("../rule.ts").Rule<"tests/named-values", undefined>, import("../rule.ts").Rule<"tests/no-limit-imports", undefined>];
38
36
  //# sourceMappingURL=builtIn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"AA2CA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HA2Cf,CAAC"}
1
+ {"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HA0Cf,CAAC"}
@@ -25,7 +25,6 @@ import { queueSql } from "#rules/stories/queueSql.ts";
25
25
  import { storySetup } from "#rules/stories/setup.ts";
26
26
  import { specHelpers } from "#rules/stories/specHelpers.ts";
27
27
  import { storySpecs } from "#rules/stories/specs.ts";
28
- import { supportImports } from "#rules/stories/supportImports.ts";
29
28
  import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
30
29
  import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
31
30
  import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
@@ -79,7 +78,6 @@ export const builtInRules = [
79
78
  authSql,
80
79
  noExpect,
81
80
  specHelpers,
82
- supportImports,
83
81
  httpStatus,
84
82
  namedValues,
85
83
  noLimitImports,
@@ -1 +1 @@
1
- {"version":3,"file":"builtIn.js","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builtIn.js","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/quality",
3
- "version": "0.32.1",
3
+ "version": "0.33.0",
4
4
  "description": "Repository quality gate with typed rules, a baseline of existing debt and a registry of reasoned exceptions",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/quality#readme",
6
6
  "bugs": {
@@ -25,7 +25,6 @@ import { queueSql } from "#rules/stories/queueSql.ts";
25
25
  import { storySetup } from "#rules/stories/setup.ts";
26
26
  import { specHelpers } from "#rules/stories/specHelpers.ts";
27
27
  import { storySpecs } from "#rules/stories/specs.ts";
28
- import { supportImports } from "#rules/stories/supportImports.ts";
29
28
  import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
30
29
  import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
31
30
  import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
@@ -80,7 +79,6 @@ export const builtInRules = [
80
79
  authSql,
81
80
  noExpect,
82
81
  specHelpers,
83
- supportImports,
84
82
  httpStatus,
85
83
  namedValues,
86
84
  noLimitImports,
@@ -1,4 +0,0 @@
1
- import type { RuleInputs } from "#rule.ts";
2
- import type { ImportGraph } from "./graph.ts";
3
- export declare function publicModules(inputs: RuleInputs, graph: ImportGraph): Promise<ReadonlySet<string>>;
4
- //# sourceMappingURL=publicModules.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"publicModules.d.ts","sourceRoot":"","sources":["../../src/imports/publicModules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAoBxG"}
@@ -1,21 +0,0 @@
1
- import { posix } from "node:path";
2
- import { exportedModule } from "./packageExports.js";
3
- import { packageOf } from "./workspace.js";
4
- export async function publicModules(inputs, graph) {
5
- const exports = new Map(await Promise.all(graph.packages.map(async (owner) => {
6
- const manifest = JSON.parse((await inputs.readText(posix.join(owner.directory, "package.json"))) ?? "");
7
- return [owner, manifest.exports];
8
- })));
9
- const modules = new Set();
10
- for (const edge of graph.edges) {
11
- if (edge.to.kind !== "file") {
12
- continue;
13
- }
14
- const owner = packageOf(graph.packages, edge.to.path);
15
- if (owner !== undefined && exportedModule(exports.get(owner), posix.relative(owner.directory, edge.to.path))) {
16
- modules.add(edge.to.path);
17
- }
18
- }
19
- return modules;
20
- }
21
- //# sourceMappingURL=publicModules.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"publicModules.js","sourceRoot":"","sources":["../../src/imports/publicModules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAkB,EAAE,KAAkB;IACzE,MAAM,OAAO,GAAG,IAAI,GAAG,CACtB,MAAM,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAClC,MAAM,QAAQ,GAAmC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxI,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAU,CAAC;IAC3C,CAAC,CAAC,CACF,CACD,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC7B,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,KAAK,KAAK,SAAS,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC9G,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC"}
@@ -1,4 +0,0 @@
1
- export declare const supportImports: import("#rule.ts").Rule<"tests/support-imports", {
2
- readonly modules?: readonly string[];
3
- }>;
4
- //# sourceMappingURL=supportImports.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"supportImports.d.ts","sourceRoot":"","sources":["../../../src/rules/stories/supportImports.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,cAAc;;EA0BzB,CAAC"}
@@ -1,34 +0,0 @@
1
- import { Effect, Schema } from "effect";
2
- import { importGraph } from "#imports/graph.ts";
3
- import { publicModules } from "#imports/publicModules.ts";
4
- import { isTestSupport } from "#naming/testName.ts";
5
- import { defineRule } from "#rule.ts";
6
- import { matchesAny } from "./matchesAny.js";
7
- const SupportImportsOptions = Schema.Struct({
8
- modules: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
9
- });
10
- export const supportImports = defineRule({
11
- check: async ({ options, ...inputs }) => {
12
- if (!inputs.sources.some((file) => isTestSupport(file.path))) {
13
- return [];
14
- }
15
- const graph = await importGraph(inputs);
16
- const exported = await publicModules(inputs, graph);
17
- return graph.edges
18
- .filter((edge) => isTestSupport(edge.from)
19
- && edge.to.kind === "file"
20
- && !isTestSupport(edge.to.path)
21
- && !exported.has(edge.to.path)
22
- && !matchesAny(edge.to.path, options.modules))
23
- .map((edge) => ({
24
- file: edge.from,
25
- line: edge.line,
26
- message: `Test-support imports "${edge.specifier}" outside its allowed public surface. Drive the app through its host, schemas or queue definitions, or a workspace package's exports; list additional internal module paths in tests/support-imports.options.modules in the repository's quality config.`,
27
- subject: edge.specifier,
28
- }));
29
- },
30
- description: "Test-support reaches repository modules through workspace package exports and the repository's additional module allowlist.",
31
- id: "tests/support-imports",
32
- options: Schema.toStandardSchemaV1(SupportImportsOptions, { parseOptions: { errors: "all", onExcessProperty: "error" } }),
33
- });
34
- //# sourceMappingURL=supportImports.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"supportImports.js","sourceRoot":"","sources":["../../../src/rules/stories/supportImports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;CAC5F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;IACxC,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE;QACvC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC,KAAK;aAChB,MAAM,CACN,CAAC,IAAI,EAAE,EAAE,CACR,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;eACrB,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM;eACvB,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;eAC5B,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;eAC3B,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAC9C;aACA,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,yBAAyB,IAAI,CAAC,SAAS,0PAA0P;YAC1S,OAAO,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;IACN,CAAC;IACD,WAAW,EAAE,6HAA6H;IAC1I,EAAE,EAAE,uBAAuB;IAC3B,OAAO,EAAE,MAAM,CAAC,kBAAkB,CAAC,qBAAqB,EAAE,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC;CACzH,CAAC,CAAC"}
@@ -1,27 +0,0 @@
1
- import { posix } from "node:path";
2
- import type { RuleInputs } from "#rule.ts";
3
- import type { ImportGraph } from "./graph.ts";
4
- import { exportedModule } from "./packageExports.ts";
5
- import { packageOf } from "./workspace.ts";
6
-
7
- export async function publicModules(inputs: RuleInputs, graph: ImportGraph): Promise<ReadonlySet<string>> {
8
- const exports = new Map(
9
- await Promise.all(
10
- graph.packages.map(async (owner) => {
11
- const manifest: { readonly exports?: unknown } = JSON.parse((await inputs.readText(posix.join(owner.directory, "package.json"))) ?? "");
12
- return [owner, manifest.exports] as const;
13
- }),
14
- ),
15
- );
16
- const modules = new Set<string>();
17
- for (const edge of graph.edges) {
18
- if (edge.to.kind !== "file") {
19
- continue;
20
- }
21
- const owner = packageOf(graph.packages, edge.to.path);
22
- if (owner !== undefined && exportedModule(exports.get(owner), posix.relative(owner.directory, edge.to.path))) {
23
- modules.add(edge.to.path);
24
- }
25
- }
26
- return modules;
27
- }
@@ -1,38 +0,0 @@
1
- import { Effect, Schema } from "effect";
2
- import { importGraph } from "#imports/graph.ts";
3
- import { publicModules } from "#imports/publicModules.ts";
4
- import { isTestSupport } from "#naming/testName.ts";
5
- import { defineRule } from "#rule.ts";
6
- import { matchesAny } from "./matchesAny.ts";
7
-
8
- const SupportImportsOptions = Schema.Struct({
9
- modules: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
10
- });
11
-
12
- export const supportImports = defineRule({
13
- check: async ({ options, ...inputs }) => {
14
- if (!inputs.sources.some((file) => isTestSupport(file.path))) {
15
- return [];
16
- }
17
- const graph = await importGraph(inputs);
18
- const exported = await publicModules(inputs, graph);
19
- return graph.edges
20
- .filter(
21
- (edge) =>
22
- isTestSupport(edge.from)
23
- && edge.to.kind === "file"
24
- && !isTestSupport(edge.to.path)
25
- && !exported.has(edge.to.path)
26
- && !matchesAny(edge.to.path, options.modules),
27
- )
28
- .map((edge) => ({
29
- file: edge.from,
30
- line: edge.line,
31
- message: `Test-support imports "${edge.specifier}" outside its allowed public surface. Drive the app through its host, schemas or queue definitions, or a workspace package's exports; list additional internal module paths in tests/support-imports.options.modules in the repository's quality config.`,
32
- subject: edge.specifier,
33
- }));
34
- },
35
- description: "Test-support reaches repository modules through workspace package exports and the repository's additional module allowlist.",
36
- id: "tests/support-imports",
37
- options: Schema.toStandardSchemaV1(SupportImportsOptions, { parseOptions: { errors: "all", onExcessProperty: "error" } }),
38
- });