@terpjs/spec 0.15.0 → 0.17.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.
Files changed (40) hide show
  1. package/README.md +6 -6
  2. package/VERSION +1 -1
  3. package/catalog/backend/list_routes_paginate.json +2 -2
  4. package/catalog/backend/migration_history_is_intact.json +22 -0
  5. package/catalog/backend/no_hardcoded_credentials.json +1 -1
  6. package/catalog/backend/safe_methods_are_read_only.json +1 -1
  7. package/catalog/backend/schemas_exclude_sensitive_fields.json +1 -1
  8. package/catalog/frontend/escape-hatch.json +1 -1
  9. package/catalog/frontend/generated-client-only.json +2 -2
  10. package/catalog/frontend/layout-contract.json +3 -3
  11. package/catalog/frontend/no-cross-module-imports.json +1 -1
  12. package/catalog/frontend/no-deep-imports.json +2 -2
  13. package/catalog/frontend/no-dom-html-injection.json +2 -2
  14. package/catalog/frontend/no-eval.json +1 -1
  15. package/catalog/frontend/no-inline-styling.json +2 -2
  16. package/catalog/frontend/no-style-imports.json +1 -1
  17. package/catalog/frontend/no-unsafe-href.json +1 -1
  18. package/catalog/frontend/no-unsafe-target-blank.json +1 -1
  19. package/catalog/frontend/router-links.json +2 -2
  20. package/catalog/frontend/token-styled-elements.json +2 -2
  21. package/corpus/backend/migration_history_is_intact/compliant-01/modules/notes/migrations/versions/0001_base.py +10 -0
  22. package/corpus/backend/migration_history_is_intact/compliant-01/modules/notes/migrations/versions/0002_change.py +10 -0
  23. package/corpus/backend/migration_history_is_intact/violation-01/modules/notes/migrations/versions/0001_base.py +10 -0
  24. package/corpus/backend/migration_history_is_intact/violation-01/modules/notes/migrations/versions/0002_change.py +10 -0
  25. package/corpus/backend/no_hardcoded_credentials/compliant-03/modules/billing/schemas.py +20 -0
  26. package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  27. package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +1 -1
  28. package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +1 -1
  29. package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  30. package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +1 -1
  31. package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  32. package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  33. package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +1 -1
  34. package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  35. package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  36. package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  37. package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  38. package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  39. package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +1 -1
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -138,7 +138,7 @@ The standalone suite enforces the split
138
138
  | `title` | One-line statement of the invariant. |
139
139
  | `intent` | Why the rule exists — the drift or threat it prevents. |
140
140
  | `layer` | Cheapest faithful verification for a *new* stack: see below. |
141
- | `enforcement` | How the reference implementation enforces it (first entry: the `build-time` check). A `runtime` entry names the fail-closed runtime control pairing with it (the two-layer discipline a Level 3 stack must reproduce for the rules that require it); a `black-box` entry names the `@terp/conformance` probe title. For frontend rules, `reported_as` is the ESLint rule id violations surface as — several catalog rules share one core rule id, so the adapter publishes a `catalogRuleId()` mapping and findings are attributed through it. |
141
+ | `enforcement` | How the reference implementation enforces it (first entry: the `build-time` check). A `runtime` entry names the fail-closed runtime control pairing with it (the two-layer discipline a Level 3 stack must reproduce for the rules that require it); a `black-box` entry names the `@terpjs/conformance` probe title. For frontend rules, `reported_as` is the ESLint rule id violations surface as — several catalog rules share one core rule id, so the adapter publishes a `catalogRuleId()` mapping and findings are attributed through it. |
142
142
  | `runtime` | **Mandatory.** The rule's runtime-applicability classification (`required` / `not-applicable` / `deferred`) plus a `rationale` (mandatory for exemptions) and, for `deferred`, a `tracking` reference naming where the deferral is tracked — see “Runtime applicability” below. |
143
143
  | `restricted_surface` | Frontend prohibition rules only: the `restricted-surface.json` keys the rule realises — the structural citation the spec suite resolves (every key must be claimed by some rule; a prose mention in `intent` must agree with the field). |
144
144
  | `opt_out` | The *reference realisation* of the abstract escape-hatch contract (see below). |
@@ -159,7 +159,7 @@ The standalone suite enforces the split
159
159
 
160
160
  The classification is a judgment about *porting cost*, not a limit on the
161
161
  reference implementation — today every rule is enforced by `terp.arch` or
162
- `@terp/eslint-boundaries` regardless of layer.
162
+ `@terpjs/eslint-boundaries` regardless of layer.
163
163
 
164
164
  ### Runtime applicability (the two-layer discipline, per rule)
165
165
 
@@ -222,7 +222,7 @@ consumers acting on findings without re-reading the catalog), and a
222
222
  can be tracked across line-shifting edits). Attribution is always to the
223
223
  stack-neutral catalog id — the reference ESLint adapter, whose core rule ids
224
224
  are shared between several catalog rules, publishes this mapping as
225
- `catalogRuleId()` in `@terp/eslint-boundaries`.
225
+ `catalogRuleId()` in `@terpjs/eslint-boundaries`.
226
226
 
227
227
  ## Check-report format
228
228
 
@@ -380,7 +380,7 @@ rule can ship with its gap explicit and reviewed.
380
380
  ## Conformance levels
381
381
 
382
382
  - **Level 1 — black-box:** the app passes the runnable conformance suite
383
- (`@terp/conformance`) for the capabilities it claims, including the
383
+ (`@terpjs/conformance`) for the capabilities it claims, including the
384
384
  `standard:` probes the `black-box` catalog entries name.
385
385
  - **Level 2 — static rule pack:** additionally, a checker validated against
386
386
  this corpus enforces the `static-portable` rules for the app's language(s),
@@ -388,8 +388,8 @@ rule can ship with its gap explicit and reviewed.
388
388
  - **Level 3 — full harness:** additionally, the `static-bespoke` rules, the
389
389
  paired `runtime` controls of every rule whose `runtime.applicability` is
390
390
  `required`, and the governed escape-hatch budget ratchet are enforced (today:
391
- the `terp.arch` + `@terp/eslint-boundaries` reference harness on top of
392
- `terp.core` / `@terp/react-core`).
391
+ the `terp.arch` + `@terpjs/eslint-boundaries` reference harness on top of
392
+ `terp.core` / `@terpjs/react-core`).
393
393
 
394
394
  ## Growing the spec
395
395
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.15.0
1
+ 0.17.0
@@ -12,7 +12,7 @@
12
12
  },
13
13
  {
14
14
  "kind": "black-box",
15
- "tool": "@terp/conformance",
15
+ "tool": "@terpjs/conformance",
16
16
  "ref": "standard: list routes return a capped Page envelope"
17
17
  },
18
18
  {
@@ -25,7 +25,7 @@
25
25
  "opt_out": "# arch-allow-list-routes-paginate: <reason>",
26
26
  "runtime": {
27
27
  "applicability": "required",
28
- "rationale": "At boot, the route scan on the composition seam (the same seam as _validate_router_response_models) walks every composed route \u2014 decorator, imperative add_api_route, and nested included routers alike \u2014 and raises a BootError naming this rule and the route when a route declares a bare list[...] / Sequence[...] response_model instead of Page[T]. PaginationDep's fail-closed skip/limit caps and the @terp/conformance Page-envelope probe remain in place on top of this in-app refusal."
28
+ "rationale": "At boot, the route scan on the composition seam (the same seam as _validate_router_response_models) walks every composed route \u2014 decorator, imperative add_api_route, and nested included routers alike \u2014 and raises a BootError naming this rule and the route when a route declares a bare list[...] / Sequence[...] response_model instead of Page[T]. PaginationDep's fail-closed skip/limit caps and the @terpjs/conformance Page-envelope probe remain in place on top of this in-app refusal."
29
29
  },
30
30
  "guide_topic": "service",
31
31
  "corpus": true
@@ -0,0 +1,22 @@
1
+ {
2
+ "id": "backend/migration_history_is_intact",
3
+ "surface": "backend",
4
+ "title": "Each migration history is one unbroken chain from a single first revision",
5
+ "intent": "A revision whose parent is missing, or a second revision claiming to start the history, means an already-authored migration was deleted or replaced rather than built upon. Every database that applied the removed revision then becomes unupgradable, and no schema-drift check can see it: a database rebuilt from the rewritten history is perfectly consistent with the models, so the build stays green while every provisioned environment is stranded. New schema changes are added on top of the existing chain.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "terp.arch",
11
+ "ref": "check_migration_history_is_intact"
12
+ }
13
+ ],
14
+ "reference": "Within each migrations/versions directory, every revision's declared parent (one id, or each id of a merge revision's tuple) must name a revision defined in that same directory, and at most one revision may declare no parent.",
15
+ "opt_out": "# arch-allow-migration-history-is-intact: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "Chain integrity is a property of the authored revision files and is checked from source alone. The complementary runtime control (terp.migrations.assert_no_orphaned_revisions, which refuses a database whose applied revisions the code no longer defines) enforces a different invariant on a different input: it needs a live database and cannot be observed from the source tree."
19
+ },
20
+ "guide_topic": "migrations",
21
+ "corpus": true
22
+ }
@@ -2,7 +2,7 @@
2
2
  "id": "backend/no_hardcoded_credentials",
3
3
  "surface": "backend",
4
4
  "title": "App modules do not hard-code credentials or recognizable secret tokens",
5
- "intent": "A credential-shaped assignment to a non-empty string literal is almost always a secret that should come from sealed config / environment wiring, not source. The rule also rejects common high-confidence secret literal formats anywhere in a module so leaked keys are caught even when assigned to a bland variable name. As a security rule this also scans test and migration files inside a module — a real secret is a leak wherever it is committed.",
5
+ "intent": "A credential-shaped assignment to a non-empty string literal is almost always a secret that should come from sealed config / environment wiring, not source. The rule also rejects common high-confidence secret literal formats anywhere in a module so leaked keys are caught even when assigned to a bland variable name. As a security rule this also scans test and migration files inside a module — a real secret is a leak wherever it is committed. One shape is exempt: an enum member whose literal is its own name (SECRET_REFERENCE = \"secret_reference\") is vocabulary, not secret material.",
6
6
  "layer": "static-portable",
7
7
  "enforcement": [
8
8
  {
@@ -17,7 +17,7 @@
17
17
  },
18
18
  {
19
19
  "kind": "black-box",
20
- "tool": "@terp/conformance",
20
+ "tool": "@terpjs/conformance",
21
21
  "ref": "standard: safe methods observably mutate nothing"
22
22
  }
23
23
  ],
@@ -12,7 +12,7 @@
12
12
  },
13
13
  {
14
14
  "kind": "black-box",
15
- "tool": "@terp/conformance",
15
+ "tool": "@terpjs/conformance",
16
16
  "ref": "standard: responses never expose credential-shaped fields"
17
17
  },
18
18
  {
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/escape-hatch",
12
12
  "reported_as": "terp/escape-hatch"
13
13
  }
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.restrictedGlobals",
12
12
  "reported_as": "no-restricted-globals"
13
13
  }
14
14
  ],
15
- "reference": "useTerpClient() + unwrap from @terp/react-core for request/response; useRealtimeChannel() from @terp/react-core for typed SSE/WebSocket subscriptions (one-use ticket minted through the generated client).",
15
+ "reference": "useTerpClient() + unwrap from @terpjs/react-core for request/response; useRealtimeChannel() from @terpjs/react-core for typed SSE/WebSocket subscriptions (one-use ticket minted through the generated client).",
16
16
  "opt_out": "// terp-allow-generated-client-only: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -7,18 +7,18 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/layout-contract",
12
12
  "reported_as": "terp/layout-contract"
13
13
  },
14
14
  {
15
15
  "kind": "runtime",
16
- "tool": "@terp/react-core",
16
+ "tool": "@terpjs/react-core",
17
17
  "ref": "verifySlotChildren"
18
18
  }
19
19
  ],
20
20
  "opt_out": "// terp-allow-layout-contract: <reason>",
21
- "reference": "frontend/layout-contract.json + the layoutContract bootstrap option (ADR 0079); verifySlotChildren in @terp/react-core is the runtime DOM check.",
21
+ "reference": "frontend/layout-contract.json + the layoutContract bootstrap option (ADR 0079); verifySlotChildren in @terpjs/react-core is the runtime DOM check.",
22
22
  "runtime": {
23
23
  "applicability": "required"
24
24
  },
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/no-cross-module-imports",
12
12
  "reported_as": "terp/no-cross-module-imports"
13
13
  }
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.internalImportPatterns",
12
12
  "reported_as": "no-restricted-imports"
13
13
  }
14
14
  ],
15
- "reference": "@terp/*/src/* and @terp/*/dist/* imports are refused; import from the @terp/* package root.",
15
+ "reference": "@terpjs/*/src/* and @terpjs/*/dist/* imports are refused; import from the @terpjs/* package root.",
16
16
  "opt_out": "// terp-allow-no-deep-imports: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/no-dom-html-injection",
12
12
  "reported_as": "terp/no-dom-html-injection"
13
13
  }
14
14
  ],
15
- "reference": "The Markdown component from @terp/react-core for rich text.",
15
+ "reference": "The Markdown component from @terpjs/react-core for rich text.",
16
16
  "opt_out": "// terp-allow-no-dom-html-injection: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/no-eval",
12
12
  "reported_as": "terp/no-eval"
13
13
  }
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.restrictedAttributes",
12
12
  "reported_as": "no-restricted-syntax"
13
13
  }
14
14
  ],
15
- "reference": "Layout via Stack, DetailList and the page archetypes from @terp/react-core; theming via the app's token source.",
15
+ "reference": "Layout via Stack, DetailList and the page archetypes from @terpjs/react-core; theming via the app's token source.",
16
16
  "opt_out": "// terp-allow-no-inline-styling: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.styleImportPatterns",
12
12
  "reported_as": "no-restricted-imports"
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/no-unsafe-href",
12
12
  "reported_as": "terp/no-unsafe-href"
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "terp/no-unsafe-target-blank",
12
12
  "reported_as": "terp/no-unsafe-target-blank"
13
13
  }
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.restrictInAppAnchors",
12
12
  "reported_as": "no-restricted-syntax"
13
13
  }
14
14
  ],
15
- "reference": "Link from @terp/react-core (router-integrated, role-aware).",
15
+ "reference": "Link from @terpjs/react-core (router-integrated, role-aware).",
16
16
  "opt_out": "// terp-allow-router-links: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -7,12 +7,12 @@
7
7
  "enforcement": [
8
8
  {
9
9
  "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
10
+ "tool": "@terpjs/eslint-boundaries",
11
11
  "ref": "BOUNDARY_SPEC.restrictedElements",
12
12
  "reported_as": "no-restricted-syntax"
13
13
  }
14
14
  ],
15
- "reference": "Button, Input, Select, Textarea, DataView, ConfirmDialog and Stack as=\"form\" from @terp/react-core (BOUNDARY_SPEC.restrictedElements maps each element to its replacement).",
15
+ "reference": "Button, Input, Select, Textarea, DataView, ConfirmDialog and Stack as=\"form\" from @terpjs/react-core (BOUNDARY_SPEC.restrictedElements maps each element to its replacement).",
16
16
  "opt_out": "// terp-allow-token-styled-elements: <reason>",
17
17
  "runtime": {
18
18
  "applicability": "not-applicable",
@@ -0,0 +1,10 @@
1
+ revision = "aaa111"
2
+ down_revision = None
3
+
4
+
5
+ def upgrade():
6
+ op.create_table("notes")
7
+
8
+
9
+ def downgrade():
10
+ op.drop_table("notes")
@@ -0,0 +1,10 @@
1
+ revision = "bbb222"
2
+ down_revision = "aaa111"
3
+
4
+
5
+ def upgrade():
6
+ op.add_column("notes", column)
7
+
8
+
9
+ def downgrade():
10
+ op.drop_column("notes", "title")
@@ -0,0 +1,10 @@
1
+ revision = "aaa111"
2
+ down_revision = None
3
+
4
+
5
+ def upgrade():
6
+ op.create_table("notes")
7
+
8
+
9
+ def downgrade():
10
+ op.drop_table("notes")
@@ -0,0 +1,10 @@
1
+ revision = "bbb222"
2
+ down_revision = "deleted_parent"
3
+
4
+
5
+ def upgrade():
6
+ op.add_column("notes", column)
7
+
8
+
9
+ def downgrade():
10
+ op.drop_column("notes", "title")
@@ -0,0 +1,20 @@
1
+ """Compliant: enum vocabulary whose literal is the member's own name.
2
+
3
+ A member like ``SECRET_REFERENCE = "secret_reference"`` names a parameter kind —
4
+ the string carries no secret material, it *is* the identifier's wire spelling.
5
+ Flagging it would push authors to spell the same vocabulary as ``auto()`` purely
6
+ to dodge the rule, hiding the wire value from readers of the schema.
7
+ """
8
+
9
+ from enum import StrEnum
10
+
11
+
12
+ class ParameterType(StrEnum):
13
+ TEXT = "text"
14
+ NUMBER = "number"
15
+ SECRET_REFERENCE = "secret_reference"
16
+
17
+
18
+ class TokenKind(StrEnum):
19
+ ACCESS_TOKEN = "access_token"
20
+ REFRESH_TOKEN = "refresh_token"
@@ -1,4 +1,4 @@
1
- import { useTerpClient } from "@terp/react-core";
1
+ import { useTerpClient } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  const client = useTerpClient();
4
4
  void client;
@@ -1,4 +1,4 @@
1
- import { useTerpClient } from "@terp/react-core";
1
+ import { useTerpClient } from "@terpjs/react-core";
2
2
 
3
3
  const repository = {
4
4
  fetch(id: string): string {
@@ -1,4 +1,4 @@
1
- import { useRealtimeChannel } from "@terp/react-core";
1
+ import { useRealtimeChannel } from "@terpjs/react-core";
2
2
 
3
3
  type Notice = { sequence: number; text: string };
4
4
 
@@ -1,4 +1,4 @@
1
- import { HubCard, HubPage } from "@terp/react-core";
1
+ import { HubCard, HubPage } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return (
4
4
  <HubPage title="Notes">
@@ -1,4 +1,4 @@
1
- import { DetailList, HubPage } from "@terp/react-core";
1
+ import { DetailList, HubPage } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return (
4
4
  <HubPage title="Notes">
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,2 +1,2 @@
1
- import Button from "@terp/react-core/src/Button";
1
+ import Button from "@terpjs/react-core/src/Button";
2
2
  export const Widget = () => <Button>x</Button>;
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Link } from "@terp/react-core";
1
+ import { Link } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Link to="/notes">notes</Link>;
4
4
  }
@@ -1,4 +1,4 @@
1
- import { Button } from "@terp/react-core";
1
+ import { Button } from "@terpjs/react-core";
2
2
  export function Widget() {
3
3
  return <Button>ok</Button>;
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terpjs/spec",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "The Terp Standard — stack-neutral rule catalog, violation corpus, finding format, and refused-surface declaration (ADRs 0080/0081; packaged per ADR 0082, published per ADR 0086). Data only: consumers resolve the spec root via require.resolve('@terpjs/spec/package.json').",
5
5
  "files": [
6
6
  "VERSION",