@terpjs/eslint-boundaries 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terpjs/eslint-boundaries",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Terp frontend boundary rules (as data) + the ESLint adapter: no cross-module imports, no package internals, design-token-only styling (no style/className/module stylesheets), token-styled components for raw HTML tags, router-only in-app links, generated-client-only, and browser XSS/navigation sink bans. Strict-only (no modes); governed opt-outs via terp-allow markers + the escape-hatch budget ratchet (terp-boundaries-budget).",
6
6
  "main": "./src/index.js",
@@ -25,7 +25,7 @@
25
25
  "typescript-eslint": "^8.20.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@terp/spec": "github:AITT-NL/terp-spec#v0.14.0",
28
+ "@terpjs/spec": "0.16.0",
29
29
  "eslint": "^9.18.0",
30
30
  "vitest": "^3.0.0"
31
31
  },
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Shared by the certification test (corpus.test.js) and the scorecard emitter
6
6
  * (scorecard.js), so the two can never apply different corpus semantics. The spec
7
- * is resolved as a declared dependency (@terp/spec, ADR 0082) by the CALLER —
7
+ * is resolved as a declared dependency (@terpjs/spec, ADR 0082) by the CALLER —
8
8
  * this module takes paths, never resolves the spec itself.
9
9
  */
10
10
 
@@ -22,10 +22,9 @@ import { describe, expect, it } from "vitest";
22
22
 
23
23
  import { lintCaseFindings } from "./corpus-harness.js";
24
24
 
25
- // The spec is a declared dependency (@terp/spec, ADR 0082), never a repo-relative path
26
- // inside the monorepo it resolves to the workspace member; after a repo split, to the pin.
25
+ // The spec is a declared dependency (@terpjs/spec, ADR 0082), never a repo-relative path.
27
26
  const SPEC_ROOT = path.dirname(
28
- createRequire(import.meta.url).resolve("@terp/spec/package.json"),
27
+ createRequire(import.meta.url).resolve("@terpjs/spec/package.json"),
29
28
  );
30
29
  const CATALOG = path.join(SPEC_ROOT, "catalog", "frontend");
31
30
  const CORPUS = path.join(SPEC_ROOT, "corpus", "frontend");
package/src/findings.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * check `terp-boundaries-budget` runs) in one command, and publishes one **findings
8
8
  * envelope** on stdout:
9
9
  *
10
- * { "terp_findings": 1, "tool": "@terp/eslint-boundaries",
10
+ * { "terp_findings": 1, "tool": "@terpjs/eslint-boundaries",
11
11
  * "rules": ["frontend/<rule>", …], // every catalog rule this run evaluated
12
12
  * "not_applicable": ["frontend/<rule>", …], // opt-in rules this app has not enabled
13
13
  * "findings": [{ rule, path, line, message }, …], // spec findings.schema.json shape
@@ -128,7 +128,7 @@ export function renderEnvelope(results, cwd = process.cwd(), options = {}) {
128
128
  return {
129
129
  envelope: {
130
130
  terp_findings: 1,
131
- tool: "@terp/eslint-boundaries",
131
+ tool: "@terpjs/eslint-boundaries",
132
132
  rules: catalogRuleIds().filter((id) => !notApplicable.includes(id)),
133
133
  not_applicable: notApplicable,
134
134
  findings,
@@ -16,7 +16,7 @@ import { SPEC_VERSION } from "./spec.js";
16
16
  // stack-neutral catalog ids, humans keep stderr, and the exit code stays the verdict.
17
17
 
18
18
  const SPEC_ROOT = path.dirname(
19
- createRequire(import.meta.url).resolve("@terp/spec/package.json"),
19
+ createRequire(import.meta.url).resolve("@terpjs/spec/package.json"),
20
20
  );
21
21
  const FINDINGS_BIN = fileURLToPath(new URL("./findings.js", import.meta.url));
22
22
 
@@ -71,7 +71,7 @@ describe("renderEnvelope", () => {
71
71
  layoutContract: true,
72
72
  });
73
73
  expect(envelope.terp_findings).toBe(1);
74
- expect(envelope.tool).toBe("@terp/eslint-boundaries");
74
+ expect(envelope.tool).toBe("@terpjs/eslint-boundaries");
75
75
  expect(envelope.rules).toEqual(catalogRuleIds());
76
76
  expect(envelope.not_applicable).toEqual([]);
77
77
  const rules = envelope.findings.map((finding) => finding.rule);
@@ -252,11 +252,11 @@ describe("the check report (--format check-report, app-check-report.schema.json)
252
252
  const report = asCheckReport(envelope);
253
253
  expect(report.terp_check_report).toBe(1);
254
254
  // The certified spec version rides every report. Shape only here — the
255
- // equality lock against the pinned @terp/spec lives in the framework gate
255
+ // equality lock against the pinned @terpjs/spec lives in the framework gate
256
256
  // (test_check_json.py), because certification runs THIS suite against
257
257
  // candidate spec releases whose version is allowed to be newer.
258
258
  expect(report.spec_version).toMatch(/^\d+\.\d+\.\d+$/);
259
- expect(report.checker.tool).toBe("@terp/eslint-boundaries");
259
+ expect(report.checker.tool).toBe("@terpjs/eslint-boundaries");
260
260
  expect(report.checker.version).toMatch(/^\d+\.\d+\.\d+$/);
261
261
  expect(report.ok).toBe(false);
262
262
  expect(report.rules).toEqual(catalogRuleIds());
package/src/index.test.js CHANGED
@@ -187,7 +187,7 @@ describe("terpBoundaries", () => {
187
187
  });
188
188
 
189
189
  it("flags a deep import into a package's internals", async () => {
190
- const code = 'import x from "@terp/react-core/src/secret";\nexport const W = () => null;';
190
+ const code = 'import x from "@terpjs/react-core/src/secret";\nexport const W = () => null;';
191
191
  expect(await lint(code)).toContain("no-restricted-imports");
192
192
  });
193
193
 
package/src/scorecard.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Emit the Terp Standard conformance scorecard for `@terp/eslint-boundaries`
3
+ * Emit the Terp Standard conformance scorecard for `@terpjs/eslint-boundaries`
4
4
  * (the frontend checker).
5
5
  *
6
6
  * The scorecard (`scorecard.schema.json` in the spec) turns "certified against
@@ -9,7 +9,7 @@
9
9
  * residuals the adapter relies on (held to a subset of the spec's recorded
10
10
  * `corpus/RESIDUALS.json`). A consumer re-runs the corpus and reproduces it.
11
11
  *
12
- * A certification-context tool, not an app tool: it needs `@terp/spec` (a dev
12
+ * A certification-context tool, not an app tool: it needs `@terpjs/spec` (a dev
13
13
  * dependency of the platform repo) and runs the SAME harness the corpus test
14
14
  * uses (./corpus-harness.js), so the scorecard can never disagree with the
15
15
  * suite. Self-validates and refuses to write an invalid or failing scorecard
@@ -31,12 +31,12 @@ const require = createRequire(import.meta.url);
31
31
  const RULE_ID_RE = /^(backend\/[a-z0-9_]+|frontend\/[a-z0-9-]+)$/;
32
32
  const SEMVER_RE = /^\d+\.\d+\.\d+$/;
33
33
 
34
- /** The @terp/spec root — resolved lazily so importing this module never requires it. */
34
+ /** The @terpjs/spec root — resolved lazily so importing this module never requires it. */
35
35
  function specRoot() {
36
- return path.dirname(require.resolve("@terp/spec/package.json"));
36
+ return path.dirname(require.resolve("@terpjs/spec/package.json"));
37
37
  }
38
38
 
39
- /** Build the @terp/eslint-boundaries scorecard over the frontend corpus. */
39
+ /** Build the @terpjs/eslint-boundaries scorecard over the frontend corpus. */
40
40
  export async function buildScorecard() {
41
41
  const root = specRoot();
42
42
  const catalogDir = path.join(root, "catalog", "frontend");
@@ -70,7 +70,7 @@ export async function buildScorecard() {
70
70
  }
71
71
  return {
72
72
  spec_version: fs.readFileSync(path.join(root, "VERSION"), "utf8").trim(),
73
- checker: { tool: "@terp/eslint-boundaries", version: packageVersion },
73
+ checker: { tool: "@terpjs/eslint-boundaries", version: packageVersion },
74
74
  rules,
75
75
  };
76
76
  }
@@ -15,10 +15,10 @@ import { describe, expect, it } from "vitest";
15
15
  import { buildScorecard, validateScorecard } from "./scorecard.js";
16
16
 
17
17
  const SPEC_ROOT = path.dirname(
18
- createRequire(import.meta.url).resolve("@terp/spec/package.json"),
18
+ createRequire(import.meta.url).resolve("@terpjs/spec/package.json"),
19
19
  );
20
20
 
21
- describe("the @terp/eslint-boundaries scorecard", () => {
21
+ describe("the @terpjs/eslint-boundaries scorecard", () => {
22
22
  it("claims the whole corpus-covered catalog, green, schema-shaped", async () => {
23
23
  const scorecard = await buildScorecard();
24
24
  expect(validateScorecard(scorecard)).toEqual([]);
@@ -34,7 +34,7 @@ describe("the @terp/eslint-boundaries scorecard", () => {
34
34
  expect(scorecard.spec_version).toBe(
35
35
  fs.readFileSync(path.join(SPEC_ROOT, "VERSION"), "utf8").trim(),
36
36
  );
37
- expect(scorecard.checker.tool).toBe("@terp/eslint-boundaries");
37
+ expect(scorecard.checker.tool).toBe("@terpjs/eslint-boundaries");
38
38
  const itemProperties = Object.keys(schema.properties.rules.items.properties);
39
39
  for (const claim of scorecard.rules) {
40
40
  expect(claim.pass).toBe(true);
package/src/spec.js CHANGED
@@ -9,13 +9,13 @@
9
9
  /**
10
10
  * The Terp Standard version this adapter is certified against — the `spec_version` a
11
11
  * check report (`app-check-report.schema.json`) carries. A constant rather than a runtime
12
- * `@terp/spec` read: the spec data package is a dev/certification dependency of the platform
12
+ * `@terpjs/spec` read: the spec data package is a dev/certification dependency of the platform
13
13
  * repo, not of a generated app, and the version is a property of the toolchain build. Held
14
14
  * equal to the pinned spec release by the framework gate (test_check_json.py — deliberately
15
15
  * NOT by this package's own suite, which certification runs against candidate spec releases
16
16
  * whose version is allowed to be newer).
17
17
  */
18
- export const SPEC_VERSION = "0.14.0";
18
+ export const SPEC_VERSION = "0.16.0";
19
19
 
20
20
  export const BOUNDARY_SPEC = {
21
21
  /** App module files the boundary + frontend security defaults apply to. */
@@ -45,7 +45,7 @@ export const BOUNDARY_SPEC = {
45
45
  */
46
46
  restrictInAppAnchors: true,
47
47
  /** Package internals an app module must not deep-import (import from the package root). */
48
- internalImportPatterns: ["@terp/*/src/*", "@terp/*/dist/*"],
48
+ internalImportPatterns: ["@terpjs/*/src/*", "@terpjs/*/dist/*"],
49
49
  /** Module-authored stylesheets are refused — theming flows from the app's token source. */
50
50
  styleImportPatterns: [
51
51
  "*.css",
@@ -22,9 +22,9 @@ import { describe, expect, it } from "vitest";
22
22
 
23
23
  import terpBoundaries, { BOUNDARY_SPEC, catalogRuleId } from "./index.js";
24
24
 
25
- // The spec is a declared dependency (@terp/spec, ADR 0082), never a repo-relative path.
25
+ // The spec is a declared dependency (@terpjs/spec, ADR 0082), never a repo-relative path.
26
26
  const SPEC_ROOT = path.dirname(
27
- createRequire(import.meta.url).resolve("@terp/spec/package.json"),
27
+ createRequire(import.meta.url).resolve("@terpjs/spec/package.json"),
28
28
  );
29
29
  const SURFACE = JSON.parse(
30
30
  fs.readFileSync(path.join(SPEC_ROOT, "restricted-surface.json"), "utf8"),
@@ -70,7 +70,7 @@ describe("structural parity: BOUNDARY_SPEC realises exactly the declared surface
70
70
 
71
71
  it("every declared deep-import segment is refused by the import patterns", () => {
72
72
  for (const segment of SURFACE.deepImportPathSegments) {
73
- expect(BOUNDARY_SPEC.internalImportPatterns).toContain(`@terp/*/${segment}/*`);
73
+ expect(BOUNDARY_SPEC.internalImportPatterns).toContain(`@terpjs/*/${segment}/*`);
74
74
  }
75
75
  expect(BOUNDARY_SPEC.internalImportPatterns).toHaveLength(
76
76
  SURFACE.deepImportPathSegments.length,
@@ -123,7 +123,7 @@ describe("behavioural parity: each declared primitive is refused with the right
123
123
  for (const segment of SURFACE.deepImportPathSegments) {
124
124
  it(`deep import via /${segment}/ -> frontend/no-deep-imports`, async () => {
125
125
  expect(
126
- await lintModuleSource(`import { x } from "@terp/react-core/${segment}/internal";\n`),
126
+ await lintModuleSource(`import { x } from "@terpjs/react-core/${segment}/internal";\n`),
127
127
  ).toContain("frontend/no-deep-imports");
128
128
  });
129
129
  }
@@ -148,7 +148,7 @@ const VIOLATION_SNIPPETS = {
148
148
  "frontend/no-inline-styling": 'export const W = () => <div className="x" />;',
149
149
  "frontend/router-links": 'export const W = () => <a href="/notes">go</a>;',
150
150
  "frontend/generated-client-only": 'export const ping = () => fetch("/healthz");',
151
- "frontend/no-deep-imports": 'import { x } from "@terp/react-core/src/internal";',
151
+ "frontend/no-deep-imports": 'import { x } from "@terpjs/react-core/src/internal";',
152
152
  "frontend/no-style-imports": 'import "./widget.css";',
153
153
  "frontend/no-cross-module-imports": 'import { x } from "../other/thing";',
154
154
  "frontend/no-dom-html-injection": "export const W = (el, html) => { el.innerHTML = html; };",