@usefragments/core 1.11.0 → 2.0.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 (39) hide show
  1. package/dist/{chunk-RPSEABY3.js → chunk-BMPYIUZE.js} +23 -33
  2. package/dist/chunk-BMPYIUZE.js.map +1 -0
  3. package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
  4. package/dist/chunk-MZ2FS7U4.js.map +1 -0
  5. package/dist/codes/index.d.ts +1 -1
  6. package/dist/codes/index.js +1 -1
  7. package/dist/compiled-types/index.d.ts +1 -1
  8. package/dist/generate/index.d.ts +1 -1
  9. package/dist/{governance-BAsy1k2H.d.ts → governance-hOPXGbbs.d.ts} +3 -3
  10. package/dist/index.d.ts +133 -6026
  11. package/dist/index.js +179 -806
  12. package/dist/index.js.map +1 -1
  13. package/dist/preview-runtime.d.ts +1 -1
  14. package/dist/react-types.d.ts +1 -1
  15. package/dist/schemas/index.js +1 -1
  16. package/dist/storyAdapter.d.ts +1 -1
  17. package/dist/test-utils.d.ts +1 -1
  18. package/dist/topology/index.d.ts +1 -1
  19. package/dist/topology/index.js +1 -1
  20. package/package.json +1 -1
  21. package/src/approved-contract-tokens.test.ts +39 -0
  22. package/src/approved-contract-tokens.ts +18 -0
  23. package/src/codes/__tests__/codes.test.ts +4 -0
  24. package/src/codes/codes.ts +10 -0
  25. package/src/domain-ids.test.ts +9 -26
  26. package/src/domain-ids.ts +0 -45
  27. package/src/evaluation/evaluate.ts +17 -7
  28. package/src/evaluation/evaluation-v2-receipt-v1.test.ts +20 -8
  29. package/src/evaluation/index.ts +1 -1
  30. package/src/governance.ts +18 -0
  31. package/src/index.ts +10 -79
  32. package/src/topology/resolve-area.ts +1 -1
  33. package/src/types.ts +0 -3
  34. package/dist/chunk-3LLRNCPX.js.map +0 -1
  35. package/dist/chunk-RPSEABY3.js.map +0 -1
  36. package/src/feature-plan/digest.ts +0 -217
  37. package/src/feature-plan/feature-plan-v1.test.ts +0 -529
  38. package/src/feature-plan/index.ts +0 -65
  39. package/src/feature-plan/types.ts +0 -628
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { V as VariantRenderOptions, b as VariantLoader } from './governance-BAsy1k2H.js';
3
+ import { V as VariantRenderOptions, b as VariantLoader } from './governance-hOPXGbbs.js';
4
4
  import 'zod';
5
5
  import './topology/index.js';
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode, JSX } from 'react';
2
- export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-BAsy1k2H.js';
2
+ export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-hOPXGbbs.js';
3
3
  import 'zod';
4
4
  import './topology/index.js';
5
5
 
@@ -24,7 +24,7 @@ import {
24
24
  suppressionDirectiveSchema,
25
25
  validatorResultSchema,
26
26
  violationSchema
27
- } from "../chunk-RPSEABY3.js";
27
+ } from "../chunk-BMPYIUZE.js";
28
28
  import "../chunk-EIYNNS77.js";
29
29
  import "../chunk-PWIJMOI4.js";
30
30
  import "../chunk-JNBFJ34I.js";
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, ComponentType } from 'react';
2
2
  import { isExportStory as isExportStory$1, storyNameFromExport as storyNameFromExport$1, toId as toId$1 } from '@storybook/csf';
3
- import { F as FragmentDefinition } from './governance-BAsy1k2H.js';
3
+ import { F as FragmentDefinition } from './governance-hOPXGbbs.js';
4
4
  import 'zod';
5
5
  import './topology/index.js';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { CompiledBlock, CompiledFragment } from './compiled-types/index.js';
2
2
  import './types-xJ2xyp_G.js';
3
- import './governance-BAsy1k2H.js';
3
+ import './governance-hOPXGbbs.js';
4
4
  import 'zod';
5
5
  import 'react';
6
6
  import './topology/index.js';
@@ -6,7 +6,7 @@
6
6
  * preview, cross-repo reconciliation) import it; none reimplements glob/area
7
7
  * logic. Pure and browser-safe — no Node, no parser deps.
8
8
  *
9
- * See `apps/cloud/docs/topology/01-architecture.md §1`.
9
+ * See `docs/fragments-v1/ARCHITECTURE.md §1`.
10
10
  */
11
11
  type AreaCriticality = "low" | "medium" | "high" | "revenue" | "regulated";
12
12
  interface Area {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveArea
3
- } from "../chunk-3LLRNCPX.js";
3
+ } from "../chunk-MZ2FS7U4.js";
4
4
  export {
5
5
  resolveArea
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefragments/core",
3
- "version": "1.11.0",
3
+ "version": "2.0.0",
4
4
  "license": "MIT",
5
5
  "description": "Core types, schemas, and runtime API for Fragments component definitions",
6
6
  "author": "Conan McNicholl",
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ APPROVED_CONTRACT_TOKEN_MAX_NAMES,
4
+ APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH,
5
+ isApprovedContractTokenNames,
6
+ } from "./approved-contract-tokens.js";
7
+ import { parseComponentGovernancePolicyJson } from "./governance.js";
8
+
9
+ describe("approved token transport", () => {
10
+ it("distinguishes absent authority from an explicitly empty vocabulary", () => {
11
+ expect(isApprovedContractTokenNames(undefined)).toBe(false);
12
+ expect(isApprovedContractTokenNames(null)).toBe(false);
13
+ expect(isApprovedContractTokenNames([])).toBe(true);
14
+ expect(isApprovedContractTokenNames(["--approved"])).toBe(true);
15
+ });
16
+ it("enforces the same bounded vocabulary on every consumer", () => {
17
+ expect(isApprovedContractTokenNames(Array(APPROVED_CONTRACT_TOKEN_MAX_NAMES).fill("--x"))).toBe(
18
+ true
19
+ );
20
+ expect(
21
+ isApprovedContractTokenNames(Array(APPROVED_CONTRACT_TOKEN_MAX_NAMES + 1).fill("--x"))
22
+ ).toBe(false);
23
+ expect(
24
+ isApprovedContractTokenNames(["--" + "x".repeat(APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH - 2)])
25
+ ).toBe(true);
26
+ expect(
27
+ isApprovedContractTokenNames(["--" + "x".repeat(APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH - 1)])
28
+ ).toBe(false);
29
+ expect(isApprovedContractTokenNames(["--"])).toBe(false);
30
+ expect(isApprovedContractTokenNames(["local-token"])).toBe(false);
31
+ });
32
+ });
33
+
34
+ it("normalizes supported policy envelopes without treating malformed authority as empty rules", () => {
35
+ for (const payload of ["[]", '{"governance":[]}', '{"rules":[]}'])
36
+ expect(parseComponentGovernancePolicyJson(payload)).toEqual([]);
37
+ for (const payload of ["null", "{}", "[{}]", "invalid", '{"governance":false}'])
38
+ expect(parseComponentGovernancePolicyJson(payload)).toBeNull();
39
+ });
@@ -0,0 +1,18 @@
1
+ /** Portable bounds shared by contract authoring and first-party policy clients. */
2
+ export const APPROVED_CONTRACT_TOKEN_MAX_NAMES = 10_000;
3
+ export const APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH = 512;
4
+
5
+ /** Empty is authored absence; missing is unavailable authority. */
6
+ export function isApprovedContractTokenNames(value: unknown): value is string[] {
7
+ return (
8
+ Array.isArray(value) &&
9
+ value.length <= APPROVED_CONTRACT_TOKEN_MAX_NAMES &&
10
+ value.every(
11
+ (name) =>
12
+ typeof name === "string" &&
13
+ name.startsWith("--") &&
14
+ name.length > 2 &&
15
+ name.length <= APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH
16
+ )
17
+ );
18
+ }
@@ -29,6 +29,10 @@ const SUBSYSTEM_EMITTED_CODES = [
29
29
  // extracted component, is refused before any rule has a contract to run on.
30
30
  "FUI9010",
31
31
  "FUI9011",
32
+ // Emitted by Cloud's machine contract write path (CLI route + MCP tool) when
33
+ // the workspace plan does not include contract writes from machines; the CLI
34
+ // relays it. Not a rule.
35
+ "FUI9012",
32
36
  // Emitted by tokens/css-vars-must-be-defined when the rule is active but the
33
37
  // resolved contract vocabulary is empty — a config-level inert diagnostic,
34
38
  // not a second rule id in RULES.
@@ -557,6 +557,16 @@ export const CODES = [
557
557
  fixAvailable: false,
558
558
  evidenceRequired: false,
559
559
  }),
560
+ code({
561
+ code: "FUI9012",
562
+ ruleId: "contract/write-not-entitled",
563
+ category: "system",
564
+ defaultSeverity: "critical",
565
+ title: "Contract write requires a paid workspace",
566
+ lifecycle: "experimental",
567
+ fixAvailable: false,
568
+ evidenceRequired: false,
569
+ }),
560
570
  ] as const satisfies readonly FuiCode[];
561
571
 
562
572
  export const byCode: ReadonlyMap<string, FuiCode> = new Map(
@@ -3,12 +3,8 @@ import {
3
3
  analysisPlanIdFromDigest,
4
4
  evaluationReceiptIdFromDigest,
5
5
  mintEvaluationReceiptId,
6
- featurePlanIdFromEntropy,
7
- featureRevisionIdFromDigest,
8
6
  parseAnalysisPlanId,
9
7
  parseEvaluationReceiptId,
10
- parseFeaturePlanId,
11
- parseFeatureRevisionId,
12
8
  } from "./domain-ids.js";
13
9
 
14
10
  const DIGEST = "0123456789abcdef".repeat(4);
@@ -18,35 +14,22 @@ describe("V1 domain IDs", () => {
18
14
  expect(analysisPlanIdFromDigest(DIGEST)).toBe(`apl_${DIGEST}`);
19
15
  expect(evaluationReceiptIdFromDigest(DIGEST)).toBe(`evr_${DIGEST}`);
20
16
  expect(mintEvaluationReceiptId(DIGEST)).toBe(`evr_${DIGEST}`);
21
- expect(featureRevisionIdFromDigest(DIGEST)).toBe(`fpr_${DIGEST}`);
22
- });
23
-
24
- it("keeps feature workflow identity opaque and exactly 128-bit", () => {
25
- const entropy = "00112233445566778899aabbccddeeff";
26
- expect(featurePlanIdFromEntropy(entropy)).toBe(`fpl_${entropy}`);
27
17
  });
28
18
 
29
19
  it.each([
30
20
  [parseAnalysisPlanId, `apl_${DIGEST}`],
31
21
  [parseEvaluationReceiptId, `evr_${DIGEST}`],
32
- [parseFeatureRevisionId, `fpr_${DIGEST}`],
33
- [parseFeaturePlanId, "fpl_00112233445566778899aabbccddeeff"],
34
22
  ] as const)("round-trips a valid ID", (parse, value) => {
35
23
  expect(parse(value)).toBe(value);
36
24
  });
37
25
 
38
- it.each([
39
- `apl_${DIGEST.slice(0, -1)}`,
40
- `apl_${DIGEST.toUpperCase()}`,
41
- `evr_${DIGEST}extra`,
42
- `fpr_${DIGEST.slice(0, 32)}`,
43
- "fpl_00112233445566778899AABBCCDDEEFF",
44
- ])("rejects truncated, uppercase, or extended IDs: %s", (value) => {
45
- expect(() => {
46
- if (value.startsWith("apl_")) parseAnalysisPlanId(value);
47
- else if (value.startsWith("evr_")) parseEvaluationReceiptId(value);
48
- else if (value.startsWith("fpr_")) parseFeatureRevisionId(value);
49
- else parseFeaturePlanId(value);
50
- }).toThrow();
51
- });
26
+ it.each([`apl_${DIGEST.slice(0, -1)}`, `apl_${DIGEST.toUpperCase()}`, `evr_${DIGEST}extra`])(
27
+ "rejects truncated, uppercase, or extended IDs: %s",
28
+ (value) => {
29
+ expect(() => {
30
+ if (value.startsWith("apl_")) parseAnalysisPlanId(value);
31
+ else if (value.startsWith("evr_")) parseEvaluationReceiptId(value);
32
+ }).toThrow();
33
+ }
34
+ );
52
35
  });
package/src/domain-ids.ts CHANGED
@@ -1,23 +1,15 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  const FULL_DIGEST_PATTERN = /^[0-9a-f]{64}$/u;
4
- const WORKFLOW_ENTROPY_PATTERN = /^[0-9a-f]{32}$/u;
5
4
 
6
5
  export declare const analysisPlanIdBrand: unique symbol;
7
6
  export declare const evaluationReceiptIdBrand: unique symbol;
8
- export declare const featurePlanIdBrand: unique symbol;
9
- export declare const featureRevisionIdBrand: unique symbol;
10
7
 
11
8
  export type DigestHex = string;
12
9
  export type AnalysisPlanId = `apl_${string}` & { readonly [analysisPlanIdBrand]: void };
13
10
  export type EvaluationReceiptId = `evr_${string}` & {
14
11
  readonly [evaluationReceiptIdBrand]: void;
15
12
  };
16
- export type FeaturePlanId = `fpl_${string}` & { readonly [featurePlanIdBrand]: void };
17
- export type FeatureRevisionId = `fpr_${string}` & {
18
- readonly [featureRevisionIdBrand]: void;
19
- };
20
-
21
13
  export const digestHexStringSchema = z
22
14
  .string()
23
15
  .regex(FULL_DIGEST_PATTERN, "Expected 64 lowercase hexadecimal characters");
@@ -38,20 +30,6 @@ export const evaluationReceiptIdStringSchema = z
38
30
  .string()
39
31
  .regex(/^evr_[0-9a-f]{64}$/u, "Invalid evaluation receipt ID");
40
32
 
41
- export const featurePlanIdStringSchema = z
42
- .string()
43
- .regex(/^fpl_[0-9a-f]{32}$/u, "Invalid feature plan ID");
44
- export const featurePlanIdSchema = featurePlanIdStringSchema.transform(
45
- (value) => value as FeaturePlanId
46
- );
47
-
48
- export const featureRevisionIdStringSchema = z
49
- .string()
50
- .regex(/^fpr_[0-9a-f]{64}$/u, "Invalid feature revision ID");
51
- export const featureRevisionIdSchema = featureRevisionIdStringSchema.transform(
52
- (value) => value as FeatureRevisionId
53
- );
54
-
55
33
  export function parseDigestHex(value: string): DigestHex {
56
34
  return digestHexSchema.parse(value);
57
35
  }
@@ -74,21 +52,6 @@ export function mintEvaluationReceiptId(resultDigest: string): EvaluationReceipt
74
52
  return evaluationReceiptIdFromDigest(resultDigest);
75
53
  }
76
54
 
77
- export function featureRevisionIdFromDigest(digest: string): FeatureRevisionId {
78
- return featureRevisionIdSchema.parse(`fpr_${parseDigestHex(digest)}`);
79
- }
80
-
81
- /**
82
- * Construct a stable feature-workflow ID from 128 bits supplied by the caller.
83
- * Core deliberately owns neither a clock nor a random-number generator.
84
- */
85
- export function featurePlanIdFromEntropy(entropyHex: string): FeaturePlanId {
86
- if (!WORKFLOW_ENTROPY_PATTERN.test(entropyHex)) {
87
- throw new TypeError("Feature plan entropy must be exactly 128-bit lowercase hexadecimal");
88
- }
89
- return featurePlanIdSchema.parse(`fpl_${entropyHex}`);
90
- }
91
-
92
55
  export function parseAnalysisPlanId(value: string): AnalysisPlanId {
93
56
  return analysisPlanIdSchema.parse(value);
94
57
  }
@@ -96,11 +59,3 @@ export function parseAnalysisPlanId(value: string): AnalysisPlanId {
96
59
  export function parseEvaluationReceiptId(value: string): EvaluationReceiptId {
97
60
  return evaluationReceiptIdSchema.parse(value);
98
61
  }
99
-
100
- export function parseFeaturePlanId(value: string): FeaturePlanId {
101
- return featurePlanIdSchema.parse(value);
102
- }
103
-
104
- export function parseFeatureRevisionId(value: string): FeatureRevisionId {
105
- return featureRevisionIdSchema.parse(value);
106
- }
@@ -303,7 +303,7 @@ function evaluateV2(rawInput: EvaluationInputV2): EvaluationResultV2 {
303
303
  : coverageSummary.requiredByState.analyzed === coverageSummary.requiredRegions
304
304
  ? "complete"
305
305
  : "incomplete";
306
- const integrity = {
306
+ const integrity: EvaluationResultV2["integrity"] = {
307
307
  fcidVerified: compatibilityResult.integrity.fcidVerified === true,
308
308
  analysisPlanVerified: true,
309
309
  factIntegrity: input.integrity.facts.state,
@@ -324,9 +324,7 @@ function evaluateV2(rawInput: EvaluationInputV2): EvaluationResultV2 {
324
324
  verdict = "indeterminate";
325
325
  }
326
326
 
327
- const inputDigest = sha256Hex(
328
- canonicalPreimage(normalizeInputV2ForDigest(input, coverageSummary))
329
- );
327
+ const inputDigest = evaluationInputDigestV2(input);
330
328
  const partial = {
331
329
  schemaVersion: 2 as const,
332
330
  verdict,
@@ -340,12 +338,24 @@ function evaluateV2(rawInput: EvaluationInputV2): EvaluationResultV2 {
340
338
  coverageSummary,
341
339
  integrity,
342
340
  };
343
- const resultDigest = sha256Hex(
344
- canonicalPreimage({ schema: "evaluation-result:v2", ...partial, inputDigest })
345
- );
341
+ const resultDigest = evaluationResultDigestV2({ ...partial, inputDigest });
346
342
  return evaluationResultV2Schema.parse({ ...partial, inputDigest, resultDigest });
347
343
  }
348
344
 
345
+ /** Hash captured input identity without re-running the rule engine. */
346
+ export function evaluationInputDigestV2(input: EvaluationInputV2): string {
347
+ return sha256Hex(
348
+ canonicalPreimage(
349
+ normalizeInputV2ForDigest(input, deriveCoverageSummaryV1(input.analysisPlan, input.coverage))
350
+ )
351
+ );
352
+ }
353
+
354
+ /** Validate stored result identity using the same algorithm as evaluation. */
355
+ export function evaluationResultDigestV2(result: Omit<EvaluationResultV2, "resultDigest">): string {
356
+ return sha256Hex(canonicalPreimage({ schema: "evaluation-result:v2", ...result }));
357
+ }
358
+
349
359
  function normalizeInputV2ForDigest(
350
360
  input: EvaluationInputV2,
351
361
  coverageSummary: ReturnType<typeof deriveCoverageSummaryV1>
@@ -9,6 +9,8 @@ import { repositoryBindingDigestV1 } from "../repository-binding.js";
9
9
  import {
10
10
  EVALUATOR_VERSION_V2,
11
11
  evaluate,
12
+ evaluationInputDigestV2,
13
+ evaluationResultDigestV2,
12
14
  evaluationInputV2Schema,
13
15
  evaluationReceiptDigestV1,
14
16
  evaluationReceiptMintInputV1Schema,
@@ -550,6 +552,18 @@ describe("EvaluationResultV2", () => {
550
552
  });
551
553
 
552
554
  describe("evaluate V2", () => {
555
+ it("validates stored semantic digests without a second evaluation", () => {
556
+ const input = evaluationInput();
557
+ const result = evaluate(input);
558
+ const { resultDigest, ...semantic } = result;
559
+ expect(evaluationInputDigestV2(input)).toBe(result.inputDigest);
560
+ expect(evaluationResultDigestV2(semantic)).toBe(resultDigest);
561
+ expect(evaluationResultDigestV2({ ...semantic, verdict: "block" })).not.toBe(resultDigest);
562
+ expect(evaluationInputDigestV2({ ...input, evaluatedAt: input.evaluatedAt + 1 })).not.toBe(
563
+ result.inputDigest
564
+ );
565
+ });
566
+
553
567
  it("produces the checked-in deterministic pass result for complete healthy evidence", () => {
554
568
  const result = evaluate(evaluationInput());
555
569
 
@@ -741,14 +755,12 @@ describe("EvaluationReceiptV1", () => {
741
755
  resultDigest: A,
742
756
  evaluatorVersion: "evaluation-kernel:v2",
743
757
  } satisfies EvaluationReceiptMintInputV1;
744
- if (false) {
745
- // @ts-expect-error Source-verified trust is derived with provider proof by the application.
746
- const sourceVerifiedMintInput: EvaluationReceiptMintInputV1 = {
747
- ...external,
748
- trust: "source_verified",
749
- };
750
- void sourceVerifiedMintInput;
751
- }
758
+ // @ts-expect-error Source-verified trust is derived with provider proof by the application.
759
+ const sourceVerifiedMintInput: EvaluationReceiptMintInputV1 = {
760
+ ...external,
761
+ trust: "source_verified",
762
+ };
763
+ void sourceVerifiedMintInput;
752
764
  expect(evaluationReceiptMintInputV1Schema.parse(external)).toEqual(external);
753
765
  expect(() =>
754
766
  evaluationReceiptMintInputV1Schema.parse({ ...external, authority: "enforcement_eligible" })
@@ -3,7 +3,7 @@
3
3
  * Adapters: `packages/cli/src/govern/evaluation.ts` and
4
4
  * `apps/cloud/src/lib/govern/evaluation-adapter.ts`.
5
5
  */
6
- export { evaluate } from "./evaluate.js";
6
+ export { evaluate, evaluationInputDigestV2, evaluationResultDigestV2 } from "./evaluate.js";
7
7
  export { EVALUATOR_VERSION, EVALUATOR_VERSION_V2 } from "./types.js";
8
8
  export {
9
9
  EVALUATION_MAX_FINDINGS_V2,
package/src/governance.ts CHANGED
@@ -172,6 +172,24 @@ export const componentGovernanceRecordSchema = z.discriminatedUnion("kind", [
172
172
 
173
173
  export const componentGovernanceRecordsSchema = z.array(componentGovernanceRecordSchema);
174
174
 
175
+ /** Parse portable component policy records; malformed authority is unavailable, never empty. */
176
+ export function parseComponentGovernancePolicyJson(
177
+ policyJson: string
178
+ ): ComponentGovernanceRecord[] | null {
179
+ try {
180
+ const parsed: unknown = JSON.parse(policyJson);
181
+ const record =
182
+ parsed && typeof parsed === "object" && !Array.isArray(parsed)
183
+ ? (parsed as Record<string, unknown>)
184
+ : null;
185
+ const candidate = Array.isArray(parsed) ? parsed : (record?.governance ?? record?.rules);
186
+ const result = componentGovernanceRecordsSchema.safeParse(candidate);
187
+ return result.success ? result.data : null;
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+
175
193
  const componentPolicyMatchSchema = z
176
194
  .object({
177
195
  componentKey: z.string().min(1).optional(),
package/src/index.ts CHANGED
@@ -508,6 +508,7 @@ export type { InferProps } from "./defineFragment.js";
508
508
  export {
509
509
  componentGovernanceRecordSchema,
510
510
  componentGovernanceRecordsSchema,
511
+ parseComponentGovernancePolicyJson,
511
512
  canonicalBridgeV1Schema,
512
513
  g,
513
514
  globalGovernanceRecordSchema,
@@ -1060,25 +1061,11 @@ export {
1060
1061
  mintEvaluationReceiptId,
1061
1062
  evaluationReceiptIdSchema,
1062
1063
  evaluationReceiptIdStringSchema,
1063
- featurePlanIdFromEntropy,
1064
- featurePlanIdSchema,
1065
- featurePlanIdStringSchema,
1066
- featureRevisionIdFromDigest,
1067
- featureRevisionIdSchema,
1068
- featureRevisionIdStringSchema,
1069
1064
  parseAnalysisPlanId,
1070
1065
  parseDigestHex,
1071
1066
  parseEvaluationReceiptId,
1072
- parseFeaturePlanId,
1073
- parseFeatureRevisionId,
1074
- } from "./domain-ids.js";
1075
- export type {
1076
- AnalysisPlanId,
1077
- DigestHex,
1078
- EvaluationReceiptId,
1079
- FeaturePlanId,
1080
- FeatureRevisionId,
1081
1067
  } from "./domain-ids.js";
1068
+ export type { AnalysisPlanId, DigestHex, EvaluationReceiptId } from "./domain-ids.js";
1082
1069
  export {
1083
1070
  PROVIDER_ID_MAX_BYTES_V1,
1084
1071
  REPOSITORY_BINDING_ID_MAX_BYTES_V1,
@@ -1118,70 +1105,6 @@ export type {
1118
1105
  CoverageSummaryV1,
1119
1106
  } from "./analysis-plan/index.js";
1120
1107
 
1121
- export {
1122
- AGENT_FEATURE_CONTEXT_MAX_BYTES_V1,
1123
- FEATURE_PLAN_MAX_ACCEPTANCE_ITEMS_V1,
1124
- FEATURE_PLAN_MAX_DECISIONS_V1,
1125
- FEATURE_PLAN_MAX_EVIDENCE_REFS_V1,
1126
- FEATURE_PLAN_MAX_GAPS_V1,
1127
- FEATURE_PLAN_MAX_SCENARIOS_V1,
1128
- FEATURE_PLAN_MAX_TEXT_BYTES_V1,
1129
- FEATURE_PLAN_MAX_USES_PER_KIND_V1,
1130
- agentFeatureContextV1Schema,
1131
- approvedFeatureManifestInputV1Schema,
1132
- approvedFeatureManifestRevisionDigestV1,
1133
- approvedFeatureManifestV1Schema,
1134
- buildApprovedFeatureManifestV1,
1135
- buildFeatureProposalV1,
1136
- capabilityGapV1Schema,
1137
- componentUseDecisionV1Schema,
1138
- decisionRequestV1Schema,
1139
- decisionResolutionV1Schema,
1140
- evidenceRefV1Schema,
1141
- featureAcceptanceItemV1Schema,
1142
- featurePinsV1Schema,
1143
- featureProposalInputV1Schema,
1144
- featureProposalRevisionDigestV1,
1145
- featureProposalV1Schema,
1146
- gapDispositionV1Schema,
1147
- layoutConcernV1Schema,
1148
- layoutOwnershipV1Schema,
1149
- normalizeApprovedFeatureManifestInputV1,
1150
- normalizeFeatureProposalInputV1,
1151
- parseAgentFeatureContextV1,
1152
- parseApprovedFeatureManifestV1,
1153
- parseFeatureProposalV1,
1154
- patternStatusV1Schema,
1155
- patternUseDecisionV1Schema,
1156
- placementDecisionV1Schema,
1157
- responsiveDecisionV1Schema,
1158
- scenarioRequirementV1Schema,
1159
- tokenUseDecisionV1Schema,
1160
- } from "./feature-plan/index.js";
1161
- export type {
1162
- AgentFeatureContextV1,
1163
- ApprovedFeatureManifestInputV1,
1164
- ApprovedFeatureManifestV1,
1165
- CapabilityGapV1,
1166
- ComponentUseDecisionV1,
1167
- DecisionRequestV1,
1168
- DecisionResolutionV1,
1169
- EvidenceRefV1,
1170
- FeatureAcceptanceItemV1,
1171
- FeaturePinsV1,
1172
- FeatureProposalInputV1,
1173
- FeatureProposalV1,
1174
- GapDispositionV1,
1175
- LayoutConcernV1,
1176
- LayoutOwnershipV1,
1177
- PatternStatusV1,
1178
- PatternUseDecisionV1,
1179
- PlacementDecisionV1,
1180
- ResponsiveDecisionV1,
1181
- ScenarioRequirementV1,
1182
- TokenUseDecisionV1,
1183
- } from "./feature-plan/index.js";
1184
-
1185
1108
  // Topology — map file paths to product areas (area-scoped governance).
1186
1109
  // Also available at the `@usefragments/core/topology` subpath.
1187
1110
  export { resolveArea } from "./topology/index.js";
@@ -1203,6 +1126,8 @@ export {
1203
1126
  EVALUATOR_VERSION_V2,
1204
1127
  effectiveEvaluationWaiverV2Schema,
1205
1128
  evaluate,
1129
+ evaluationInputDigestV2,
1130
+ evaluationResultDigestV2,
1206
1131
  evaluationInputV2Schema,
1207
1132
  evaluationReceiptDigestV1,
1208
1133
  evaluationReceiptMintInputV1Schema,
@@ -1245,3 +1170,9 @@ export type {
1245
1170
  ProviderProofSemanticV1,
1246
1171
  ProviderProofV1,
1247
1172
  } from "./evaluation/index.js";
1173
+
1174
+ export {
1175
+ APPROVED_CONTRACT_TOKEN_MAX_NAMES,
1176
+ APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH,
1177
+ isApprovedContractTokenNames,
1178
+ } from "./approved-contract-tokens.js";
@@ -6,7 +6,7 @@
6
6
  * preview, cross-repo reconciliation) import it; none reimplements glob/area
7
7
  * logic. Pure and browser-safe — no Node, no parser deps.
8
8
  *
9
- * See `apps/cloud/docs/topology/01-architecture.md §1`.
9
+ * See `docs/fragments-v1/ARCHITECTURE.md §1`.
10
10
  */
11
11
 
12
12
  export type AreaCriticality = "low" | "medium" | "high" | "revenue" | "regulated";
package/src/types.ts CHANGED
@@ -922,9 +922,6 @@ export interface ThemeSeeds {
922
922
  /** Neutral palette name */
923
923
  neutral?: "stone" | "ice" | "earth" | "sand" | "fire" | "fragments";
924
924
 
925
- /** Spacing density scale */
926
- density?: "compact" | "default" | "relaxed";
927
-
928
925
  /** Border radius style */
929
926
  radiusStyle?: "sharp" | "subtle" | "default" | "rounded" | "pill";
930
927
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/topology/resolve-area.ts"],"sourcesContent":["/**\n * Topology — map a repo-relative file path to its product area.\n *\n * This is the DRY keystone of the topology layer: the ONLY area-matching\n * implementation in the monorepo. Consumers (CLI scan, Cloud managed-editor\n * preview, cross-repo reconciliation) import it; none reimplements glob/area\n * logic. Pure and browser-safe — no Node, no parser deps.\n *\n * See `apps/cloud/docs/topology/01-architecture.md §1`.\n */\n\nexport type AreaCriticality = \"low\" | \"medium\" | \"high\" | \"revenue\" | \"regulated\";\n\nexport interface Area {\n id: string;\n name: string;\n criticality: AreaCriticality;\n owners: string[];\n /** Globs matched against repo-relative file paths in v0. */\n files: string[];\n /** Stored for later route-topology adapters; NOT matched in v0. */\n routes?: string[];\n /** Tie-break: higher wins. Defaults to declaration order. */\n priority?: number;\n}\n\nexport interface Topology {\n /** Bump signals \"re-tag\" to consumers that cache. */\n version: number;\n /** Whether area file globs are matched relative to app.path or repo root. Defaults to \"app\". */\n base?: \"app\" | \"repo\";\n /** Array (not a keyed record) so precedence is deterministic. */\n areas: Area[];\n}\n\nexport interface AreaMatch {\n areaId: string;\n areaName: string;\n criticality: AreaCriticality;\n owners: string[];\n /** The winning pattern — surfaced in reports for transparency. */\n matchedGlob: string;\n}\n\n/**\n * Compile a minimal glob to an anchored RegExp. We intentionally do NOT depend\n * on picomatch: core stays lean, the surface we need is small, and we want\n * exact control over Next.js route-group parens (`(marketing)` is a literal\n * path segment here, not an extglob group).\n *\n * Semantics:\n * - `/**` at a segment boundary (end-of-glob or before `/`) → \"this directory\n * and everything under it\": the leading slash is optional, so\n * `src/app/checkout/**` matches `src/app/checkout` AND any descendant.\n * - a bare/leading `**` → matches across path separators (`.*`). Note a\n * leading globstar-then-slash does NOT match the root (use a lone `**`\n * for \"everything\").\n * - `*` → matches within a single segment (`[^/]*`)\n * - `?` → a single non-separator char\n * - `{a,b}` → alternation `(?:a|b)`\n * - every other regex-significant char (`.`, `(`, `)`, `+`, …) is literal\n *\n * Throws on a malformed pattern (e.g. an unbalanced `{`). Callers compile via\n * `compile()`, which catches the throw so a config typo degrades one glob to a\n * non-match instead of aborting the whole scan.\n */\nfunction globToRegExp(glob: string): RegExp {\n let out = \"\";\n let braceDepth = 0;\n let i = 0;\n while (i < glob.length) {\n const ch = glob[i];\n\n // `/**` at a segment boundary → optional slash + anything-below. Matches\n // the directory itself (`a/**` ⇒ `a`) and every descendant.\n if (\n ch === \"/\" &&\n glob[i + 1] === \"*\" &&\n glob[i + 2] === \"*\" &&\n (glob[i + 3] === undefined || glob[i + 3] === \"/\")\n ) {\n out += \"(?:/.*)?\";\n i += 3; // consume `/**`; a trailing `/` (in `a/**/b`) is handled next pass\n continue;\n }\n\n // Bare/leading globstar — crosses path separators.\n if (ch === \"*\" && glob[i + 1] === \"*\") {\n out += \".*\";\n i += 2;\n continue;\n }\n\n switch (ch) {\n case \"*\":\n out += \"[^/]*\"; // single segment\n break;\n case \"?\":\n out += \"[^/]\";\n break;\n case \"{\":\n braceDepth += 1;\n out += \"(?:\";\n break;\n case \"}\":\n if (braceDepth > 0) {\n braceDepth -= 1;\n out += \")\";\n } else {\n out += \"\\\\}\";\n }\n break;\n case \",\":\n out += braceDepth > 0 ? \"|\" : \",\";\n break;\n // Regex-significant chars kept literal (parens cover route groups).\n case \".\":\n case \"(\":\n case \")\":\n case \"+\":\n case \"^\":\n case \"$\":\n case \"|\":\n case \"[\":\n case \"]\":\n case \"\\\\\":\n out += `\\\\${ch}`;\n break;\n default:\n out += ch;\n }\n i += 1;\n }\n return new RegExp(`^${out}$`);\n}\n\ninterface CompiledArea {\n area: Area;\n matchers: { glob: string; re: RegExp }[];\n}\n\n/**\n * Cache compiled matchers per topology object. Consumers pass the same\n * `topology` for every finding in a run, so this turns N×globs recompiles into\n * one. A reloaded config is a new object → fresh entry, so `version` bumps need\n * no manual invalidation.\n */\nconst compiledCache = new WeakMap<Topology, CompiledArea[]>();\n\nfunction compile(topology: Topology): CompiledArea[] {\n const cached = compiledCache.get(topology);\n if (cached) return cached;\n\n // Order by (priority desc, declaration order). Decorate-sort-undecorate keeps\n // the sort stable across engines.\n const ordered = topology.areas\n .map((area, index) => ({ area, index }))\n .sort((a, b) => {\n const pa = a.area.priority ?? 0;\n const pb = b.area.priority ?? 0;\n if (pa !== pb) return pb - pa;\n return a.index - b.index;\n })\n .map(({ area }) => ({\n area,\n matchers: area.files.flatMap((glob) => {\n try {\n return [{ glob, re: globToRegExp(glob) }];\n } catch {\n // A malformed glob (e.g. unbalanced `{`) compiles to an invalid\n // RegExp. Skip it rather than aborting the entire scan — topology\n // config is user-authored, so one typo must not take down\n // `fragments check`. Its files simply fall through to \"Unassigned\".\n if (typeof console !== \"undefined\") {\n console.warn(\n `[topology] ignoring invalid glob in area \"${area.id}\": ${JSON.stringify(glob)}`\n );\n }\n return [];\n }\n }),\n }));\n\n compiledCache.set(topology, ordered);\n return ordered;\n}\n\n/** Normalize to a POSIX, repo-relative path before matching. */\nfunction normalizePath(repoRelPath: string): string {\n return repoRelPath.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").replace(/^\\/+/, \"\");\n}\n\n/**\n * Resolve the product area for a repo-relative path. First match by\n * `(priority desc, declaration order)`. Returns `null` when nothing matches —\n * the caller buckets that as the explicit \"Unassigned\" area (never dropped;\n * dropping unmatched evidence would make coverage lie).\n */\nexport function resolveArea(repoRelPath: string, topology: Topology): AreaMatch | null {\n const path = normalizePath(repoRelPath);\n for (const { area, matchers } of compile(topology)) {\n for (const { glob, re } of matchers) {\n if (re.test(path)) {\n return {\n areaId: area.id,\n areaName: area.name,\n criticality: area.criticality,\n owners: area.owners,\n matchedGlob: glob,\n };\n }\n }\n }\n return null;\n}\n"],"mappings":"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","names":[]}