@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
|
@@ -1,173 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
portableRepoPathError
|
|
3
3
|
} from "./chunk-EIYNNS77.js";
|
|
4
|
+
import {
|
|
5
|
+
canonicalPreimage,
|
|
6
|
+
contractHash,
|
|
7
|
+
sha256Hex
|
|
8
|
+
} from "./chunk-PWIJMOI4.js";
|
|
4
9
|
|
|
5
10
|
// src/registry.ts
|
|
6
11
|
import { z } from "zod";
|
|
7
|
-
|
|
8
|
-
// src/contract/hash.ts
|
|
9
|
-
function normalizeNumber(value) {
|
|
10
|
-
if (Number.isNaN(value)) return '"@num:nan"';
|
|
11
|
-
if (value === Infinity) return '"@num:+inf"';
|
|
12
|
-
if (value === -Infinity) return '"@num:-inf"';
|
|
13
|
-
if (Object.is(value, -0)) return "0";
|
|
14
|
-
return JSON.stringify(value);
|
|
15
|
-
}
|
|
16
|
-
function canonicalPreimage(value) {
|
|
17
|
-
if (value === void 0 || value === null) return "null";
|
|
18
|
-
const type = typeof value;
|
|
19
|
-
if (type === "number") return normalizeNumber(value);
|
|
20
|
-
if (type === "string" || type === "boolean") return JSON.stringify(value);
|
|
21
|
-
if (type === "bigint") return JSON.stringify(value.toString());
|
|
22
|
-
if (Array.isArray(value)) {
|
|
23
|
-
return `[${value.map(canonicalPreimage).join(",")}]`;
|
|
24
|
-
}
|
|
25
|
-
if (type !== "object") {
|
|
26
|
-
return "null";
|
|
27
|
-
}
|
|
28
|
-
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
29
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalPreimage(v)}`).join(",")}}`;
|
|
30
|
-
}
|
|
31
|
-
var K = new Uint32Array([
|
|
32
|
-
1116352408,
|
|
33
|
-
1899447441,
|
|
34
|
-
3049323471,
|
|
35
|
-
3921009573,
|
|
36
|
-
961987163,
|
|
37
|
-
1508970993,
|
|
38
|
-
2453635748,
|
|
39
|
-
2870763221,
|
|
40
|
-
3624381080,
|
|
41
|
-
310598401,
|
|
42
|
-
607225278,
|
|
43
|
-
1426881987,
|
|
44
|
-
1925078388,
|
|
45
|
-
2162078206,
|
|
46
|
-
2614888103,
|
|
47
|
-
3248222580,
|
|
48
|
-
3835390401,
|
|
49
|
-
4022224774,
|
|
50
|
-
264347078,
|
|
51
|
-
604807628,
|
|
52
|
-
770255983,
|
|
53
|
-
1249150122,
|
|
54
|
-
1555081692,
|
|
55
|
-
1996064986,
|
|
56
|
-
2554220882,
|
|
57
|
-
2821834349,
|
|
58
|
-
2952996808,
|
|
59
|
-
3210313671,
|
|
60
|
-
3336571891,
|
|
61
|
-
3584528711,
|
|
62
|
-
113926993,
|
|
63
|
-
338241895,
|
|
64
|
-
666307205,
|
|
65
|
-
773529912,
|
|
66
|
-
1294757372,
|
|
67
|
-
1396182291,
|
|
68
|
-
1695183700,
|
|
69
|
-
1986661051,
|
|
70
|
-
2177026350,
|
|
71
|
-
2456956037,
|
|
72
|
-
2730485921,
|
|
73
|
-
2820302411,
|
|
74
|
-
3259730800,
|
|
75
|
-
3345764771,
|
|
76
|
-
3516065817,
|
|
77
|
-
3600352804,
|
|
78
|
-
4094571909,
|
|
79
|
-
275423344,
|
|
80
|
-
430227734,
|
|
81
|
-
506948616,
|
|
82
|
-
659060556,
|
|
83
|
-
883997877,
|
|
84
|
-
958139571,
|
|
85
|
-
1322822218,
|
|
86
|
-
1537002063,
|
|
87
|
-
1747873779,
|
|
88
|
-
1955562222,
|
|
89
|
-
2024104815,
|
|
90
|
-
2227730452,
|
|
91
|
-
2361852424,
|
|
92
|
-
2428436474,
|
|
93
|
-
2756734187,
|
|
94
|
-
3204031479,
|
|
95
|
-
3329325298
|
|
96
|
-
]);
|
|
97
|
-
function rotr(value, bits) {
|
|
98
|
-
return (value >>> bits | value << 32 - bits) >>> 0;
|
|
99
|
-
}
|
|
100
|
-
function sha256Hex(message) {
|
|
101
|
-
const bytes = new TextEncoder().encode(message);
|
|
102
|
-
const length = bytes.length;
|
|
103
|
-
const bitLength = length * 8;
|
|
104
|
-
const withMarker = length + 1;
|
|
105
|
-
const zeroPad = (56 - withMarker % 64 + 64) % 64;
|
|
106
|
-
const total = withMarker + zeroPad + 8;
|
|
107
|
-
const buffer = new Uint8Array(total);
|
|
108
|
-
buffer.set(bytes, 0);
|
|
109
|
-
buffer[length] = 128;
|
|
110
|
-
const view = new DataView(buffer.buffer);
|
|
111
|
-
view.setUint32(total - 8, Math.floor(bitLength / 4294967296));
|
|
112
|
-
view.setUint32(total - 4, bitLength >>> 0);
|
|
113
|
-
let h0 = 1779033703;
|
|
114
|
-
let h1 = 3144134277;
|
|
115
|
-
let h2 = 1013904242;
|
|
116
|
-
let h3 = 2773480762;
|
|
117
|
-
let h4 = 1359893119;
|
|
118
|
-
let h5 = 2600822924;
|
|
119
|
-
let h6 = 528734635;
|
|
120
|
-
let h7 = 1541459225;
|
|
121
|
-
const w = new Uint32Array(64);
|
|
122
|
-
for (let chunk = 0; chunk < total; chunk += 64) {
|
|
123
|
-
for (let t = 0; t < 16; t++) w[t] = view.getUint32(chunk + t * 4);
|
|
124
|
-
for (let t = 16; t < 64; t++) {
|
|
125
|
-
const w15 = w[t - 15];
|
|
126
|
-
const w2 = w[t - 2];
|
|
127
|
-
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
|
|
128
|
-
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
|
|
129
|
-
w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
|
|
130
|
-
}
|
|
131
|
-
let a = h0;
|
|
132
|
-
let b = h1;
|
|
133
|
-
let c = h2;
|
|
134
|
-
let d = h3;
|
|
135
|
-
let e = h4;
|
|
136
|
-
let f = h5;
|
|
137
|
-
let g = h6;
|
|
138
|
-
let h = h7;
|
|
139
|
-
for (let t = 0; t < 64; t++) {
|
|
140
|
-
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
141
|
-
const ch = e & f ^ ~e & g;
|
|
142
|
-
const temp1 = h + s1 + ch + K[t] + w[t] >>> 0;
|
|
143
|
-
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
144
|
-
const maj = a & b ^ a & c ^ b & c;
|
|
145
|
-
const temp2 = s0 + maj >>> 0;
|
|
146
|
-
h = g;
|
|
147
|
-
g = f;
|
|
148
|
-
f = e;
|
|
149
|
-
e = d + temp1 >>> 0;
|
|
150
|
-
d = c;
|
|
151
|
-
c = b;
|
|
152
|
-
b = a;
|
|
153
|
-
a = temp1 + temp2 >>> 0;
|
|
154
|
-
}
|
|
155
|
-
h0 = h0 + a >>> 0;
|
|
156
|
-
h1 = h1 + b >>> 0;
|
|
157
|
-
h2 = h2 + c >>> 0;
|
|
158
|
-
h3 = h3 + d >>> 0;
|
|
159
|
-
h4 = h4 + e >>> 0;
|
|
160
|
-
h5 = h5 + f >>> 0;
|
|
161
|
-
h6 = h6 + g >>> 0;
|
|
162
|
-
h7 = h7 + h >>> 0;
|
|
163
|
-
}
|
|
164
|
-
return [h0, h1, h2, h3, h4, h5, h6, h7].map((value) => value.toString(16).padStart(8, "0")).join("");
|
|
165
|
-
}
|
|
166
|
-
function contractHash(body) {
|
|
167
|
-
return sha256Hex(canonicalPreimage(body));
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// src/registry.ts
|
|
171
12
|
var REGISTRY_MANIFEST_SCHEMA_VERSION = "fragments.registry.v1";
|
|
172
13
|
var REGISTRY_ARTIFACT_SCHEMA_VERSION = "fragments.registry-artifact.v1";
|
|
173
14
|
var REGISTRY_INSTALL_RECEIPT_SCHEMA_VERSION = "fragments.registry-lock.v1";
|
|
@@ -289,6 +130,8 @@ var registryManifestBaseSchema = z.object({
|
|
|
289
130
|
generatedAt: z.string().datetime(),
|
|
290
131
|
source: registrySourceSchema,
|
|
291
132
|
packageFiles: z.array(registryFileSchema).default([]),
|
|
133
|
+
/** Files every install writes, whatever the selection: the stylesheet entry and its closure. */
|
|
134
|
+
baseFiles: z.array(registryFileSchema).default([]),
|
|
292
135
|
entrypoints: z.array(registryEntrypointSchema).default([]),
|
|
293
136
|
components: z.record(z.string(), registryComponentSchema),
|
|
294
137
|
dependencies: z.array(registryDependencySchema).default([]),
|
|
@@ -315,6 +158,7 @@ var registryArtifactSchema = z.object({
|
|
|
315
158
|
}
|
|
316
159
|
const manifestFiles = [
|
|
317
160
|
...artifact.manifest.packageFiles,
|
|
161
|
+
...artifact.manifest.baseFiles,
|
|
318
162
|
...artifact.manifest.entrypoints.map((entrypoint) => ({
|
|
319
163
|
path: entrypoint.sourcePath,
|
|
320
164
|
role: "barrel",
|
|
@@ -456,11 +300,176 @@ function registryArtifactDigest(artifact) {
|
|
|
456
300
|
function registryManifestCanonicalPreimage(manifest) {
|
|
457
301
|
return canonicalPreimage(registryHashPayload(manifest));
|
|
458
302
|
}
|
|
303
|
+
var REGISTRY_INDEX_SCHEMA_VERSION = "fragments.registry-index.v2";
|
|
304
|
+
var REGISTRY_SHARD_SCHEMA_VERSION = "fragments.registry-shard.v2";
|
|
305
|
+
var registryShardPathSchema = registryFilePathSchema.superRefine((value, ctx) => {
|
|
306
|
+
if (!value.endsWith(".json")) {
|
|
307
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Shard path must end with .json" });
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
var registryShardSchema = z.object({
|
|
311
|
+
schemaVersion: z.literal(REGISTRY_SHARD_SCHEMA_VERSION),
|
|
312
|
+
files: z.record(z.string().min(1), registryFileContentSchema)
|
|
313
|
+
}).superRefine((shard, ctx) => {
|
|
314
|
+
for (const [contentRef, file] of Object.entries(shard.files)) {
|
|
315
|
+
if (contentRef !== `sha256:${file.sha256}`) {
|
|
316
|
+
ctx.addIssue({
|
|
317
|
+
code: z.ZodIssueCode.custom,
|
|
318
|
+
path: ["files", contentRef],
|
|
319
|
+
message: `Shard key ${contentRef} does not match content hash of ${file.path}`
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
var registryIndexSchema = z.object({
|
|
325
|
+
schemaVersion: z.literal(REGISTRY_INDEX_SCHEMA_VERSION),
|
|
326
|
+
manifest: registryManifestSchema,
|
|
327
|
+
/** Shard path (relative to the index) → registry file paths whose bodies it carries. */
|
|
328
|
+
shards: z.record(registryShardPathSchema, z.array(registryFilePathSchema).min(1))
|
|
329
|
+
}).superRefine((index, ctx) => {
|
|
330
|
+
const owner = /* @__PURE__ */ new Map();
|
|
331
|
+
for (const [shardPath, paths] of Object.entries(index.shards)) {
|
|
332
|
+
for (const path of paths) {
|
|
333
|
+
const existing = owner.get(path);
|
|
334
|
+
if (existing) {
|
|
335
|
+
ctx.addIssue({
|
|
336
|
+
code: z.ZodIssueCode.custom,
|
|
337
|
+
path: ["shards", shardPath],
|
|
338
|
+
message: `Registry file ${path} is listed in both ${existing} and ${shardPath}`
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
owner.set(path, shardPath);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const path of registryManifestFilePaths(index.manifest)) {
|
|
345
|
+
if (!owner.has(path)) {
|
|
346
|
+
ctx.addIssue({
|
|
347
|
+
code: z.ZodIssueCode.custom,
|
|
348
|
+
path: ["shards"],
|
|
349
|
+
message: `Registry file ${path} is not carried by any shard`
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
var REGISTRY_POINTER_SCHEMA_VERSION = "fragments.registry-pointer.v1";
|
|
355
|
+
var registryPointerSchema = z.object({
|
|
356
|
+
schemaVersion: z.literal(REGISTRY_POINTER_SCHEMA_VERSION),
|
|
357
|
+
registryId: z.string().min(1),
|
|
358
|
+
version: z.string().min(1),
|
|
359
|
+
registryHash: sha256Schema,
|
|
360
|
+
index: registryShardPathSchema,
|
|
361
|
+
generatedAt: z.string().datetime()
|
|
362
|
+
});
|
|
363
|
+
function buildRegistryPointer(index, indexPath) {
|
|
364
|
+
return registryPointerSchema.parse({
|
|
365
|
+
schemaVersion: REGISTRY_POINTER_SCHEMA_VERSION,
|
|
366
|
+
registryId: index.manifest.registryId,
|
|
367
|
+
version: index.manifest.version,
|
|
368
|
+
registryHash: index.manifest.registryHash,
|
|
369
|
+
index: indexPath,
|
|
370
|
+
generatedAt: index.manifest.generatedAt
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
function registryManifestFilePaths(manifest) {
|
|
374
|
+
return [...registryManifestFileMetadata(manifest).keys()].sort(
|
|
375
|
+
(left, right) => left.localeCompare(right)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
function registryManifestFileMetadata(manifest) {
|
|
379
|
+
const files = /* @__PURE__ */ new Map();
|
|
380
|
+
const add = (file) => {
|
|
381
|
+
if (!files.get(file.path)) files.set(file.path, file);
|
|
382
|
+
};
|
|
383
|
+
manifest.packageFiles.forEach(add);
|
|
384
|
+
manifest.baseFiles.forEach(add);
|
|
385
|
+
for (const component of Object.values(manifest.components)) component.files.forEach(add);
|
|
386
|
+
for (const entrypoint of manifest.entrypoints) {
|
|
387
|
+
if (!files.has(entrypoint.sourcePath)) files.set(entrypoint.sourcePath, null);
|
|
388
|
+
}
|
|
389
|
+
return files;
|
|
390
|
+
}
|
|
391
|
+
function defaultRegistryShardPath(path) {
|
|
392
|
+
const componentMatch = /^src\/components\/([^/]+)\//.exec(path);
|
|
393
|
+
if (componentMatch) return `components/${componentMatch[1]}.json`;
|
|
394
|
+
if (!path.startsWith("src/")) return "shared/package.json";
|
|
395
|
+
const segments = path.slice("src/".length).split("/");
|
|
396
|
+
return segments.length > 1 ? `shared/${segments[0]}.json` : "shared/src.json";
|
|
397
|
+
}
|
|
398
|
+
function shardKeyFor(metadata, sha256) {
|
|
399
|
+
return metadata?.contentRef ?? `sha256:${sha256}`;
|
|
400
|
+
}
|
|
401
|
+
function shardRegistryArtifact(artifact, shardPathFor = defaultRegistryShardPath) {
|
|
402
|
+
const metadata = registryManifestFileMetadata(artifact.manifest);
|
|
403
|
+
const shards = /* @__PURE__ */ new Map();
|
|
404
|
+
const shardPaths = {};
|
|
405
|
+
for (const [path, content] of Object.entries(artifact.files).sort(
|
|
406
|
+
([left], [right]) => left.localeCompare(right)
|
|
407
|
+
)) {
|
|
408
|
+
const shardPath = shardPathFor(path);
|
|
409
|
+
const shard = shards.get(shardPath) ?? {
|
|
410
|
+
schemaVersion: REGISTRY_SHARD_SCHEMA_VERSION,
|
|
411
|
+
files: {}
|
|
412
|
+
};
|
|
413
|
+
shard.files[shardKeyFor(metadata.get(path), content.sha256)] = content;
|
|
414
|
+
shards.set(shardPath, shard);
|
|
415
|
+
(shardPaths[shardPath] ??= []).push(path);
|
|
416
|
+
}
|
|
417
|
+
const index = registryIndexSchema.parse({
|
|
418
|
+
schemaVersion: REGISTRY_INDEX_SCHEMA_VERSION,
|
|
419
|
+
manifest: artifact.manifest,
|
|
420
|
+
shards: shardPaths
|
|
421
|
+
});
|
|
422
|
+
for (const [path, shard] of shards) shards.set(path, registryShardSchema.parse(shard));
|
|
423
|
+
return { index, shards };
|
|
424
|
+
}
|
|
425
|
+
function shardOwnerMap(index) {
|
|
426
|
+
const owner = /* @__PURE__ */ new Map();
|
|
427
|
+
for (const [shardPath, paths] of Object.entries(index.shards)) {
|
|
428
|
+
for (const path of paths) owner.set(path, shardPath);
|
|
429
|
+
}
|
|
430
|
+
return owner;
|
|
431
|
+
}
|
|
432
|
+
function registryShardsForPaths(index, paths) {
|
|
433
|
+
const owner = shardOwnerMap(index);
|
|
434
|
+
const shards = /* @__PURE__ */ new Set();
|
|
435
|
+
for (const path of paths) {
|
|
436
|
+
const shardPath = owner.get(path);
|
|
437
|
+
if (!shardPath) throw new Error(`Registry index has no shard for ${path}`);
|
|
438
|
+
shards.add(shardPath);
|
|
439
|
+
}
|
|
440
|
+
return [...shards].sort((left, right) => left.localeCompare(right));
|
|
441
|
+
}
|
|
442
|
+
function assembleRegistryArtifact(args) {
|
|
443
|
+
const metadata = registryManifestFileMetadata(args.index.manifest);
|
|
444
|
+
const owner = shardOwnerMap(args.index);
|
|
445
|
+
const files = {};
|
|
446
|
+
for (const path of [...new Set(args.paths)].sort((left, right) => left.localeCompare(right))) {
|
|
447
|
+
const shardPath = owner.get(path);
|
|
448
|
+
if (!shardPath) throw new Error(`Registry index has no shard for ${path}`);
|
|
449
|
+
const shard = args.shards.get(shardPath);
|
|
450
|
+
if (!shard) throw new Error(`Registry shard ${shardPath} was not loaded (needed for ${path})`);
|
|
451
|
+
const expected = metadata.get(path) ?? null;
|
|
452
|
+
const content = expected ? shard.files[expected.contentRef] : Object.values(shard.files).find((file) => file.path === path);
|
|
453
|
+
if (!content || content.path !== path) {
|
|
454
|
+
throw new Error(`Registry shard ${shardPath} is missing content for ${path}`);
|
|
455
|
+
}
|
|
456
|
+
const actualSha256 = hashRegistryFileContent(content.content);
|
|
457
|
+
const actualSize = registryFileSize(content.content);
|
|
458
|
+
const expectedSha256 = expected?.sha256 ?? content.sha256;
|
|
459
|
+
const expectedSize = expected?.size ?? content.size;
|
|
460
|
+
if (actualSha256 !== expectedSha256 || actualSize !== expectedSize) {
|
|
461
|
+
throw new Error(`Registry shard ${shardPath} content for ${path} does not match the index`);
|
|
462
|
+
}
|
|
463
|
+
files[path] = content;
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
schemaVersion: REGISTRY_ARTIFACT_SCHEMA_VERSION,
|
|
467
|
+
manifest: args.index.manifest,
|
|
468
|
+
files
|
|
469
|
+
};
|
|
470
|
+
}
|
|
459
471
|
|
|
460
472
|
export {
|
|
461
|
-
canonicalPreimage,
|
|
462
|
-
sha256Hex,
|
|
463
|
-
contractHash,
|
|
464
473
|
REGISTRY_MANIFEST_SCHEMA_VERSION,
|
|
465
474
|
REGISTRY_ARTIFACT_SCHEMA_VERSION,
|
|
466
475
|
REGISTRY_INSTALL_RECEIPT_SCHEMA_VERSION,
|
|
@@ -488,6 +497,19 @@ export {
|
|
|
488
497
|
buildRegistryArtifact,
|
|
489
498
|
assertValidRegistryArtifact,
|
|
490
499
|
registryArtifactDigest,
|
|
491
|
-
registryManifestCanonicalPreimage
|
|
500
|
+
registryManifestCanonicalPreimage,
|
|
501
|
+
REGISTRY_INDEX_SCHEMA_VERSION,
|
|
502
|
+
REGISTRY_SHARD_SCHEMA_VERSION,
|
|
503
|
+
registryShardSchema,
|
|
504
|
+
registryIndexSchema,
|
|
505
|
+
REGISTRY_POINTER_SCHEMA_VERSION,
|
|
506
|
+
registryPointerSchema,
|
|
507
|
+
buildRegistryPointer,
|
|
508
|
+
registryManifestFilePaths,
|
|
509
|
+
registryManifestFileMetadata,
|
|
510
|
+
defaultRegistryShardPath,
|
|
511
|
+
shardRegistryArtifact,
|
|
512
|
+
registryShardsForPaths,
|
|
513
|
+
assembleRegistryArtifact
|
|
492
514
|
};
|
|
493
|
-
//# sourceMappingURL=chunk-
|
|
515
|
+
//# sourceMappingURL=chunk-ML5S6QNU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { portableRepoPathError } from \"./config-paths.js\";\nimport { canonicalPreimage, contractHash, sha256Hex } from \"./contract/hash.js\";\n\nexport const REGISTRY_MANIFEST_SCHEMA_VERSION = \"fragments.registry.v1\";\nexport const REGISTRY_ARTIFACT_SCHEMA_VERSION = \"fragments.registry-artifact.v1\";\nexport const REGISTRY_INSTALL_RECEIPT_SCHEMA_VERSION = \"fragments.registry-lock.v1\";\n\nconst sha256Schema = z.string().regex(/^[0-9a-f]{64}$/);\n\nconst registryPathSchema = z\n .string()\n .min(1)\n .superRefine((value, ctx) => {\n const error = portableRepoPathError(value);\n if (error) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: error });\n }\n });\n\nconst registryFilePathSchema = registryPathSchema.superRefine((value, ctx) => {\n if (value === \".\") {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"File path must point to a file, not the package root\",\n });\n }\n});\n\nexport const registryDependencyTypeSchema = z.enum([\"runtime\", \"peer\", \"dev\", \"optional\"]);\n\nexport const registryDependencySchema = z.object({\n name: z.string().min(1),\n versionRange: z.string().min(1),\n type: registryDependencyTypeSchema,\n optional: z.boolean().optional(),\n});\n\nexport const registryFileRoleSchema = z.enum([\n \"source\",\n \"style\",\n \"token\",\n \"contract\",\n \"test\",\n \"story\",\n \"example\",\n \"asset\",\n \"barrel\",\n \"metadata\",\n]);\n\nexport const registryFileSchema = z.object({\n path: registryFilePathSchema,\n role: registryFileRoleSchema,\n sha256: sha256Schema,\n size: z.number().int().nonnegative(),\n contentType: z.string().min(1),\n contentRef: z.string().min(1),\n});\n\nexport const registryFileContentSchema = z.object({\n path: registryFilePathSchema,\n content: z.string(),\n sha256: sha256Schema,\n size: z.number().int().nonnegative(),\n contentType: z.string().min(1),\n});\n\nexport const registrySourceSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"package\"),\n packageName: z.string().min(1),\n version: z.string().optional(),\n rootPath: registryPathSchema.optional(),\n commitSha: z.string().optional(),\n }),\n z.object({\n kind: z.literal(\"github\"),\n repoFullName: z.string().min(1),\n commitSha: z.string().min(1),\n path: registryPathSchema.optional(),\n }),\n z.object({\n kind: z.literal(\"local\"),\n rootPath: registryPathSchema.optional(),\n commitSha: z.string().optional(),\n }),\n z.object({\n kind: z.literal(\"manual\"),\n label: z.string().min(1),\n }),\n]);\n\nexport const registryComponentExportSchema = z.object({\n name: z.string().min(1),\n kind: z.enum([\"component\", \"hook\", \"utility\", \"type\", \"style\"]).default(\"component\"),\n path: registryFilePathSchema.optional(),\n});\n\nexport const registryComponentSchema = z.object({\n componentId: z.string().min(1),\n publicRef: z.string().min(1),\n name: z.string().min(1),\n status: z.string().min(1).default(\"stable\"),\n tier: z.enum([\"core\", \"composition\", \"utility\", \"custom\"]).default(\"core\"),\n category: z.string().optional(),\n files: z.array(registryFileSchema).min(1),\n styleFiles: z.array(registryFilePathSchema).default([]),\n tokenDependencies: z.array(z.string().min(1)).default([]),\n runtimeDependencies: z.array(registryDependencySchema).default([]),\n peerDependencies: z.array(registryDependencySchema).default([]),\n internalDependencies: z.array(z.string().min(1)).default([]),\n exports: z.array(registryComponentExportSchema).default([]),\n contract: z.unknown().optional(),\n examples: z.array(z.unknown()).default([]),\n provenance: z.record(z.unknown()).default({}),\n});\n\nexport const registryEntrypointSchema = z.object({\n specifier: z.string().min(1),\n sourcePath: registryFilePathSchema,\n});\n\nexport const registryInstallProfileSchema = z.object({\n name: z.string().min(1),\n targetPath: registryPathSchema,\n importPath: z.string().min(1),\n stylePath: registryPathSchema.optional(),\n includeTests: z.boolean().default(false),\n});\n\nconst registryManifestBaseSchema = z.object({\n schemaVersion: z.literal(REGISTRY_MANIFEST_SCHEMA_VERSION),\n registryId: z.string().min(1),\n name: z.string().min(1),\n channel: z.string().min(1),\n version: z.string().min(1),\n contractVersionId: z.string().optional(),\n fcid: sha256Schema.optional(),\n generatedAt: z.string().datetime(),\n source: registrySourceSchema,\n packageFiles: z.array(registryFileSchema).default([]),\n /** Files every install writes, whatever the selection: the stylesheet entry and its closure. */\n baseFiles: z.array(registryFileSchema).default([]),\n entrypoints: z.array(registryEntrypointSchema).default([]),\n components: z.record(z.string(), registryComponentSchema),\n dependencies: z.array(registryDependencySchema).default([]),\n installProfiles: z.array(registryInstallProfileSchema).default([]),\n});\n\nexport const registryManifestDraftSchema = registryManifestBaseSchema;\n\nexport const registryManifestSchema = registryManifestBaseSchema.extend({\n registryHash: sha256Schema,\n});\n\nexport const registryArtifactSchema = z\n .object({\n schemaVersion: z.literal(REGISTRY_ARTIFACT_SCHEMA_VERSION),\n manifest: registryManifestSchema,\n files: z.record(registryFilePathSchema, registryFileContentSchema),\n })\n .superRefine((artifact, ctx) => {\n const filesByPath = artifact.files;\n for (const [path, content] of Object.entries(filesByPath)) {\n if (content.path !== path) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"files\", path, \"path\"],\n message: `Registry file map key ${path} does not match content path ${content.path}`,\n });\n }\n }\n\n const manifestFiles = [\n ...artifact.manifest.packageFiles,\n ...artifact.manifest.baseFiles,\n ...artifact.manifest.entrypoints.map((entrypoint) => ({\n path: entrypoint.sourcePath,\n role: \"barrel\" as const,\n sha256: filesByPath[entrypoint.sourcePath]?.sha256 ?? \"\",\n size: filesByPath[entrypoint.sourcePath]?.size ?? 0,\n contentType: filesByPath[entrypoint.sourcePath]?.contentType ?? \"text/plain\",\n contentRef: `sha256:${filesByPath[entrypoint.sourcePath]?.sha256 ?? \"\"}`,\n })),\n ...Object.values(artifact.manifest.components).flatMap((component) => component.files),\n ];\n\n for (const file of manifestFiles) {\n const content = filesByPath[file.path];\n if (!content) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"files\", file.path],\n message: `Missing content for registry file ${file.path}`,\n });\n continue;\n }\n if (content.sha256 !== file.sha256) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"files\", file.path, \"sha256\"],\n message: `Content hash for ${file.path} does not match manifest`,\n });\n }\n if (content.size !== file.size) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"files\", file.path, \"size\"],\n message: `Content size for ${file.path} does not match manifest`,\n });\n }\n }\n });\n\nexport const registryInstallReceiptFileSchema = z.object({\n path: registryFilePathSchema,\n sha256: sha256Schema,\n sourcePath: registryFilePathSchema,\n componentId: z.string().min(1).optional(),\n});\n\nexport const registryInstallReceiptComponentSchema = z.object({\n componentId: z.string().min(1),\n publicRef: z.string().min(1),\n name: z.string().min(1),\n files: z.array(registryFilePathSchema),\n});\n\nexport const registryInstallReceiptSchema = z.object({\n schemaVersion: z.literal(REGISTRY_INSTALL_RECEIPT_SCHEMA_VERSION),\n registryId: z.string().min(1),\n registryVersion: z.string().min(1),\n registryHash: sha256Schema,\n fcid: sha256Schema.optional(),\n channel: z.string().min(1).optional(),\n targetPath: registryPathSchema,\n importPath: z.string().min(1),\n installedComponents: z.array(registryInstallReceiptComponentSchema),\n files: z.array(registryInstallReceiptFileSchema),\n dependencies: z.array(registryDependencySchema).default([]),\n installedAt: z.string().datetime(),\n});\n\nexport type RegistryDependencyType = z.infer<typeof registryDependencyTypeSchema>;\nexport type RegistryDependency = z.infer<typeof registryDependencySchema>;\nexport type RegistryFileRole = z.infer<typeof registryFileRoleSchema>;\nexport type RegistryFile = z.infer<typeof registryFileSchema>;\nexport type RegistryFileContent = z.infer<typeof registryFileContentSchema>;\nexport type RegistrySource = z.infer<typeof registrySourceSchema>;\nexport type RegistryComponentExport = z.infer<typeof registryComponentExportSchema>;\nexport type RegistryComponent = z.infer<typeof registryComponentSchema>;\nexport type RegistryEntrypoint = z.infer<typeof registryEntrypointSchema>;\nexport type RegistryInstallProfile = z.infer<typeof registryInstallProfileSchema>;\nexport type RegistryManifestDraft = z.input<typeof registryManifestDraftSchema>;\nexport type RegistryManifest = z.infer<typeof registryManifestSchema>;\nexport type RegistryArtifact = z.infer<typeof registryArtifactSchema>;\nexport type RegistryInstallReceiptFile = z.infer<typeof registryInstallReceiptFileSchema>;\nexport type RegistryInstallReceiptComponent = z.infer<typeof registryInstallReceiptComponentSchema>;\nexport type RegistryInstallReceipt = z.infer<typeof registryInstallReceiptSchema>;\n\nexport interface RegistryFileInput {\n path: string;\n role: RegistryFileRole;\n content: string;\n contentType: string;\n contentRef?: string;\n}\n\nexport interface RegistryArtifactInput {\n manifest: RegistryManifestDraft;\n files: readonly RegistryFileContent[];\n}\n\nfunction registryHashPayload(manifest: RegistryManifestDraft | RegistryManifest): unknown {\n const { generatedAt: _generatedAt, ...withoutGeneratedAt } = manifest;\n if (\"registryHash\" in withoutGeneratedAt) {\n const { registryHash: _registryHash, ...withoutHash } = withoutGeneratedAt;\n return withoutHash;\n }\n return withoutGeneratedAt;\n}\n\nexport function computeRegistryHash(manifest: RegistryManifestDraft | RegistryManifest): string {\n return contractHash(registryHashPayload(manifest));\n}\n\nexport function finalizeRegistryManifest(input: RegistryManifestDraft): RegistryManifest {\n const draft = registryManifestDraftSchema.parse(input);\n return registryManifestSchema.parse({\n ...draft,\n registryHash: computeRegistryHash(draft),\n });\n}\n\nexport function hashRegistryFileContent(content: string): string {\n return sha256Hex(content);\n}\n\nexport function registryFileSize(content: string): number {\n return new TextEncoder().encode(content).length;\n}\n\nexport function buildRegistryFile(input: RegistryFileInput): {\n metadata: RegistryFile;\n content: RegistryFileContent;\n} {\n const sha256 = hashRegistryFileContent(input.content);\n const size = registryFileSize(input.content);\n const path = registryFilePathSchema.parse(input.path);\n const contentType = z.string().min(1).parse(input.contentType);\n return {\n metadata: registryFileSchema.parse({\n path,\n role: input.role,\n sha256,\n size,\n contentType,\n contentRef: input.contentRef ?? `sha256:${sha256}`,\n }),\n content: registryFileContentSchema.parse({\n path,\n content: input.content,\n sha256,\n size,\n contentType,\n }),\n };\n}\n\nexport function buildRegistryArtifact(input: RegistryArtifactInput): RegistryArtifact {\n const manifest = finalizeRegistryManifest(input.manifest);\n const files = new Map<string, RegistryFileContent>();\n for (const file of input.files) {\n const content = registryFileContentSchema.parse(file);\n if (files.has(content.path)) {\n throw new Error(`Duplicate registry file content for ${content.path}`);\n }\n files.set(content.path, content);\n }\n\n return registryArtifactSchema.parse({\n schemaVersion: REGISTRY_ARTIFACT_SCHEMA_VERSION,\n manifest,\n files: Object.fromEntries(files),\n });\n}\n\nexport function assertValidRegistryArtifact(artifact: unknown): RegistryArtifact {\n return registryArtifactSchema.parse(artifact);\n}\n\nexport function registryArtifactDigest(artifact: RegistryArtifact): string {\n const files = Object.fromEntries(\n Object.entries(artifact.files)\n .map(([path, file]) => [path, { sha256: file.sha256, size: file.size }] as const)\n .sort(([leftPath], [rightPath]) => leftPath.localeCompare(rightPath))\n );\n\n return contractHash({\n schemaVersion: artifact.schemaVersion,\n manifest: registryHashPayload(artifact.manifest),\n files,\n });\n}\n\nexport function registryManifestCanonicalPreimage(\n manifest: RegistryManifestDraft | RegistryManifest\n): string {\n return canonicalPreimage(registryHashPayload(manifest));\n}\n\n// ============================================================================\n// Sharded artifact (schema v2): index without bodies + content shards\n// ============================================================================\n\nexport const REGISTRY_INDEX_SCHEMA_VERSION = \"fragments.registry-index.v2\";\nexport const REGISTRY_SHARD_SCHEMA_VERSION = \"fragments.registry-shard.v2\";\n\nconst registryShardPathSchema = registryFilePathSchema.superRefine((value, ctx) => {\n if (!value.endsWith(\".json\")) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: \"Shard path must end with .json\" });\n }\n});\n\nexport const registryShardSchema = z\n .object({\n schemaVersion: z.literal(REGISTRY_SHARD_SCHEMA_VERSION),\n files: z.record(z.string().min(1), registryFileContentSchema),\n })\n .superRefine((shard, ctx) => {\n for (const [contentRef, file] of Object.entries(shard.files)) {\n if (contentRef !== `sha256:${file.sha256}`) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"files\", contentRef],\n message: `Shard key ${contentRef} does not match content hash of ${file.path}`,\n });\n }\n }\n });\n\nexport const registryIndexSchema = z\n .object({\n schemaVersion: z.literal(REGISTRY_INDEX_SCHEMA_VERSION),\n manifest: registryManifestSchema,\n /** Shard path (relative to the index) → registry file paths whose bodies it carries. */\n shards: z.record(registryShardPathSchema, z.array(registryFilePathSchema).min(1)),\n })\n .superRefine((index, ctx) => {\n const owner = new Map<string, string>();\n for (const [shardPath, paths] of Object.entries(index.shards)) {\n for (const path of paths) {\n const existing = owner.get(path);\n if (existing) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"shards\", shardPath],\n message: `Registry file ${path} is listed in both ${existing} and ${shardPath}`,\n });\n }\n owner.set(path, shardPath);\n }\n }\n for (const path of registryManifestFilePaths(index.manifest)) {\n if (!owner.has(path)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"shards\"],\n message: `Registry file ${path} is not carried by any shard`,\n });\n }\n }\n });\n\n/**\n * A short-lived pointer (`latest.json`) to an immutable versioned index. One hop only;\n * `index` is a relative path so the hop cannot leave the pointer's origin.\n */\nexport const REGISTRY_POINTER_SCHEMA_VERSION = \"fragments.registry-pointer.v1\";\n\nexport const registryPointerSchema = z.object({\n schemaVersion: z.literal(REGISTRY_POINTER_SCHEMA_VERSION),\n registryId: z.string().min(1),\n version: z.string().min(1),\n registryHash: sha256Schema,\n index: registryShardPathSchema,\n generatedAt: z.string().datetime(),\n});\n\nexport type RegistryShard = z.infer<typeof registryShardSchema>;\nexport type RegistryIndex = z.infer<typeof registryIndexSchema>;\nexport type RegistryPointer = z.infer<typeof registryPointerSchema>;\n\nexport function buildRegistryPointer(index: RegistryIndex, indexPath: string): RegistryPointer {\n return registryPointerSchema.parse({\n schemaVersion: REGISTRY_POINTER_SCHEMA_VERSION,\n registryId: index.manifest.registryId,\n version: index.manifest.version,\n registryHash: index.manifest.registryHash,\n index: indexPath,\n generatedAt: index.manifest.generatedAt,\n });\n}\n\n/** Every file path the manifest references, sorted and unique. */\nexport function registryManifestFilePaths(manifest: RegistryManifest): string[] {\n return [...registryManifestFileMetadata(manifest).keys()].sort((left, right) =>\n left.localeCompare(right)\n );\n}\n\n/** Path → metadata for every manifest file; entrypoint-only paths map to `null`. */\nexport function registryManifestFileMetadata(\n manifest: RegistryManifest\n): Map<string, RegistryFile | null> {\n const files = new Map<string, RegistryFile | null>();\n const add = (file: RegistryFile) => {\n if (!files.get(file.path)) files.set(file.path, file);\n };\n manifest.packageFiles.forEach(add);\n manifest.baseFiles.forEach(add);\n for (const component of Object.values(manifest.components)) component.files.forEach(add);\n for (const entrypoint of manifest.entrypoints) {\n if (!files.has(entrypoint.sourcePath)) files.set(entrypoint.sourcePath, null);\n }\n return files;\n}\n\n/**\n * Default shard layout: one shard per component directory, one shard per shared\n * top-level directory under `src/` (`shared/tokens.json`, `shared/utils.json`, …),\n * `shared/src.json` for files directly under `src/`, and `shared/package.json` for\n * package-root files (`package.json`, `fragments.json`) that only complete installs need.\n */\nexport function defaultRegistryShardPath(path: string): string {\n const componentMatch = /^src\\/components\\/([^/]+)\\//.exec(path);\n if (componentMatch) return `components/${componentMatch[1]}.json`;\n if (!path.startsWith(\"src/\")) return \"shared/package.json\";\n const segments = path.slice(\"src/\".length).split(\"/\");\n return segments.length > 1 ? `shared/${segments[0]}.json` : \"shared/src.json\";\n}\n\nexport interface ShardedRegistryArtifact {\n index: RegistryIndex;\n shards: Map<string, RegistryShard>;\n}\n\nfunction shardKeyFor(metadata: RegistryFile | null | undefined, sha256: string): string {\n return metadata?.contentRef ?? `sha256:${sha256}`;\n}\n\nexport function shardRegistryArtifact(\n artifact: RegistryArtifact,\n shardPathFor: (path: string) => string = defaultRegistryShardPath\n): ShardedRegistryArtifact {\n const metadata = registryManifestFileMetadata(artifact.manifest);\n const shards = new Map<string, RegistryShard>();\n const shardPaths: Record<string, string[]> = {};\n\n for (const [path, content] of Object.entries(artifact.files).sort(([left], [right]) =>\n left.localeCompare(right)\n )) {\n const shardPath = shardPathFor(path);\n const shard = shards.get(shardPath) ?? {\n schemaVersion: REGISTRY_SHARD_SCHEMA_VERSION,\n files: {},\n };\n shard.files[shardKeyFor(metadata.get(path), content.sha256)] = content;\n shards.set(shardPath, shard);\n (shardPaths[shardPath] ??= []).push(path);\n }\n\n const index = registryIndexSchema.parse({\n schemaVersion: REGISTRY_INDEX_SCHEMA_VERSION,\n manifest: artifact.manifest,\n shards: shardPaths,\n });\n for (const [path, shard] of shards) shards.set(path, registryShardSchema.parse(shard));\n return { index, shards };\n}\n\nfunction shardOwnerMap(index: RegistryIndex): Map<string, string> {\n const owner = new Map<string, string>();\n for (const [shardPath, paths] of Object.entries(index.shards)) {\n for (const path of paths) owner.set(path, shardPath);\n }\n return owner;\n}\n\n/** The shards (sorted, unique) that carry the given registry file paths. */\nexport function registryShardsForPaths(index: RegistryIndex, paths: readonly string[]): string[] {\n const owner = shardOwnerMap(index);\n const shards = new Set<string>();\n for (const path of paths) {\n const shardPath = owner.get(path);\n if (!shardPath) throw new Error(`Registry index has no shard for ${path}`);\n shards.add(shardPath);\n }\n return [...shards].sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Rebuild a v1-shaped artifact from an index plus the shards that cover `paths`.\n * Only the requested paths are materialised; every body is checked against the\n * manifest's hash and size before it is accepted.\n */\nexport function assembleRegistryArtifact(args: {\n index: RegistryIndex;\n shards: ReadonlyMap<string, RegistryShard>;\n paths: readonly string[];\n}): RegistryArtifact {\n const metadata = registryManifestFileMetadata(args.index.manifest);\n const owner = shardOwnerMap(args.index);\n const files: Record<string, RegistryFileContent> = {};\n\n for (const path of [...new Set(args.paths)].sort((left, right) => left.localeCompare(right))) {\n const shardPath = owner.get(path);\n if (!shardPath) throw new Error(`Registry index has no shard for ${path}`);\n const shard = args.shards.get(shardPath);\n if (!shard) throw new Error(`Registry shard ${shardPath} was not loaded (needed for ${path})`);\n const expected = metadata.get(path) ?? null;\n const content = expected\n ? shard.files[expected.contentRef]\n : Object.values(shard.files).find((file) => file.path === path);\n if (!content || content.path !== path) {\n throw new Error(`Registry shard ${shardPath} is missing content for ${path}`);\n }\n // Re-hash the body: a shard's own sha256/size fields are claims, the index is the truth.\n const actualSha256 = hashRegistryFileContent(content.content);\n const actualSize = registryFileSize(content.content);\n const expectedSha256 = expected?.sha256 ?? content.sha256;\n const expectedSize = expected?.size ?? content.size;\n if (actualSha256 !== expectedSha256 || actualSize !== expectedSize) {\n throw new Error(`Registry shard ${shardPath} content for ${path} does not match the index`);\n }\n files[path] = content;\n }\n\n return {\n schemaVersion: REGISTRY_ARTIFACT_SCHEMA_VERSION,\n manifest: args.index.manifest,\n files,\n };\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,SAAS;AAKX,IAAM,mCAAmC;AACzC,IAAM,mCAAmC;AACzC,IAAM,0CAA0C;AAEvD,IAAM,eAAe,EAAE,OAAO,EAAE,MAAM,gBAAgB;AAEtD,IAAM,qBAAqB,EACxB,OAAO,EACP,IAAI,CAAC,EACL,YAAY,CAAC,OAAO,QAAQ;AAC3B,QAAM,QAAQ,sBAAsB,KAAK;AACzC,MAAI,OAAO;AACT,QAAI,SAAS,EAAE,MAAM,EAAE,aAAa,QAAQ,SAAS,MAAM,CAAC;AAAA,EAC9D;AACF,CAAC;AAEH,IAAM,yBAAyB,mBAAmB,YAAY,CAAC,OAAO,QAAQ;AAC5E,MAAI,UAAU,KAAK;AACjB,QAAI,SAAS;AAAA,MACX,MAAM,EAAE,aAAa;AAAA,MACrB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,+BAA+B,EAAE,KAAK,CAAC,WAAW,QAAQ,OAAO,UAAU,CAAC;AAElF,IAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC9B,MAAM;AAAA,EACN,UAAU,EAAE,QAAQ,EAAE,SAAS;AACjC,CAAC;AAEM,IAAM,yBAAyB,EAAE,KAAK;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACnC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAC9B,CAAC;AAEM,IAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,MAAM;AAAA,EACN,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ;AAAA,EACR,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACnC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAC/B,CAAC;AAEM,IAAM,uBAAuB,EAAE,mBAAmB,QAAQ;AAAA,EAC/D,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,QAAQ,SAAS;AAAA,IACzB,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC7B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,UAAU,mBAAmB,SAAS;AAAA,IACtC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,CAAC;AAAA,EACD,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,QAAQ,QAAQ;AAAA,IACxB,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC9B,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC3B,MAAM,mBAAmB,SAAS;AAAA,EACpC,CAAC;AAAA,EACD,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,QAAQ,OAAO;AAAA,IACvB,UAAU,mBAAmB,SAAS;AAAA,IACtC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,CAAC;AAAA,EACD,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,QAAQ,QAAQ;AAAA,IACxB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,CAAC;AACH,CAAC;AAEM,IAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,MAAM,EAAE,KAAK,CAAC,aAAa,QAAQ,WAAW,QAAQ,OAAO,CAAC,EAAE,QAAQ,WAAW;AAAA,EACnF,MAAM,uBAAuB,SAAS;AACxC,CAAC;AAEM,IAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,QAAQ;AAAA,EAC1C,MAAM,EAAE,KAAK,CAAC,QAAQ,eAAe,WAAW,QAAQ,CAAC,EAAE,QAAQ,MAAM;AAAA,EACzE,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,OAAO,EAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EACxC,YAAY,EAAE,MAAM,sBAAsB,EAAE,QAAQ,CAAC,CAAC;AAAA,EACtD,mBAAmB,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACxD,qBAAqB,EAAE,MAAM,wBAAwB,EAAE,QAAQ,CAAC,CAAC;AAAA,EACjE,kBAAkB,EAAE,MAAM,wBAAwB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC9D,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC3D,SAAS,EAAE,MAAM,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC1D,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACzC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAEM,IAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,YAAY;AACd,CAAC;AAEM,IAAM,+BAA+B,EAAE,OAAO;AAAA,EACnD,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,YAAY;AAAA,EACZ,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,WAAW,mBAAmB,SAAS;AAAA,EACvC,cAAc,EAAE,QAAQ,EAAE,QAAQ,KAAK;AACzC,CAAC;AAED,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,eAAe,EAAE,QAAQ,gCAAgC;AAAA,EACzD,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,MAAM,aAAa,SAAS;AAAA,EAC5B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQ;AAAA,EACR,cAAc,EAAE,MAAM,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA,EAEpD,WAAW,EAAE,MAAM,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAAA,EACjD,aAAa,EAAE,MAAM,wBAAwB,EAAE,QAAQ,CAAC,CAAC;AAAA,EACzD,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,uBAAuB;AAAA,EACxD,cAAc,EAAE,MAAM,wBAAwB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC1D,iBAAiB,EAAE,MAAM,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AACnE,CAAC;AAEM,IAAM,8BAA8B;AAEpC,IAAM,yBAAyB,2BAA2B,OAAO;AAAA,EACtE,cAAc;AAChB,CAAC;AAEM,IAAM,yBAAyB,EACnC,OAAO;AAAA,EACN,eAAe,EAAE,QAAQ,gCAAgC;AAAA,EACzD,UAAU;AAAA,EACV,OAAO,EAAE,OAAO,wBAAwB,yBAAyB;AACnE,CAAC,EACA,YAAY,CAAC,UAAU,QAAQ;AAC9B,QAAM,cAAc,SAAS;AAC7B,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,WAAW,GAAG;AACzD,QAAI,QAAQ,SAAS,MAAM;AACzB,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,SAAS,MAAM,MAAM;AAAA,QAC5B,SAAS,yBAAyB,IAAI,gCAAgC,QAAQ,IAAI;AAAA,MACpF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,gBAAgB;AAAA,IACpB,GAAG,SAAS,SAAS;AAAA,IACrB,GAAG,SAAS,SAAS;AAAA,IACrB,GAAG,SAAS,SAAS,YAAY,IAAI,CAAC,gBAAgB;AAAA,MACpD,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA,MACN,QAAQ,YAAY,WAAW,UAAU,GAAG,UAAU;AAAA,MACtD,MAAM,YAAY,WAAW,UAAU,GAAG,QAAQ;AAAA,MAClD,aAAa,YAAY,WAAW,UAAU,GAAG,eAAe;AAAA,MAChE,YAAY,UAAU,YAAY,WAAW,UAAU,GAAG,UAAU,EAAE;AAAA,IACxE,EAAE;AAAA,IACF,GAAG,OAAO,OAAO,SAAS,SAAS,UAAU,EAAE,QAAQ,CAAC,cAAc,UAAU,KAAK;AAAA,EACvF;AAEA,aAAW,QAAQ,eAAe;AAChC,UAAM,UAAU,YAAY,KAAK,IAAI;AACrC,QAAI,CAAC,SAAS;AACZ,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,SAAS,KAAK,IAAI;AAAA,QACzB,SAAS,qCAAqC,KAAK,IAAI;AAAA,MACzD,CAAC;AACD;AAAA,IACF;AACA,QAAI,QAAQ,WAAW,KAAK,QAAQ;AAClC,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,SAAS,KAAK,MAAM,QAAQ;AAAA,QACnC,SAAS,oBAAoB,KAAK,IAAI;AAAA,MACxC,CAAC;AAAA,IACH;AACA,QAAI,QAAQ,SAAS,KAAK,MAAM;AAC9B,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,SAAS,KAAK,MAAM,MAAM;AAAA,QACjC,SAAS,oBAAoB,KAAK,IAAI;AAAA,MACxC,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAEI,IAAM,mCAAmC,EAAE,OAAO;AAAA,EACvD,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAC1C,CAAC;AAEM,IAAM,wCAAwC,EAAE,OAAO;AAAA,EAC5D,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,OAAO,EAAE,MAAM,sBAAsB;AACvC,CAAC;AAEM,IAAM,+BAA+B,EAAE,OAAO;AAAA,EACnD,eAAe,EAAE,QAAQ,uCAAuC;AAAA,EAChE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACjC,cAAc;AAAA,EACd,MAAM,aAAa,SAAS;AAAA,EAC5B,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,YAAY;AAAA,EACZ,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,qBAAqB,EAAE,MAAM,qCAAqC;AAAA,EAClE,OAAO,EAAE,MAAM,gCAAgC;AAAA,EAC/C,cAAc,EAAE,MAAM,wBAAwB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC1D,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAgCD,SAAS,oBAAoB,UAA6D;AACxF,QAAM,EAAE,aAAa,cAAc,GAAG,mBAAmB,IAAI;AAC7D,MAAI,kBAAkB,oBAAoB;AACxC,UAAM,EAAE,cAAc,eAAe,GAAG,YAAY,IAAI;AACxD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,UAA4D;AAC9F,SAAO,aAAa,oBAAoB,QAAQ,CAAC;AACnD;AAEO,SAAS,yBAAyB,OAAgD;AACvF,QAAM,QAAQ,4BAA4B,MAAM,KAAK;AACrD,SAAO,uBAAuB,MAAM;AAAA,IAClC,GAAG;AAAA,IACH,cAAc,oBAAoB,KAAK;AAAA,EACzC,CAAC;AACH;AAEO,SAAS,wBAAwB,SAAyB;AAC/D,SAAO,UAAU,OAAO;AAC1B;AAEO,SAAS,iBAAiB,SAAyB;AACxD,SAAO,IAAI,YAAY,EAAE,OAAO,OAAO,EAAE;AAC3C;AAEO,SAAS,kBAAkB,OAGhC;AACA,QAAM,SAAS,wBAAwB,MAAM,OAAO;AACpD,QAAM,OAAO,iBAAiB,MAAM,OAAO;AAC3C,QAAM,OAAO,uBAAuB,MAAM,MAAM,IAAI;AACpD,QAAM,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,MAAM,WAAW;AAC7D,SAAO;AAAA,IACL,UAAU,mBAAmB,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,MAAM;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,MAAM,cAAc,UAAU,MAAM;AAAA,IAClD,CAAC;AAAA,IACD,SAAS,0BAA0B,MAAM;AAAA,MACvC;AAAA,MACA,SAAS,MAAM;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBAAsB,OAAgD;AACpF,QAAM,WAAW,yBAAyB,MAAM,QAAQ;AACxD,QAAM,QAAQ,oBAAI,IAAiC;AACnD,aAAW,QAAQ,MAAM,OAAO;AAC9B,UAAM,UAAU,0BAA0B,MAAM,IAAI;AACpD,QAAI,MAAM,IAAI,QAAQ,IAAI,GAAG;AAC3B,YAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAI,EAAE;AAAA,IACvE;AACA,UAAM,IAAI,QAAQ,MAAM,OAAO;AAAA,EACjC;AAEA,SAAO,uBAAuB,MAAM;AAAA,IAClC,eAAe;AAAA,IACf;AAAA,IACA,OAAO,OAAO,YAAY,KAAK;AAAA,EACjC,CAAC;AACH;AAEO,SAAS,4BAA4B,UAAqC;AAC/E,SAAO,uBAAuB,MAAM,QAAQ;AAC9C;AAEO,SAAS,uBAAuB,UAAoC;AACzE,QAAM,QAAQ,OAAO;AAAA,IACnB,OAAO,QAAQ,SAAS,KAAK,EAC1B,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,QAAQ,KAAK,QAAQ,MAAM,KAAK,KAAK,CAAC,CAAU,EAC/E,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,SAAS,MAAM,SAAS,cAAc,SAAS,CAAC;AAAA,EACxE;AAEA,SAAO,aAAa;AAAA,IAClB,eAAe,SAAS;AAAA,IACxB,UAAU,oBAAoB,SAAS,QAAQ;AAAA,IAC/C;AAAA,EACF,CAAC;AACH;AAEO,SAAS,kCACd,UACQ;AACR,SAAO,kBAAkB,oBAAoB,QAAQ,CAAC;AACxD;AAMO,IAAM,gCAAgC;AACtC,IAAM,gCAAgC;AAE7C,IAAM,0BAA0B,uBAAuB,YAAY,CAAC,OAAO,QAAQ;AACjF,MAAI,CAAC,MAAM,SAAS,OAAO,GAAG;AAC5B,QAAI,SAAS,EAAE,MAAM,EAAE,aAAa,QAAQ,SAAS,iCAAiC,CAAC;AAAA,EACzF;AACF,CAAC;AAEM,IAAM,sBAAsB,EAChC,OAAO;AAAA,EACN,eAAe,EAAE,QAAQ,6BAA6B;AAAA,EACtD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,yBAAyB;AAC9D,CAAC,EACA,YAAY,CAAC,OAAO,QAAQ;AAC3B,aAAW,CAAC,YAAY,IAAI,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AAC5D,QAAI,eAAe,UAAU,KAAK,MAAM,IAAI;AAC1C,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,SAAS,UAAU;AAAA,QAC1B,SAAS,aAAa,UAAU,mCAAmC,KAAK,IAAI;AAAA,MAC9E,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAEI,IAAM,sBAAsB,EAChC,OAAO;AAAA,EACN,eAAe,EAAE,QAAQ,6BAA6B;AAAA,EACtD,UAAU;AAAA;AAAA,EAEV,QAAQ,EAAE,OAAO,yBAAyB,EAAE,MAAM,sBAAsB,EAAE,IAAI,CAAC,CAAC;AAClF,CAAC,EACA,YAAY,CAAC,OAAO,QAAQ;AAC3B,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC7D,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAW,MAAM,IAAI,IAAI;AAC/B,UAAI,UAAU;AACZ,YAAI,SAAS;AAAA,UACX,MAAM,EAAE,aAAa;AAAA,UACrB,MAAM,CAAC,UAAU,SAAS;AAAA,UAC1B,SAAS,iBAAiB,IAAI,sBAAsB,QAAQ,QAAQ,SAAS;AAAA,QAC/E,CAAC;AAAA,MACH;AACA,YAAM,IAAI,MAAM,SAAS;AAAA,IAC3B;AAAA,EACF;AACA,aAAW,QAAQ,0BAA0B,MAAM,QAAQ,GAAG;AAC5D,QAAI,CAAC,MAAM,IAAI,IAAI,GAAG;AACpB,UAAI,SAAS;AAAA,QACX,MAAM,EAAE,aAAa;AAAA,QACrB,MAAM,CAAC,QAAQ;AAAA,QACf,SAAS,iBAAiB,IAAI;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAMI,IAAM,kCAAkC;AAExC,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,eAAe,EAAE,QAAQ,+BAA+B;AAAA,EACxD,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,cAAc;AAAA,EACd,OAAO;AAAA,EACP,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAMM,SAAS,qBAAqB,OAAsB,WAAoC;AAC7F,SAAO,sBAAsB,MAAM;AAAA,IACjC,eAAe;AAAA,IACf,YAAY,MAAM,SAAS;AAAA,IAC3B,SAAS,MAAM,SAAS;AAAA,IACxB,cAAc,MAAM,SAAS;AAAA,IAC7B,OAAO;AAAA,IACP,aAAa,MAAM,SAAS;AAAA,EAC9B,CAAC;AACH;AAGO,SAAS,0BAA0B,UAAsC;AAC9E,SAAO,CAAC,GAAG,6BAA6B,QAAQ,EAAE,KAAK,CAAC,EAAE;AAAA,IAAK,CAAC,MAAM,UACpE,KAAK,cAAc,KAAK;AAAA,EAC1B;AACF;AAGO,SAAS,6BACd,UACkC;AAClC,QAAM,QAAQ,oBAAI,IAAiC;AACnD,QAAM,MAAM,CAAC,SAAuB;AAClC,QAAI,CAAC,MAAM,IAAI,KAAK,IAAI,EAAG,OAAM,IAAI,KAAK,MAAM,IAAI;AAAA,EACtD;AACA,WAAS,aAAa,QAAQ,GAAG;AACjC,WAAS,UAAU,QAAQ,GAAG;AAC9B,aAAW,aAAa,OAAO,OAAO,SAAS,UAAU,EAAG,WAAU,MAAM,QAAQ,GAAG;AACvF,aAAW,cAAc,SAAS,aAAa;AAC7C,QAAI,CAAC,MAAM,IAAI,WAAW,UAAU,EAAG,OAAM,IAAI,WAAW,YAAY,IAAI;AAAA,EAC9E;AACA,SAAO;AACT;AAQO,SAAS,yBAAyB,MAAsB;AAC7D,QAAM,iBAAiB,8BAA8B,KAAK,IAAI;AAC9D,MAAI,eAAgB,QAAO,cAAc,eAAe,CAAC,CAAC;AAC1D,MAAI,CAAC,KAAK,WAAW,MAAM,EAAG,QAAO;AACrC,QAAM,WAAW,KAAK,MAAM,OAAO,MAAM,EAAE,MAAM,GAAG;AACpD,SAAO,SAAS,SAAS,IAAI,UAAU,SAAS,CAAC,CAAC,UAAU;AAC9D;AAOA,SAAS,YAAY,UAA2C,QAAwB;AACtF,SAAO,UAAU,cAAc,UAAU,MAAM;AACjD;AAEO,SAAS,sBACd,UACA,eAAyC,0BAChB;AACzB,QAAM,WAAW,6BAA6B,SAAS,QAAQ;AAC/D,QAAM,SAAS,oBAAI,IAA2B;AAC9C,QAAM,aAAuC,CAAC;AAE9C,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,SAAS,KAAK,EAAE;AAAA,IAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MAC/E,KAAK,cAAc,KAAK;AAAA,EAC1B,GAAG;AACD,UAAM,YAAY,aAAa,IAAI;AACnC,UAAM,QAAQ,OAAO,IAAI,SAAS,KAAK;AAAA,MACrC,eAAe;AAAA,MACf,OAAO,CAAC;AAAA,IACV;AACA,UAAM,MAAM,YAAY,SAAS,IAAI,IAAI,GAAG,QAAQ,MAAM,CAAC,IAAI;AAC/D,WAAO,IAAI,WAAW,KAAK;AAC3B,KAAC,WAAW,SAAS,MAAM,CAAC,GAAG,KAAK,IAAI;AAAA,EAC1C;AAEA,QAAM,QAAQ,oBAAoB,MAAM;AAAA,IACtC,eAAe;AAAA,IACf,UAAU,SAAS;AAAA,IACnB,QAAQ;AAAA,EACV,CAAC;AACD,aAAW,CAAC,MAAM,KAAK,KAAK,OAAQ,QAAO,IAAI,MAAM,oBAAoB,MAAM,KAAK,CAAC;AACrF,SAAO,EAAE,OAAO,OAAO;AACzB;AAEA,SAAS,cAAc,OAA2C;AAChE,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC7D,eAAW,QAAQ,MAAO,OAAM,IAAI,MAAM,SAAS;AAAA,EACrD;AACA,SAAO;AACT;AAGO,SAAS,uBAAuB,OAAsB,OAAoC;AAC/F,QAAM,QAAQ,cAAc,KAAK;AACjC,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,MAAM,IAAI,IAAI;AAChC,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,mCAAmC,IAAI,EAAE;AACzE,WAAO,IAAI,SAAS;AAAA,EACtB;AACA,SAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AACpE;AAOO,SAAS,yBAAyB,MAIpB;AACnB,QAAM,WAAW,6BAA6B,KAAK,MAAM,QAAQ;AACjE,QAAM,QAAQ,cAAc,KAAK,KAAK;AACtC,QAAM,QAA6C,CAAC;AAEpD,aAAW,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC,GAAG;AAC5F,UAAM,YAAY,MAAM,IAAI,IAAI;AAChC,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,mCAAmC,IAAI,EAAE;AACzE,UAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;AACvC,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kBAAkB,SAAS,+BAA+B,IAAI,GAAG;AAC7F,UAAM,WAAW,SAAS,IAAI,IAAI,KAAK;AACvC,UAAM,UAAU,WACZ,MAAM,MAAM,SAAS,UAAU,IAC/B,OAAO,OAAO,MAAM,KAAK,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI;AAChE,QAAI,CAAC,WAAW,QAAQ,SAAS,MAAM;AACrC,YAAM,IAAI,MAAM,kBAAkB,SAAS,2BAA2B,IAAI,EAAE;AAAA,IAC9E;AAEA,UAAM,eAAe,wBAAwB,QAAQ,OAAO;AAC5D,UAAM,aAAa,iBAAiB,QAAQ,OAAO;AACnD,UAAM,iBAAiB,UAAU,UAAU,QAAQ;AACnD,UAAM,eAAe,UAAU,QAAQ,QAAQ;AAC/C,QAAI,iBAAiB,kBAAkB,eAAe,cAAc;AAClE,YAAM,IAAI,MAAM,kBAAkB,SAAS,gBAAgB,IAAI,2BAA2B;AAAA,IAC5F;AACA,UAAM,IAAI,IAAI;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,UAAU,KAAK,MAAM;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
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 `docs/fragments-v1/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":[]}
|