@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.
- package/dist/{chunk-MZ4SW3TP.js → chunk-3IOWHECM.js} +8 -1
- package/dist/{chunk-MZ4SW3TP.js.map → chunk-3IOWHECM.js.map} +1 -1
- package/dist/chunk-7ULGH74M.js +66 -0
- package/dist/chunk-7ULGH74M.js.map +1 -0
- package/dist/{chunk-XN3LSDPY.js → chunk-BMPYIUZE.js} +612 -344
- package/dist/chunk-BMPYIUZE.js.map +1 -0
- package/dist/{chunk-YF65VYRY.js → chunk-ML5S6QNU.js} +191 -169
- package/dist/chunk-ML5S6QNU.js.map +1 -0
- package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
- package/dist/chunk-MZ2FS7U4.js.map +1 -0
- package/dist/chunk-PWIJMOI4.js +202 -0
- package/dist/chunk-PWIJMOI4.js.map +1 -0
- package/dist/chunk-RYFULE43.js +578 -0
- package/dist/chunk-RYFULE43.js.map +1 -0
- package/dist/codes/index.d.ts +2 -2
- package/dist/codes/index.js +3 -2
- package/dist/compiled-types/index.d.ts +327 -2
- package/dist/compiled-types/index.js +1 -1
- package/dist/generate/index.d.ts +3 -2
- package/dist/{governance-CLk_wkP9.d.ts → governance-hOPXGbbs.d.ts} +474 -515
- package/dist/governance-telemetry.d.ts +6 -0
- package/dist/governance-telemetry.js +1 -1
- package/dist/{index-_sxhUNqx.d.ts → index-C8bcXVav.d.ts} +448 -448
- package/dist/index.d.ts +3280 -1305
- package/dist/index.js +2414 -350
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +228 -0
- package/dist/manifest.js +24 -0
- package/dist/manifest.js.map +1 -0
- package/dist/preview/index.js +45 -1
- package/dist/preview/index.js.map +1 -1
- package/dist/preview-runtime.d.ts +1 -2
- package/dist/preview-runtime.js +150 -14
- package/dist/preview-runtime.js.map +1 -1
- package/dist/react-types.d.ts +1 -2
- package/dist/registry.d.ts +1412 -203
- package/dist/registry.js +30 -3
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +3 -2
- package/dist/storyAdapter.d.ts +1 -2
- package/dist/storyAdapter.js +11 -49
- package/dist/storyAdapter.js.map +1 -1
- package/dist/test-utils.d.ts +3 -2
- package/dist/topology/index.d.ts +1 -1
- package/dist/topology/index.js +1 -1
- package/package.json +8 -2
- package/src/__tests__/contract-parser.test.ts +318 -277
- package/src/__tests__/preview-runtime-hook.test.tsx +315 -0
- package/src/__tests__/preview-runtime.test.tsx +30 -8
- package/src/__tests__/schema.test.ts +191 -14
- package/src/analysis-plan/analysis-plan-v1.test.ts +320 -0
- package/src/analysis-plan/coverage.ts +181 -0
- package/src/analysis-plan/digest.ts +141 -0
- package/src/analysis-plan/index.ts +34 -0
- package/src/analysis-plan/types.ts +207 -0
- package/src/approved-contract-tokens.test.ts +39 -0
- package/src/approved-contract-tokens.ts +18 -0
- package/src/codes/__tests__/codes.test.ts +13 -0
- package/src/codes/codes.ts +40 -0
- package/src/compiled-types/index.ts +640 -39
- package/src/compiled-types/parse.test.ts +145 -4
- package/src/component-contract.ts +95 -53
- package/src/composition.ts +7 -13
- package/src/constants.ts +3 -6
- package/src/contract/hash.test.ts +20 -0
- package/src/contract/hash.ts +66 -9
- package/src/contract/index.ts +24 -1
- package/src/contract/manifest.test.ts +94 -0
- package/src/contract/manifest.ts +68 -0
- package/src/contract/preimage.test.ts +219 -1
- package/src/contract/preimage.ts +326 -6
- package/src/contract/stamp.test.ts +3 -0
- package/src/contract/stamp.ts +1 -1
- package/src/contract-parser.ts +54 -30
- package/src/defineFragment.test.ts +476 -91
- package/src/defineFragment.ts +204 -114
- package/src/domain-ids.test.ts +35 -0
- package/src/domain-ids.ts +61 -0
- package/src/evaluation/evaluate.test.ts +522 -0
- package/src/evaluation/evaluate.ts +690 -0
- package/src/evaluation/evaluation-v2-receipt-v1.test.ts +772 -0
- package/src/evaluation/index.ts +58 -0
- package/src/evaluation/receipt.ts +753 -0
- package/src/evaluation/types.ts +406 -0
- package/src/facts/builders.ts +2 -0
- package/src/facts/compile.ts +29 -6
- package/src/facts/fact-index.ts +13 -3
- package/src/facts/fact-integrity-v1.test.ts +172 -0
- package/src/facts/facts.test.ts +15 -0
- package/src/facts/ids.ts +46 -3
- package/src/facts/index.ts +14 -1
- package/src/facts/integrity.ts +134 -0
- package/src/facts/types.ts +36 -0
- package/src/governance-integrity.test.ts +1 -0
- package/src/governance-integrity.ts +5 -3
- package/src/governance-telemetry.ts +8 -0
- package/src/governance.ts +70 -8
- package/src/index.ts +230 -37
- package/src/preview/validation.test.ts +62 -0
- package/src/preview/validation.ts +48 -2
- package/src/preview-runtime.tsx +227 -20
- package/src/registry-install-plan.ts +200 -109
- package/src/registry-shards.test.ts +263 -0
- package/src/registry.ts +237 -0
- package/src/repository-binding.test.ts +50 -0
- package/src/repository-binding.ts +96 -0
- package/src/rules/families.test.ts +36 -0
- package/src/rules/finding.ts +7 -2
- package/src/rules/index.ts +17 -1
- package/src/rules/rule-config.test.ts +66 -0
- package/src/rules/rule-config.ts +73 -0
- package/src/rules/rules.test.ts +26 -0
- package/src/rules/tokens-css-vars-must-be-defined.test.ts +51 -2
- package/src/rules/tokens-css-vars-must-be-defined.ts +34 -1
- package/src/schema.ts +293 -113
- package/src/schemas/index.ts +1 -1
- package/src/storyAdapter.test.ts +68 -12
- package/src/storyAdapter.ts +44 -75
- package/src/topology/resolve-area.ts +1 -1
- package/src/types.ts +258 -40
- package/dist/chunk-3LLRNCPX.js.map +0 -1
- package/dist/chunk-RANPUC6C.js +0 -72
- package/dist/chunk-RANPUC6C.js.map +0 -1
- package/dist/chunk-XN3LSDPY.js.map +0 -1
- package/dist/chunk-YF65VYRY.js.map +0 -1
- package/src/fragment-types.ts +0 -214
- package/src/react-create-element.test.ts +0 -22
- package/src/react-create-element.ts +0 -12
- package/src/storyFilters.test.ts +0 -350
- package/src/storyFilters.ts +0 -253
package/src/rules/rules.test.ts
CHANGED
|
@@ -1540,6 +1540,32 @@ describe("runRules — Phase 5 vertical slice", () => {
|
|
|
1540
1540
|
}
|
|
1541
1541
|
});
|
|
1542
1542
|
|
|
1543
|
+
it("never runs a rule whose governance_rule_config fact is disabled", () => {
|
|
1544
|
+
// `govern.rules["styles/no-raw-spacing"] = { enabled: false }` compiles to a
|
|
1545
|
+
// rule-config fact. Enablement is honoured INSIDE runRules so the engine scan
|
|
1546
|
+
// and the kernel's facts-mode evaluation agree on which rules exist — a
|
|
1547
|
+
// post-filter in one evaluator is how a disabled rule blocked `--ci`.
|
|
1548
|
+
const ix = emptyIndexWithGovernance();
|
|
1549
|
+
ix.add(makeGovernanceRuleConfigFact({ ruleId: "styles/no-raw-spacing", enabled: false }));
|
|
1550
|
+
addButtonUsage(ix, {
|
|
1551
|
+
file: "apps/checkout/page.tsx",
|
|
1552
|
+
nodePath: "0/0",
|
|
1553
|
+
line: 42,
|
|
1554
|
+
column: 8,
|
|
1555
|
+
props: [{ prop: "variant", value: "secondary" }],
|
|
1556
|
+
inlineStyles: [
|
|
1557
|
+
{ property: "padding", valueKind: "number", value: "13" },
|
|
1558
|
+
{ property: "color", valueKind: "static", value: "#2563eb" },
|
|
1559
|
+
],
|
|
1560
|
+
text: "Continue",
|
|
1561
|
+
});
|
|
1562
|
+
|
|
1563
|
+
const ruleIds = runRules(ix)
|
|
1564
|
+
.map((f) => f.ruleId)
|
|
1565
|
+
.sort();
|
|
1566
|
+
expect(ruleIds).toEqual(["components/forbidden-prop-value", "styles/no-raw-color"]);
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1543
1569
|
it("emits stable fingerprints across runs", () => {
|
|
1544
1570
|
function buildIndex(): FactIndex {
|
|
1545
1571
|
const ix = emptyIndexWithGovernance();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
compileGlobalGovernanceFacts,
|
|
4
5
|
FactIndex,
|
|
5
6
|
makeContractTokenFact,
|
|
6
7
|
makeStyleCssVarsMustBeDefinedFact,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
import type { Fact } from "../index.js";
|
|
10
11
|
import {
|
|
11
12
|
contractTokenReferenceCensus,
|
|
13
|
+
emptyVocabularyConfigFile,
|
|
12
14
|
ruleTokensCssVarsMustBeDefined,
|
|
13
15
|
} from "./tokens-css-vars-must-be-defined.js";
|
|
14
16
|
|
|
@@ -121,10 +123,45 @@ describe("ruleTokensCssVarsMustBeDefined", () => {
|
|
|
121
123
|
expect(ruleTokensCssVarsMustBeDefined(ix)).toHaveLength(0);
|
|
122
124
|
});
|
|
123
125
|
|
|
124
|
-
it("
|
|
126
|
+
it("does not compile an activation fact from a rules-map enablement alone", () => {
|
|
127
|
+
const facts = compileGlobalGovernanceFacts({
|
|
128
|
+
rules: { "tokens/css-vars-must-be-defined": { enabled: true, severity: "error" } },
|
|
129
|
+
});
|
|
130
|
+
expect(facts.some((fact) => fact.kind === "style_css_vars_must_be_defined")).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("emits one config-level inert finding when the contract vocabulary is empty", () => {
|
|
125
134
|
const ix = index([POLICY, decl(".b", "var(--fui-color-brand)")]);
|
|
126
135
|
|
|
127
|
-
|
|
136
|
+
const findings = ruleTokensCssVarsMustBeDefined(ix);
|
|
137
|
+
expect(findings).toHaveLength(1);
|
|
138
|
+
expect(findings[0]).toMatchObject({
|
|
139
|
+
ruleId: "tokens/css-vars-must-be-defined",
|
|
140
|
+
code: "FUI2018",
|
|
141
|
+
severity: "serious",
|
|
142
|
+
message: "token sources resolved to 0 tokens — rule inert",
|
|
143
|
+
location: { file: "your fragments config", line: 0, column: 0 },
|
|
144
|
+
attributes: { inert: true, tokenCount: 0, source: "config" },
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("points the inert finding at the resolved config path when the policy fact carries it", () => {
|
|
149
|
+
const policy = makeStyleCssVarsMustBeDefinedFact({
|
|
150
|
+
severity: "warn",
|
|
151
|
+
configPath: "fragments.config.json",
|
|
152
|
+
});
|
|
153
|
+
const findings = ruleTokensCssVarsMustBeDefined(index([policy]));
|
|
154
|
+
expect(findings).toHaveLength(1);
|
|
155
|
+
expect(findings[0].location.file).toBe("fragments.config.json");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("keeps ordinary drift findings when the vocabulary is non-empty", () => {
|
|
159
|
+
const ix = index([POLICY, ...vocabulary(), decl(".b", "var(--fui-color-brand)")]);
|
|
160
|
+
|
|
161
|
+
const findings = ruleTokensCssVarsMustBeDefined(ix);
|
|
162
|
+
expect(findings).toHaveLength(1);
|
|
163
|
+
expect(findings[0].code).toBe("FUI2015");
|
|
164
|
+
expect(findings[0].attributes).toMatchObject({ tokenName: "--fui-color-brand" });
|
|
128
165
|
});
|
|
129
166
|
|
|
130
167
|
// #33 — CSS function names are case-insensitive; VAR()/Var() must be detected.
|
|
@@ -307,3 +344,15 @@ describe("contractTokenReferenceCensus", () => {
|
|
|
307
344
|
expect(census).toEqual({ checkedSites: 0 });
|
|
308
345
|
});
|
|
309
346
|
});
|
|
347
|
+
|
|
348
|
+
describe("emptyVocabularyConfigFile", () => {
|
|
349
|
+
it("uses the resolved filename when a path is present", () => {
|
|
350
|
+
expect(emptyVocabularyConfigFile("fragments.config.json")).toBe("fragments.config.json");
|
|
351
|
+
expect(emptyVocabularyConfigFile("apps/docs/fragments.config.ts")).toBe("fragments.config.ts");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("drops the filename when the path is missing", () => {
|
|
355
|
+
expect(emptyVocabularyConfigFile(undefined)).toBe("your fragments config");
|
|
356
|
+
expect(emptyVocabularyConfigFile(null)).toBe("your fragments config");
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -3,9 +3,20 @@ import type { FactIndex } from "../facts/index.js";
|
|
|
3
3
|
import { makeFinding } from "./finding.js";
|
|
4
4
|
import type { Finding } from "./types.js";
|
|
5
5
|
import { contractPrefixFamilies } from "./utils.js";
|
|
6
|
+
import type { StyleCssVarsMustBeDefinedFact } from "../facts/index.js";
|
|
6
7
|
|
|
7
8
|
export const RULE_ID = "tokens/css-vars-must-be-defined";
|
|
8
9
|
export const RULE_VERSION = "1";
|
|
10
|
+
export const EMPTY_VOCABULARY_CODE = "FUI2018";
|
|
11
|
+
export const EMPTY_VOCABULARY_MESSAGE = "token sources resolved to 0 tokens — rule inert";
|
|
12
|
+
export const EMPTY_VOCABULARY_CONFIG_FALLBACK = "your fragments config";
|
|
13
|
+
|
|
14
|
+
/** Filename of the resolved fragments config, or a filename-free fallback. */
|
|
15
|
+
export function emptyVocabularyConfigFile(configPath?: string | null): string {
|
|
16
|
+
if (!configPath) return EMPTY_VOCABULARY_CONFIG_FALLBACK;
|
|
17
|
+
const base = configPath.replaceAll("\\", "/").replace(/\/+$/u, "").split("/").pop();
|
|
18
|
+
return base && base.length > 0 ? base : EMPTY_VOCABULARY_CONFIG_FALLBACK;
|
|
19
|
+
}
|
|
9
20
|
|
|
10
21
|
// The leading `var(--name` of a reference. The `var(` keyword is matched
|
|
11
22
|
// case-insensitively (CSS function names are case-insensitive per spec, so
|
|
@@ -101,7 +112,7 @@ export function ruleTokensCssVarsMustBeDefined(ix: FactIndex): Finding[] {
|
|
|
101
112
|
const policy = ix.policy.cssVarsMustBeDefined();
|
|
102
113
|
if (!policy) return [];
|
|
103
114
|
const context = contractVocabularyContext(ix);
|
|
104
|
-
if (!context) return [];
|
|
115
|
+
if (!context) return [emptyVocabularyFinding(ix, policy)];
|
|
105
116
|
const { vocabulary } = context;
|
|
106
117
|
|
|
107
118
|
const findings: Finding[] = [];
|
|
@@ -155,6 +166,28 @@ export function ruleTokensCssVarsMustBeDefined(ix: FactIndex): Finding[] {
|
|
|
155
166
|
return findings;
|
|
156
167
|
}
|
|
157
168
|
|
|
169
|
+
function emptyVocabularyFinding(ix: FactIndex, policy: StyleCssVarsMustBeDefinedFact): Finding {
|
|
170
|
+
return makeFinding({
|
|
171
|
+
ruleId: RULE_ID,
|
|
172
|
+
ruleVersion: RULE_VERSION,
|
|
173
|
+
severity: "error",
|
|
174
|
+
code: EMPTY_VOCABULARY_CODE,
|
|
175
|
+
message: EMPTY_VOCABULARY_MESSAGE,
|
|
176
|
+
location: {
|
|
177
|
+
file: policy.configPath ?? EMPTY_VOCABULARY_CONFIG_FALLBACK,
|
|
178
|
+
line: 0,
|
|
179
|
+
column: 0,
|
|
180
|
+
},
|
|
181
|
+
evidence: ix.evidence([policy.id]),
|
|
182
|
+
fingerprintIdentity: { reason: "empty-vocabulary" },
|
|
183
|
+
attributes: {
|
|
184
|
+
source: "config",
|
|
185
|
+
inert: true,
|
|
186
|
+
tokenCount: 0,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
158
191
|
function sharesContractPrefix(name: string, prefixes: ReadonlySet<string>): boolean {
|
|
159
192
|
for (const prefix of prefixes) {
|
|
160
193
|
if (name.startsWith(prefix)) return true;
|
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type {
|
|
3
|
+
FragmentAnnotationsV3,
|
|
4
|
+
FragmentDefinitionV3,
|
|
5
|
+
FragmentDefinitionV3Body,
|
|
6
|
+
FragmentDesignV3,
|
|
7
|
+
FragmentDontExampleV3,
|
|
8
|
+
FragmentGuidanceV3,
|
|
9
|
+
FragmentMatrixV3,
|
|
10
|
+
FragmentMetaV3,
|
|
11
|
+
FragmentPreviewV3,
|
|
12
|
+
FragmentStateV3,
|
|
13
|
+
PropAnnotation,
|
|
14
|
+
} from "./types.js";
|
|
2
15
|
import { portableRepoPathError } from "./config-paths.js";
|
|
3
16
|
import { componentGovernanceRecordSchema, governanceConfigSchema } from "./governance.js";
|
|
4
17
|
import { compositionAuthoringEntrySchema } from "./rules/composition-pattern-schema.js";
|
|
@@ -13,10 +26,15 @@ const figmaStringMappingSchema = z.object({
|
|
|
13
26
|
figmaProperty: z.string().min(1),
|
|
14
27
|
});
|
|
15
28
|
|
|
29
|
+
const requiredUnknownSchema = z.custom<unknown>(
|
|
30
|
+
(value) => value !== undefined,
|
|
31
|
+
"Expected an explicitly present value"
|
|
32
|
+
);
|
|
33
|
+
|
|
16
34
|
const figmaBooleanMappingSchema = z.object({
|
|
17
35
|
__type: z.literal("figma-boolean"),
|
|
18
36
|
figmaProperty: z.string().min(1),
|
|
19
|
-
valueMapping: z.object({ true:
|
|
37
|
+
valueMapping: z.object({ true: requiredUnknownSchema, false: requiredUnknownSchema }).optional(),
|
|
20
38
|
});
|
|
21
39
|
|
|
22
40
|
const figmaEnumMappingSchema = z.object({
|
|
@@ -143,7 +161,7 @@ export const fragmentContractSchema = z.object({
|
|
|
143
161
|
a11yRules: z.array(z.string()).optional(),
|
|
144
162
|
bans: z.array(fragmentBanSchema).optional(),
|
|
145
163
|
scenarioTags: z.array(z.string()).optional(),
|
|
146
|
-
performanceBudget: z.number().positive().optional(),
|
|
164
|
+
performanceBudget: z.number().positive().finite().optional(),
|
|
147
165
|
compoundChildren: z
|
|
148
166
|
.record(
|
|
149
167
|
z.object({
|
|
@@ -163,10 +181,21 @@ export const fragmentContractSchema = z.object({
|
|
|
163
181
|
/**
|
|
164
182
|
* Schema for provenance tracking of generated fragments
|
|
165
183
|
*/
|
|
184
|
+
export const fragmentSourceValues = [
|
|
185
|
+
"storybook",
|
|
186
|
+
"manual",
|
|
187
|
+
"ai",
|
|
188
|
+
"scan",
|
|
189
|
+
"extracted",
|
|
190
|
+
"merged",
|
|
191
|
+
"migrated",
|
|
192
|
+
] as const;
|
|
193
|
+
|
|
166
194
|
export const fragmentGeneratedSchema = z.object({
|
|
167
|
-
source: z.enum(
|
|
195
|
+
source: z.enum(fragmentSourceValues),
|
|
168
196
|
sourceFile: z.string().optional(),
|
|
169
197
|
confidence: z.number().min(0).max(1).optional(),
|
|
198
|
+
verified: z.boolean().optional(),
|
|
170
199
|
timestamp: z.string().datetime().optional(),
|
|
171
200
|
});
|
|
172
201
|
|
|
@@ -299,102 +328,92 @@ const topologySchema = z.object({
|
|
|
299
328
|
* Type definitions are in types.ts - schema just ensures basic structure.
|
|
300
329
|
*/
|
|
301
330
|
export const fragmentsConfigSchema = z.object({
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
331
|
+
app: z
|
|
332
|
+
.object({
|
|
333
|
+
path: repoRelativePathSchema.optional(),
|
|
334
|
+
include: repoRelativePathsSchema.optional(),
|
|
335
|
+
exclude: z.array(repoRelativePathSchema).optional(),
|
|
336
|
+
})
|
|
337
|
+
.optional(),
|
|
338
|
+
designSystem: z
|
|
339
|
+
.object({
|
|
340
|
+
path: repoRelativePathSchema.optional(),
|
|
341
|
+
packageName: z.string().min(1).optional(),
|
|
342
|
+
components: repoRelativePathsSchema.optional(),
|
|
343
|
+
})
|
|
344
|
+
.optional(),
|
|
345
|
+
include: repoRelativePathsSchema.optional(),
|
|
346
|
+
exclude: z.array(repoRelativePathSchema).optional(),
|
|
347
|
+
components: repoRelativePathsSchema.optional(),
|
|
348
|
+
outFile: repoRelativePathSchema.optional(),
|
|
349
|
+
framework: z.enum(["react", "vue", "svelte"]).optional(),
|
|
350
|
+
figmaFile: z.string().url().optional(),
|
|
351
|
+
figmaToken: z.string().optional(),
|
|
352
|
+
screenshots: z.object({}).passthrough().optional(),
|
|
353
|
+
service: z.object({}).passthrough().optional(),
|
|
354
|
+
registry: z.object({}).passthrough().optional(),
|
|
355
|
+
tokens: tokenConfigSchema.optional(),
|
|
356
|
+
snippets: z
|
|
357
|
+
.object({
|
|
358
|
+
mode: z.enum(["warn", "error"]).optional(),
|
|
359
|
+
scope: z.enum(["snippet", "snippet+render"]).optional(),
|
|
360
|
+
requireFullSnippet: z.boolean().optional(),
|
|
361
|
+
allowedExternalModules: z.array(z.string().min(1)).optional(),
|
|
362
|
+
})
|
|
363
|
+
.optional(),
|
|
364
|
+
performance: z
|
|
365
|
+
.union([
|
|
366
|
+
z.enum(["strict", "standard", "relaxed"]),
|
|
367
|
+
z.object({
|
|
368
|
+
preset: z.enum(["strict", "standard", "relaxed"]).optional(),
|
|
369
|
+
budgets: z
|
|
370
|
+
.object({
|
|
371
|
+
bundleSize: z.number().positive().optional(),
|
|
372
|
+
})
|
|
373
|
+
.optional(),
|
|
374
|
+
}),
|
|
375
|
+
])
|
|
376
|
+
.optional(),
|
|
377
|
+
topology: topologySchema.optional(),
|
|
378
|
+
govern: governanceConfigSchema.optional(),
|
|
379
|
+
inspect: z
|
|
380
|
+
.object({
|
|
381
|
+
localCanonical: z
|
|
382
|
+
.record(
|
|
383
|
+
z.string().min(1),
|
|
384
|
+
z.object({
|
|
385
|
+
importPath: z.string().min(1),
|
|
386
|
+
resolves: z
|
|
387
|
+
.array(
|
|
388
|
+
z.object({
|
|
389
|
+
tag: z.string().min(1),
|
|
390
|
+
role: z.string().min(1).optional(),
|
|
391
|
+
inputType: z.string().min(1).optional(),
|
|
392
|
+
})
|
|
393
|
+
)
|
|
394
|
+
.min(1),
|
|
395
|
+
})
|
|
396
|
+
)
|
|
344
397
|
.optional(),
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
.optional(),
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
z.string().min(1),
|
|
365
|
-
z.object({
|
|
366
|
-
importPath: z.string().min(1),
|
|
367
|
-
resolves: z
|
|
368
|
-
.array(
|
|
369
|
-
z.object({
|
|
370
|
-
tag: z.string().min(1),
|
|
371
|
-
role: z.string().min(1).optional(),
|
|
372
|
-
inputType: z.string().min(1).optional(),
|
|
373
|
-
})
|
|
374
|
-
)
|
|
375
|
-
.min(1),
|
|
376
|
-
})
|
|
377
|
-
)
|
|
378
|
-
.optional(),
|
|
379
|
-
})
|
|
380
|
-
.optional(),
|
|
381
|
-
recognizedClassHelpers: z.array(z.string().min(1)).optional(),
|
|
382
|
-
identity: z
|
|
383
|
-
.object({
|
|
384
|
-
decisions: z
|
|
385
|
-
.array(
|
|
386
|
-
z.object({
|
|
387
|
-
component: z.string().min(1),
|
|
388
|
-
kind: z.enum(["sanction", "reject", "dismiss"]),
|
|
389
|
-
canonicalTarget: z.string().min(1).optional(),
|
|
390
|
-
reason: z.string().optional(),
|
|
391
|
-
decisionId: z.string().min(1).optional(),
|
|
392
|
-
})
|
|
393
|
-
)
|
|
394
|
-
.optional(),
|
|
395
|
-
})
|
|
396
|
-
.optional(),
|
|
397
|
-
});
|
|
398
|
+
})
|
|
399
|
+
.optional(),
|
|
400
|
+
recognizedClassHelpers: z.array(z.string().min(1)).optional(),
|
|
401
|
+
identity: z
|
|
402
|
+
.object({
|
|
403
|
+
decisions: z
|
|
404
|
+
.array(
|
|
405
|
+
z.object({
|
|
406
|
+
component: z.string().min(1),
|
|
407
|
+
kind: z.enum(["sanction", "reject", "dismiss"]),
|
|
408
|
+
canonicalTarget: z.string().min(1).optional(),
|
|
409
|
+
reason: z.string().optional(),
|
|
410
|
+
decisionId: z.string().min(1).optional(),
|
|
411
|
+
})
|
|
412
|
+
)
|
|
413
|
+
.optional(),
|
|
414
|
+
})
|
|
415
|
+
.optional(),
|
|
416
|
+
});
|
|
398
417
|
|
|
399
418
|
// ---------------------------------------------------------------------------
|
|
400
419
|
// v2 Schemas — alongside v1 (non-breaking)
|
|
@@ -415,10 +434,13 @@ export const compositionMetadataSchema = z.object({
|
|
|
415
434
|
* Schema for v2 extended provenance tracking
|
|
416
435
|
*/
|
|
417
436
|
export const fragmentProvenanceSchema = z.object({
|
|
418
|
-
source: z.enum(
|
|
437
|
+
source: z.enum(fragmentSourceValues),
|
|
419
438
|
sourceFile: z.string().optional(),
|
|
420
439
|
confidence: z.number().min(0).max(1).optional(),
|
|
421
|
-
|
|
440
|
+
verified: z.boolean().optional(),
|
|
441
|
+
frameworkSupport: z.enum(["native", "manual-only"]).optional(),
|
|
442
|
+
sourceHash: z.string().optional(),
|
|
443
|
+
timestamp: z.string().datetime({ offset: true }).optional(),
|
|
422
444
|
autoFields: z.array(z.string()).optional(),
|
|
423
445
|
humanFields: z.array(z.string()).optional(),
|
|
424
446
|
});
|
|
@@ -439,21 +461,179 @@ export const fragmentDefinitionV2Schema = z.object({
|
|
|
439
461
|
_provenance: fragmentProvenanceSchema.optional(),
|
|
440
462
|
});
|
|
441
463
|
|
|
442
|
-
export const governedFragmentDefinitionSchema = z
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
464
|
+
export const governedFragmentDefinitionSchema = z
|
|
465
|
+
.object({
|
|
466
|
+
component: z.any(),
|
|
467
|
+
meta: fragmentMetaSchema,
|
|
468
|
+
guidance: fragmentUsageSchema,
|
|
469
|
+
props: z.record(propDefinitionSchema).optional(),
|
|
470
|
+
relations: z.array(componentRelationSchema).optional(),
|
|
471
|
+
examples: z.array(fragmentVariantSchema).optional(),
|
|
472
|
+
composition: compositionMetadataSchema.optional(),
|
|
473
|
+
contract: fragmentContractSchema.optional(),
|
|
474
|
+
_provenance: fragmentProvenanceSchema.optional(),
|
|
475
|
+
govern: z.function().optional(),
|
|
476
|
+
governance: z.array(componentGovernanceRecordSchema).optional(),
|
|
477
|
+
})
|
|
478
|
+
.superRefine((def, ctx) => {
|
|
479
|
+
if (def.govern === undefined && def.governance === undefined) {
|
|
480
|
+
ctx.addIssue({
|
|
481
|
+
code: z.ZodIssueCode.custom,
|
|
482
|
+
message: "Governed definitions require `govern` or `governance`",
|
|
483
|
+
path: ["govern"],
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const fragmentRenderSchema = z.custom<FragmentStateV3["render"]>(
|
|
489
|
+
(value) => value !== undefined && typeof value !== "string",
|
|
490
|
+
"Expected JSX/render value, not a code string"
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
export const fragmentMetaV3Schema = z
|
|
494
|
+
.object({
|
|
495
|
+
name: z.string().min(1),
|
|
496
|
+
purpose: z.string().min(1),
|
|
497
|
+
category: z.string().min(1),
|
|
498
|
+
status: z.enum(["stable", "beta", "deprecated", "experimental"]).optional(),
|
|
499
|
+
aliases: z.array(z.string().min(1)).optional(),
|
|
500
|
+
tags: z.array(z.string()).optional(),
|
|
501
|
+
since: z.string().optional(),
|
|
502
|
+
dependencies: z
|
|
503
|
+
.array(
|
|
504
|
+
z
|
|
505
|
+
.object({
|
|
506
|
+
name: z.string().min(1),
|
|
507
|
+
version: z.string().min(1),
|
|
508
|
+
reason: z.string().optional(),
|
|
509
|
+
})
|
|
510
|
+
.strict()
|
|
511
|
+
)
|
|
512
|
+
.optional(),
|
|
513
|
+
figma: z.string().url().optional(),
|
|
514
|
+
figmaProps: z.record(figmaPropMappingSchema).optional(),
|
|
515
|
+
})
|
|
516
|
+
.strict() as unknown as z.ZodType<FragmentMetaV3>;
|
|
517
|
+
|
|
518
|
+
export const fragmentStateV3Schema: z.ZodType<FragmentStateV3> = z
|
|
519
|
+
.object({
|
|
520
|
+
render: fragmentRenderSchema,
|
|
521
|
+
note: z.string().optional(),
|
|
522
|
+
canonical: z.boolean().optional(),
|
|
523
|
+
})
|
|
524
|
+
.strict();
|
|
525
|
+
|
|
526
|
+
export const fragmentDontExampleV3Schema: z.ZodType<FragmentDontExampleV3> = z
|
|
527
|
+
.object({
|
|
528
|
+
reason: z.string().min(1),
|
|
529
|
+
bad: z.string().min(1),
|
|
530
|
+
good: fragmentRenderSchema,
|
|
531
|
+
})
|
|
532
|
+
.strict();
|
|
533
|
+
|
|
534
|
+
export const fragmentGuidanceV3Schema: z.ZodType<FragmentGuidanceV3> = z
|
|
535
|
+
.object({
|
|
536
|
+
when: z.array(z.string()),
|
|
537
|
+
whenNot: z.array(z.string()),
|
|
538
|
+
choose: z.record(z.string()).optional(),
|
|
539
|
+
dont: z.array(fragmentDontExampleV3Schema).optional(),
|
|
540
|
+
guidelines: z.array(z.string()).optional(),
|
|
541
|
+
accessibility: z.array(z.string()).optional(),
|
|
542
|
+
})
|
|
543
|
+
.strict();
|
|
544
|
+
|
|
545
|
+
export const fragmentMatrixV3Schema: z.ZodType<FragmentMatrixV3> = z
|
|
546
|
+
.object({
|
|
547
|
+
axes: z.record(z.union([z.literal("auto"), z.array(z.string().min(1)).readonly()])).optional(),
|
|
548
|
+
forced: z.array(z.string().min(1)).optional(),
|
|
549
|
+
worstCase: z.record(z.unknown()).optional(),
|
|
550
|
+
})
|
|
551
|
+
.strict();
|
|
552
|
+
|
|
553
|
+
export const fragmentPreviewV3Schema: z.ZodType<FragmentPreviewV3> = z
|
|
554
|
+
.object({
|
|
555
|
+
providers: z.array(z.unknown()).optional(),
|
|
556
|
+
dynamicRegions: z.array(z.string()).optional(),
|
|
557
|
+
})
|
|
558
|
+
.strict();
|
|
559
|
+
|
|
560
|
+
export const fragmentDesignV3Schema: z.ZodType<FragmentDesignV3> = z
|
|
561
|
+
.object({
|
|
562
|
+
figmaNode: z.string().min(1).optional(),
|
|
563
|
+
})
|
|
564
|
+
.strict();
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Annotate-only prop entries. Forbidden keys (`type`, `default`, `values`,
|
|
568
|
+
* `required`) redefine extracted truth and are rejected.
|
|
569
|
+
*/
|
|
570
|
+
export const propAnnotationSchema: z.ZodType<PropAnnotation> = z
|
|
571
|
+
.object({
|
|
572
|
+
description: z.string().optional(),
|
|
573
|
+
controlType: z
|
|
574
|
+
.enum([
|
|
575
|
+
"text",
|
|
576
|
+
"number",
|
|
577
|
+
"range",
|
|
578
|
+
"boolean",
|
|
579
|
+
"select",
|
|
580
|
+
"multi-select",
|
|
581
|
+
"radio",
|
|
582
|
+
"inline-radio",
|
|
583
|
+
"check",
|
|
584
|
+
"inline-check",
|
|
585
|
+
"object",
|
|
586
|
+
"file",
|
|
587
|
+
"color",
|
|
588
|
+
"date",
|
|
589
|
+
])
|
|
590
|
+
.optional(),
|
|
591
|
+
controlOptions: z.record(z.unknown()).optional(),
|
|
592
|
+
visibility: z.enum(["public", "internal", "hidden"]).optional(),
|
|
593
|
+
constraints: z.array(z.string()).optional(),
|
|
594
|
+
})
|
|
595
|
+
.strict();
|
|
596
|
+
|
|
597
|
+
export const fragmentAnnotationsV3Schema: z.ZodType<FragmentAnnotationsV3> = z
|
|
598
|
+
.object({
|
|
599
|
+
notes: z.array(z.string()).optional(),
|
|
600
|
+
review: z.array(z.string()).optional(),
|
|
601
|
+
})
|
|
602
|
+
.passthrough();
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Schema for the v3 definition body (second argument). Arity — not keys —
|
|
606
|
+
* discriminates v3 at the call site; this schema never accepts authored `code`.
|
|
607
|
+
*/
|
|
608
|
+
const fragmentDefinitionV3BodyShape = {
|
|
609
|
+
meta: fragmentMetaV3Schema,
|
|
610
|
+
states: z.record(fragmentStateV3Schema).refine((states) => Object.keys(states).length > 0, {
|
|
611
|
+
message: "At least one state is required",
|
|
612
|
+
}),
|
|
613
|
+
guidance: fragmentGuidanceV3Schema,
|
|
614
|
+
matrix: fragmentMatrixV3Schema.optional(),
|
|
615
|
+
preview: fragmentPreviewV3Schema.optional(),
|
|
616
|
+
design: fragmentDesignV3Schema.optional(),
|
|
617
|
+
annotations: fragmentAnnotationsV3Schema.optional(),
|
|
618
|
+
props: z.record(propAnnotationSchema).optional(),
|
|
619
|
+
relations: z.array(componentRelationSchema.strict()).optional(),
|
|
620
|
+
composition: compositionMetadataSchema.strict().optional(),
|
|
450
621
|
contract: fragmentContractSchema.optional(),
|
|
451
622
|
_provenance: fragmentProvenanceSchema.optional(),
|
|
452
623
|
govern: z.function().optional(),
|
|
453
624
|
governance: z.array(componentGovernanceRecordSchema).optional(),
|
|
454
|
-
}
|
|
625
|
+
};
|
|
455
626
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
627
|
+
export const fragmentDefinitionV3BodySchema = z
|
|
628
|
+
.object(fragmentDefinitionV3BodyShape)
|
|
629
|
+
.strict() as unknown as z.ZodType<FragmentDefinitionV3Body<unknown>>;
|
|
630
|
+
|
|
631
|
+
export const fragmentDefinitionV3Schema = z
|
|
632
|
+
.object({
|
|
633
|
+
...fragmentDefinitionV3BodyShape,
|
|
634
|
+
component: z.unknown().refine((component) => component !== undefined, {
|
|
635
|
+
message: "Component is required",
|
|
636
|
+
}),
|
|
637
|
+
governance: z.array(componentGovernanceRecordSchema),
|
|
638
|
+
})
|
|
639
|
+
.strict() as unknown as z.ZodType<FragmentDefinitionV3<unknown>>;
|
package/src/schemas/index.ts
CHANGED
|
@@ -347,7 +347,7 @@ export const agentErrorEnvelopeSchema = z.object({
|
|
|
347
347
|
}),
|
|
348
348
|
provenance: agentRunProvenanceSchema.optional(),
|
|
349
349
|
verdict: agentRunVerdictSchema.optional(),
|
|
350
|
-
cloudReport: z.enum(["reported", "failed", "skipped"]).optional(),
|
|
350
|
+
cloudReport: z.enum(["accepted", "reported", "failed", "skipped"]).optional(),
|
|
351
351
|
notices: z.array(z.string()).optional(),
|
|
352
352
|
});
|
|
353
353
|
export type AgentErrorEnvelope = z.infer<typeof agentErrorEnvelopeSchema>;
|