@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.
- package/dist/{chunk-RPSEABY3.js → chunk-BMPYIUZE.js} +23 -33
- package/dist/chunk-BMPYIUZE.js.map +1 -0
- package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
- package/dist/chunk-MZ2FS7U4.js.map +1 -0
- package/dist/codes/index.d.ts +1 -1
- package/dist/codes/index.js +1 -1
- package/dist/compiled-types/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/{governance-BAsy1k2H.d.ts → governance-hOPXGbbs.d.ts} +3 -3
- package/dist/index.d.ts +133 -6026
- package/dist/index.js +179 -806
- package/dist/index.js.map +1 -1
- package/dist/preview-runtime.d.ts +1 -1
- package/dist/react-types.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/storyAdapter.d.ts +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/topology/index.d.ts +1 -1
- package/dist/topology/index.js +1 -1
- package/package.json +1 -1
- package/src/approved-contract-tokens.test.ts +39 -0
- package/src/approved-contract-tokens.ts +18 -0
- package/src/codes/__tests__/codes.test.ts +4 -0
- package/src/codes/codes.ts +10 -0
- package/src/domain-ids.test.ts +9 -26
- package/src/domain-ids.ts +0 -45
- package/src/evaluation/evaluate.ts +17 -7
- package/src/evaluation/evaluation-v2-receipt-v1.test.ts +20 -8
- package/src/evaluation/index.ts +1 -1
- package/src/governance.ts +18 -0
- package/src/index.ts +10 -79
- package/src/topology/resolve-area.ts +1 -1
- package/src/types.ts +0 -3
- package/dist/chunk-3LLRNCPX.js.map +0 -1
- package/dist/chunk-RPSEABY3.js.map +0 -1
- package/src/feature-plan/digest.ts +0 -217
- package/src/feature-plan/feature-plan-v1.test.ts +0 -529
- package/src/feature-plan/index.ts +0 -65
- 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-
|
|
3
|
+
import { V as VariantRenderOptions, b as VariantLoader } from './governance-hOPXGbbs.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import './topology/index.js';
|
|
6
6
|
|
package/dist/react-types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentType, ReactNode, JSX } from 'react';
|
|
2
|
-
export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-
|
|
2
|
+
export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-hOPXGbbs.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import './topology/index.js';
|
|
5
5
|
|
package/dist/schemas/index.js
CHANGED
package/dist/storyAdapter.d.ts
CHANGED
|
@@ -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-
|
|
3
|
+
import { F as FragmentDefinition } from './governance-hOPXGbbs.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import './topology/index.js';
|
|
6
6
|
|
package/dist/test-utils.d.ts
CHANGED
package/dist/topology/index.d.ts
CHANGED
|
@@ -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 `
|
|
9
|
+
* See `docs/fragments-v1/ARCHITECTURE.md §1`.
|
|
10
10
|
*/
|
|
11
11
|
type AreaCriticality = "low" | "medium" | "high" | "revenue" | "regulated";
|
|
12
12
|
interface Area {
|
package/dist/topology/index.js
CHANGED
package/package.json
CHANGED
|
@@ -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.
|
package/src/codes/codes.ts
CHANGED
|
@@ -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(
|
package/src/domain-ids.test.ts
CHANGED
|
@@ -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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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" })
|
package/src/evaluation/index.ts
CHANGED
|
@@ -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 `
|
|
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":";AAkEA,SAAS,aAAa,MAAsB;AAC1C,MAAI,MAAM;AACV,MAAI,aAAa;AACjB,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,UAAM,KAAK,KAAK,CAAC;AAIjB,QACE,OAAO,OACP,KAAK,IAAI,CAAC,MAAM,OAChB,KAAK,IAAI,CAAC,MAAM,QACf,KAAK,IAAI,CAAC,MAAM,UAAa,KAAK,IAAI,CAAC,MAAM,MAC9C;AACA,aAAO;AACP,WAAK;AACL;AAAA,IACF;AAGA,QAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,KAAK;AACrC,aAAO;AACP,WAAK;AACL;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,MACV,KAAK;AACH,eAAO;AACP;AAAA,MACF,KAAK;AACH,eAAO;AACP;AAAA,MACF,KAAK;AACH,sBAAc;AACd,eAAO;AACP;AAAA,MACF,KAAK;AACH,YAAI,aAAa,GAAG;AAClB,wBAAc;AACd,iBAAO;AAAA,QACT,OAAO;AACL,iBAAO;AAAA,QACT;AACA;AAAA,MACF,KAAK;AACH,eAAO,aAAa,IAAI,MAAM;AAC9B;AAAA;AAAA,MAEF,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,SAAK;AAAA,EACP;AACA,SAAO,IAAI,OAAO,IAAI,GAAG,GAAG;AAC9B;AAaA,IAAM,gBAAgB,oBAAI,QAAkC;AAE5D,SAAS,QAAQ,UAAoC;AACnD,QAAM,SAAS,cAAc,IAAI,QAAQ;AACzC,MAAI,OAAQ,QAAO;AAInB,QAAM,UAAU,SAAS,MACtB,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE,EACtC,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,KAAK,EAAE,KAAK,YAAY;AAC9B,UAAM,KAAK,EAAE,KAAK,YAAY;AAC9B,QAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,WAAO,EAAE,QAAQ,EAAE;AAAA,EACrB,CAAC,EACA,IAAI,CAAC,EAAE,KAAK,OAAO;AAAA,IAClB;AAAA,IACA,UAAU,KAAK,MAAM,QAAQ,CAAC,SAAS;AACrC,UAAI;AACF,eAAO,CAAC,EAAE,MAAM,IAAI,aAAa,IAAI,EAAE,CAAC;AAAA,MAC1C,QAAQ;AAKN,YAAI,OAAO,YAAY,aAAa;AAClC,kBAAQ;AAAA,YACN,6CAA6C,KAAK,EAAE,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,UAChF;AAAA,QACF;AACA,eAAO,CAAC;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH,EAAE;AAEJ,gBAAc,IAAI,UAAU,OAAO;AACnC,SAAO;AACT;AAGA,SAAS,cAAc,aAA6B;AAClD,SAAO,YAAY,QAAQ,OAAO,GAAG,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,QAAQ,EAAE;AAChF;AAQO,SAAS,YAAY,aAAqB,UAAsC;AACrF,QAAM,OAAO,cAAc,WAAW;AACtC,aAAW,EAAE,MAAM,SAAS,KAAK,QAAQ,QAAQ,GAAG;AAClD,eAAW,EAAE,MAAM,GAAG,KAAK,UAAU;AACnC,UAAI,GAAG,KAAK,IAAI,GAAG;AACjB,eAAO;AAAA,UACL,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,UACf,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,UACb,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|