@usefragments/core 1.10.2 → 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 (130) hide show
  1. package/dist/{chunk-MZ4SW3TP.js → chunk-3IOWHECM.js} +8 -1
  2. package/dist/{chunk-MZ4SW3TP.js.map → chunk-3IOWHECM.js.map} +1 -1
  3. package/dist/chunk-7ULGH74M.js +66 -0
  4. package/dist/chunk-7ULGH74M.js.map +1 -0
  5. package/dist/{chunk-XN3LSDPY.js → chunk-BMPYIUZE.js} +612 -344
  6. package/dist/chunk-BMPYIUZE.js.map +1 -0
  7. package/dist/{chunk-YF65VYRY.js → chunk-ML5S6QNU.js} +191 -169
  8. package/dist/chunk-ML5S6QNU.js.map +1 -0
  9. package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
  10. package/dist/chunk-MZ2FS7U4.js.map +1 -0
  11. package/dist/chunk-PWIJMOI4.js +202 -0
  12. package/dist/chunk-PWIJMOI4.js.map +1 -0
  13. package/dist/chunk-RYFULE43.js +578 -0
  14. package/dist/chunk-RYFULE43.js.map +1 -0
  15. package/dist/codes/index.d.ts +2 -2
  16. package/dist/codes/index.js +3 -2
  17. package/dist/compiled-types/index.d.ts +327 -2
  18. package/dist/compiled-types/index.js +1 -1
  19. package/dist/generate/index.d.ts +3 -2
  20. package/dist/{governance-CLk_wkP9.d.ts → governance-hOPXGbbs.d.ts} +474 -515
  21. package/dist/governance-telemetry.d.ts +6 -0
  22. package/dist/governance-telemetry.js +1 -1
  23. package/dist/{index-_sxhUNqx.d.ts → index-C8bcXVav.d.ts} +448 -448
  24. package/dist/index.d.ts +3280 -1305
  25. package/dist/index.js +2414 -350
  26. package/dist/index.js.map +1 -1
  27. package/dist/manifest.d.ts +228 -0
  28. package/dist/manifest.js +24 -0
  29. package/dist/manifest.js.map +1 -0
  30. package/dist/preview/index.js +45 -1
  31. package/dist/preview/index.js.map +1 -1
  32. package/dist/preview-runtime.d.ts +1 -2
  33. package/dist/preview-runtime.js +150 -14
  34. package/dist/preview-runtime.js.map +1 -1
  35. package/dist/react-types.d.ts +1 -2
  36. package/dist/registry.d.ts +1412 -203
  37. package/dist/registry.js +30 -3
  38. package/dist/schemas/index.d.ts +1 -1
  39. package/dist/schemas/index.js +3 -2
  40. package/dist/storyAdapter.d.ts +1 -2
  41. package/dist/storyAdapter.js +11 -49
  42. package/dist/storyAdapter.js.map +1 -1
  43. package/dist/test-utils.d.ts +3 -2
  44. package/dist/topology/index.d.ts +1 -1
  45. package/dist/topology/index.js +1 -1
  46. package/package.json +8 -2
  47. package/src/__tests__/contract-parser.test.ts +318 -277
  48. package/src/__tests__/preview-runtime-hook.test.tsx +315 -0
  49. package/src/__tests__/preview-runtime.test.tsx +30 -8
  50. package/src/__tests__/schema.test.ts +191 -14
  51. package/src/analysis-plan/analysis-plan-v1.test.ts +320 -0
  52. package/src/analysis-plan/coverage.ts +181 -0
  53. package/src/analysis-plan/digest.ts +141 -0
  54. package/src/analysis-plan/index.ts +34 -0
  55. package/src/analysis-plan/types.ts +207 -0
  56. package/src/approved-contract-tokens.test.ts +39 -0
  57. package/src/approved-contract-tokens.ts +18 -0
  58. package/src/codes/__tests__/codes.test.ts +13 -0
  59. package/src/codes/codes.ts +40 -0
  60. package/src/compiled-types/index.ts +640 -39
  61. package/src/compiled-types/parse.test.ts +145 -4
  62. package/src/component-contract.ts +95 -53
  63. package/src/composition.ts +7 -13
  64. package/src/constants.ts +3 -6
  65. package/src/contract/hash.test.ts +20 -0
  66. package/src/contract/hash.ts +66 -9
  67. package/src/contract/index.ts +24 -1
  68. package/src/contract/manifest.test.ts +94 -0
  69. package/src/contract/manifest.ts +68 -0
  70. package/src/contract/preimage.test.ts +219 -1
  71. package/src/contract/preimage.ts +326 -6
  72. package/src/contract/stamp.test.ts +3 -0
  73. package/src/contract/stamp.ts +1 -1
  74. package/src/contract-parser.ts +54 -30
  75. package/src/defineFragment.test.ts +476 -91
  76. package/src/defineFragment.ts +204 -114
  77. package/src/domain-ids.test.ts +35 -0
  78. package/src/domain-ids.ts +61 -0
  79. package/src/evaluation/evaluate.test.ts +522 -0
  80. package/src/evaluation/evaluate.ts +690 -0
  81. package/src/evaluation/evaluation-v2-receipt-v1.test.ts +772 -0
  82. package/src/evaluation/index.ts +58 -0
  83. package/src/evaluation/receipt.ts +753 -0
  84. package/src/evaluation/types.ts +406 -0
  85. package/src/facts/builders.ts +2 -0
  86. package/src/facts/compile.ts +29 -6
  87. package/src/facts/fact-index.ts +13 -3
  88. package/src/facts/fact-integrity-v1.test.ts +172 -0
  89. package/src/facts/facts.test.ts +15 -0
  90. package/src/facts/ids.ts +46 -3
  91. package/src/facts/index.ts +14 -1
  92. package/src/facts/integrity.ts +134 -0
  93. package/src/facts/types.ts +36 -0
  94. package/src/governance-integrity.test.ts +1 -0
  95. package/src/governance-integrity.ts +5 -3
  96. package/src/governance-telemetry.ts +8 -0
  97. package/src/governance.ts +70 -8
  98. package/src/index.ts +230 -37
  99. package/src/preview/validation.test.ts +62 -0
  100. package/src/preview/validation.ts +48 -2
  101. package/src/preview-runtime.tsx +227 -20
  102. package/src/registry-install-plan.ts +200 -109
  103. package/src/registry-shards.test.ts +263 -0
  104. package/src/registry.ts +237 -0
  105. package/src/repository-binding.test.ts +50 -0
  106. package/src/repository-binding.ts +96 -0
  107. package/src/rules/families.test.ts +36 -0
  108. package/src/rules/finding.ts +7 -2
  109. package/src/rules/index.ts +17 -1
  110. package/src/rules/rule-config.test.ts +66 -0
  111. package/src/rules/rule-config.ts +73 -0
  112. package/src/rules/rules.test.ts +26 -0
  113. package/src/rules/tokens-css-vars-must-be-defined.test.ts +51 -2
  114. package/src/rules/tokens-css-vars-must-be-defined.ts +34 -1
  115. package/src/schema.ts +293 -113
  116. package/src/schemas/index.ts +1 -1
  117. package/src/storyAdapter.test.ts +68 -12
  118. package/src/storyAdapter.ts +44 -75
  119. package/src/topology/resolve-area.ts +1 -1
  120. package/src/types.ts +258 -40
  121. package/dist/chunk-3LLRNCPX.js.map +0 -1
  122. package/dist/chunk-RANPUC6C.js +0 -72
  123. package/dist/chunk-RANPUC6C.js.map +0 -1
  124. package/dist/chunk-XN3LSDPY.js.map +0 -1
  125. package/dist/chunk-YF65VYRY.js.map +0 -1
  126. package/src/fragment-types.ts +0 -214
  127. package/src/react-create-element.test.ts +0 -22
  128. package/src/react-create-element.ts +0 -12
  129. package/src/storyFilters.test.ts +0 -350
  130. package/src/storyFilters.ts +0 -253
@@ -0,0 +1,207 @@
1
+ import { z } from "zod";
2
+ import {
3
+ analysisPlanIdStringSchema,
4
+ digestHexStringSchema,
5
+ type AnalysisPlanId,
6
+ type DigestHex,
7
+ } from "../domain-ids.js";
8
+ import {
9
+ repositoryBindingKeyV1Schema,
10
+ type RepositoryBindingKeyV1,
11
+ } from "../repository-binding.js";
12
+
13
+ export const ANALYSIS_PLAN_MAX_REGIONS_V1 = 1_000;
14
+ export const ANALYSIS_PLAN_MAX_CAPABILITIES_V1 = 256;
15
+ export const ANALYSIS_PLAN_MAX_DIALECTS_PER_REGION_V1 = 64;
16
+
17
+ export const ANALYSIS_LIMIT_CEILINGS_V1 = {
18
+ maxSourceFiles: 100_000,
19
+ maxSourceBytes: 1_073_741_824,
20
+ maxFileBytes: 10_485_760,
21
+ maxFacts: 1_000_000,
22
+ maxRegions: ANALYSIS_PLAN_MAX_REGIONS_V1,
23
+ maxDiagnostics: 100_000,
24
+ maxDurationMs: 900_000,
25
+ } as const;
26
+
27
+ const bounded = (max: number) => z.string().min(1).max(max);
28
+ const positiveBoundedInteger = (max: number) => z.number().int().positive().max(max);
29
+ const uniqueStrings = (values: readonly string[]): boolean =>
30
+ new Set(values).size === values.length;
31
+
32
+ export const coverageStateV1Schema = z.enum([
33
+ "analyzed",
34
+ "unsupported",
35
+ "excluded_by_contract",
36
+ "skipped_limit",
37
+ "failed",
38
+ ]);
39
+ export type CoverageStateV1 = z.infer<typeof coverageStateV1Schema>;
40
+
41
+ export const analysisLimitsV1Schema = z
42
+ .object({
43
+ maxSourceFiles: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxSourceFiles),
44
+ maxSourceBytes: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxSourceBytes),
45
+ maxFileBytes: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxFileBytes),
46
+ maxFacts: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxFacts),
47
+ maxRegions: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxRegions),
48
+ maxDiagnostics: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxDiagnostics),
49
+ maxDurationMs: positiveBoundedInteger(ANALYSIS_LIMIT_CEILINGS_V1.maxDurationMs),
50
+ })
51
+ .strict();
52
+ export type AnalysisLimitsV1 = z.infer<typeof analysisLimitsV1Schema>;
53
+
54
+ export const analysisRegionV1Schema = z
55
+ .object({
56
+ regionId: bounded(256),
57
+ pathPattern: bounded(4_096),
58
+ kind: z.enum(["component_source", "style_source", "token_source", "config"]),
59
+ language: bounded(64),
60
+ required: z.boolean(),
61
+ requiredDialects: z
62
+ .array(bounded(128))
63
+ .max(ANALYSIS_PLAN_MAX_DIALECTS_PER_REGION_V1)
64
+ .refine(uniqueStrings, "requiredDialects must be unique"),
65
+ analyzer: z
66
+ .object({
67
+ id: bounded(256),
68
+ interfaceVersion: bounded(128),
69
+ implementationVersion: bounded(128),
70
+ })
71
+ .strict(),
72
+ })
73
+ .strict();
74
+ export type AnalysisRegionV1 = z.infer<typeof analysisRegionV1Schema>;
75
+
76
+ export const coverageRegionV1Schema = z
77
+ .object({
78
+ regionId: bounded(256),
79
+ state: coverageStateV1Schema,
80
+ fileCount: z.number().int().nonnegative().max(ANALYSIS_LIMIT_CEILINGS_V1.maxSourceFiles),
81
+ byteCount: z.number().int().nonnegative().max(ANALYSIS_LIMIT_CEILINGS_V1.maxSourceBytes),
82
+ diagnosticIds: z
83
+ .array(bounded(256))
84
+ .max(ANALYSIS_LIMIT_CEILINGS_V1.maxDiagnostics)
85
+ .refine(uniqueStrings, "diagnosticIds must be unique"),
86
+ })
87
+ .strict();
88
+ export type CoverageRegionV1 = z.infer<typeof coverageRegionV1Schema>;
89
+
90
+ const coverageCountsV1Schema = z
91
+ .object({
92
+ analyzed: z.number().int().nonnegative(),
93
+ unsupported: z.number().int().nonnegative(),
94
+ excluded_by_contract: z.number().int().nonnegative(),
95
+ skipped_limit: z.number().int().nonnegative(),
96
+ failed: z.number().int().nonnegative(),
97
+ })
98
+ .strict();
99
+
100
+ export const coverageSummaryV1Schema = z
101
+ .object({
102
+ plannedRegions: z.number().int().nonnegative(),
103
+ observedRegions: z.number().int().nonnegative(),
104
+ requiredRegions: z.number().int().nonnegative(),
105
+ requiredByState: coverageCountsV1Schema,
106
+ optionalByState: coverageCountsV1Schema,
107
+ eligibleFiles: z.number().int().nonnegative(),
108
+ analyzedFiles: z.number().int().nonnegative(),
109
+ analyzedBytes: z.number().int().nonnegative(),
110
+ })
111
+ .strict();
112
+ export type CoverageSummaryV1 = z.infer<typeof coverageSummaryV1Schema>;
113
+
114
+ const analysisPlanBaseShape = {
115
+ binding: repositoryBindingKeyV1Schema,
116
+ source: z
117
+ .object({
118
+ commitId: bounded(256),
119
+ treeId: bounded(256).optional(),
120
+ defaultBranch: bounded(256),
121
+ acquiredBy: z.enum(["github_app", "local_worktree", "ci_attested"]),
122
+ })
123
+ .strict(),
124
+ contract: z
125
+ .object({
126
+ fcid: digestHexStringSchema,
127
+ artifactDigest: digestHexStringSchema,
128
+ })
129
+ .strict(),
130
+ config: z
131
+ .object({
132
+ path: bounded(4_096).refine(
133
+ (value) =>
134
+ !value.startsWith("/") && !value.includes("\\") && !value.split("/").includes(".."),
135
+ "config path must be a portable repository-relative path"
136
+ ),
137
+ digest: digestHexStringSchema,
138
+ })
139
+ .strict(),
140
+ profile: z
141
+ .object({
142
+ id: z.literal("react-web-v1"),
143
+ version: bounded(128),
144
+ capabilities: z
145
+ .array(bounded(128))
146
+ .max(ANALYSIS_PLAN_MAX_CAPABILITIES_V1)
147
+ .refine(uniqueStrings, "capabilities must be unique"),
148
+ })
149
+ .strict(),
150
+ regions: z
151
+ .array(analysisRegionV1Schema)
152
+ .max(ANALYSIS_PLAN_MAX_REGIONS_V1)
153
+ .refine(
154
+ (regions) => new Set(regions.map((region) => region.regionId)).size === regions.length,
155
+ "regionId values must be unique"
156
+ ),
157
+ limits: analysisLimitsV1Schema,
158
+ evaluatorVersion: bounded(128),
159
+ createdAt: z.string().datetime({ offset: true }),
160
+ };
161
+
162
+ function validatePlanLimits(
163
+ plan: { regions: readonly AnalysisRegionV1[]; limits: AnalysisLimitsV1 },
164
+ context: z.RefinementCtx
165
+ ): void {
166
+ if (plan.regions.length > plan.limits.maxRegions) {
167
+ context.addIssue({
168
+ code: z.ZodIssueCode.custom,
169
+ path: ["regions"],
170
+ message: "Planned regions exceed the plan's maxRegions limit",
171
+ });
172
+ }
173
+ if (plan.limits.maxFileBytes > plan.limits.maxSourceBytes) {
174
+ context.addIssue({
175
+ code: z.ZodIssueCode.custom,
176
+ path: ["limits", "maxFileBytes"],
177
+ message: "maxFileBytes cannot exceed maxSourceBytes",
178
+ });
179
+ }
180
+ }
181
+
182
+ export const analysisPlanInputV1Schema = z
183
+ .object(analysisPlanBaseShape)
184
+ .strict()
185
+ .superRefine(validatePlanLimits);
186
+ export type AnalysisPlanInputV1 = z.input<typeof analysisPlanInputV1Schema>;
187
+
188
+ export const analysisPlanV1Schema = z
189
+ .object({
190
+ schemaVersion: z.literal(1),
191
+ analysisPlanId: analysisPlanIdStringSchema,
192
+ ...analysisPlanBaseShape,
193
+ obligationsDigest: digestHexStringSchema,
194
+ digest: digestHexStringSchema,
195
+ })
196
+ .strict()
197
+ .superRefine(validatePlanLimits);
198
+
199
+ export interface AnalysisPlanV1 extends Omit<
200
+ z.output<typeof analysisPlanV1Schema>,
201
+ "analysisPlanId" | "digest" | "obligationsDigest"
202
+ > {
203
+ analysisPlanId: AnalysisPlanId;
204
+ binding: RepositoryBindingKeyV1;
205
+ obligationsDigest: DigestHex;
206
+ digest: DigestHex;
207
+ }
@@ -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
+ }
@@ -24,6 +24,19 @@ const SUBSYSTEM_EMITTED_CODES = [
24
24
  // sources are read before any rule runs, and an unreadable one aborts the
25
25
  // scan rather than producing a finding.
26
26
  "FUI9009",
27
+ // Emitted by the compiler, not the rule engine: a `.fragment` source whose
28
+ // definition fails Core's schema, or whose authored claims contradict the
29
+ // extracted component, is refused before any rule has a contract to run on.
30
+ "FUI9010",
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",
36
+ // Emitted by tokens/css-vars-must-be-defined when the rule is active but the
37
+ // resolved contract vocabulary is empty — a config-level inert diagnostic,
38
+ // not a second rule id in RULES.
39
+ "FUI2018",
27
40
  ] as const;
28
41
 
29
42
  const RESERVED_CODES = {
@@ -273,6 +273,16 @@ export const CODES = [
273
273
  lifecycle: "experimental",
274
274
  evidenceRequired: true,
275
275
  }),
276
+ code({
277
+ code: "FUI2018",
278
+ ruleId: "config/empty-token-vocabulary",
279
+ category: "system",
280
+ defaultSeverity: "serious",
281
+ title: "Token sources resolved to 0 tokens",
282
+ lifecycle: "experimental",
283
+ fixAvailable: false,
284
+ evidenceRequired: true,
285
+ }),
276
286
  ruleCode({
277
287
  code: "FUI3001",
278
288
  ruleId: "a11y/required-accessible-name",
@@ -527,6 +537,36 @@ export const CODES = [
527
537
  fixAvailable: false,
528
538
  evidenceRequired: false,
529
539
  }),
540
+ code({
541
+ code: "FUI9010",
542
+ ruleId: "fragment/invalid-definition",
543
+ category: "system",
544
+ defaultSeverity: "critical",
545
+ title: "Fragment definition is not valid",
546
+ lifecycle: "experimental",
547
+ fixAvailable: false,
548
+ evidenceRequired: false,
549
+ }),
550
+ code({
551
+ code: "FUI9011",
552
+ ruleId: "fragment/source-drift",
553
+ category: "system",
554
+ defaultSeverity: "critical",
555
+ title: "Fragment definition drifted from its component source",
556
+ lifecycle: "experimental",
557
+ fixAvailable: false,
558
+ evidenceRequired: false,
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
+ }),
530
570
  ] as const satisfies readonly FuiCode[];
531
571
 
532
572
  export const byCode: ReadonlyMap<string, FuiCode> = new Map(