@animus-ui/extract 0.1.9 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +48 -1
  2. package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
  3. package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
  4. package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
  5. package/crates/extract-v2/index.d.ts +17 -98
  6. package/dist/index.cjs +78 -2101
  7. package/dist/index.mjs +2 -2026
  8. package/dist/{analyze-project-args.d.ts → pipeline/analyze-project-args.d.ts} +0 -12
  9. package/dist/pipeline/analyze-project-args.d.ts.map +1 -0
  10. package/dist/pipeline/assemble-stylesheet.d.ts +23 -0
  11. package/dist/pipeline/assemble-stylesheet.d.ts.map +1 -0
  12. package/dist/pipeline/asset-placeholders.d.ts +4 -0
  13. package/dist/pipeline/asset-placeholders.d.ts.map +1 -0
  14. package/dist/pipeline/content-hash.d.ts +2 -0
  15. package/dist/pipeline/content-hash.d.ts.map +1 -0
  16. package/dist/pipeline/core-options.d.ts +96 -0
  17. package/dist/pipeline/core-options.d.ts.map +1 -0
  18. package/dist/pipeline/correlate-external-tokens.d.ts +38 -0
  19. package/dist/pipeline/correlate-external-tokens.d.ts.map +1 -0
  20. package/dist/pipeline/discover-files.d.ts +4 -0
  21. package/dist/pipeline/discover-files.d.ts.map +1 -0
  22. package/dist/pipeline/discover-packages.d.ts +55 -0
  23. package/dist/pipeline/discover-packages.d.ts.map +1 -0
  24. package/dist/pipeline/dynamic-prop-config.d.ts +22 -0
  25. package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -0
  26. package/dist/pipeline/engine-adapter.d.ts +63 -0
  27. package/dist/pipeline/engine-adapter.d.ts.map +1 -0
  28. package/dist/pipeline/engine-retirement.d.ts +3 -0
  29. package/dist/pipeline/engine-retirement.d.ts.map +1 -0
  30. package/dist/pipeline/error-diagnostics.d.ts +8 -0
  31. package/dist/pipeline/error-diagnostics.d.ts.map +1 -0
  32. package/dist/{index.d.ts → pipeline/index.d.ts} +16 -6
  33. package/dist/pipeline/index.d.ts.map +1 -0
  34. package/dist/pipeline/internal-wire.d.ts +2 -0
  35. package/dist/pipeline/internal-wire.d.ts.map +1 -0
  36. package/dist/pipeline/manifest-diagnostics.d.ts +57 -0
  37. package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -0
  38. package/dist/pipeline/manifest-schema.d.ts +117 -0
  39. package/dist/pipeline/manifest-schema.d.ts.map +1 -0
  40. package/dist/pipeline/mdx-preprocessor.d.ts +25 -0
  41. package/dist/pipeline/mdx-preprocessor.d.ts.map +1 -0
  42. package/dist/pipeline/path-aliases.d.ts +15 -0
  43. package/dist/pipeline/path-aliases.d.ts.map +1 -0
  44. package/dist/{post-process-css.d.ts → pipeline/post-process-css.d.ts} +0 -8
  45. package/dist/pipeline/post-process-css.d.ts.map +1 -0
  46. package/dist/pipeline/prefix.d.ts +8 -0
  47. package/dist/pipeline/prefix.d.ts.map +1 -0
  48. package/dist/pipeline/replacement-plans.d.ts +22 -0
  49. package/dist/pipeline/replacement-plans.d.ts.map +1 -0
  50. package/dist/pipeline/resolve-asset.d.ts +11 -0
  51. package/dist/pipeline/resolve-asset.d.ts.map +1 -0
  52. package/dist/pipeline/run-analysis.d.ts +62 -0
  53. package/dist/pipeline/run-analysis.d.ts.map +1 -0
  54. package/dist/pipeline/source-corpus.d.ts +25 -0
  55. package/dist/pipeline/source-corpus.d.ts.map +1 -0
  56. package/dist/pipeline/source-identity.d.ts +63 -0
  57. package/dist/pipeline/source-identity.d.ts.map +1 -0
  58. package/dist/pipeline/source-ingestion.d.ts +168 -0
  59. package/dist/pipeline/source-ingestion.d.ts.map +1 -0
  60. package/dist/pipeline/static-css.d.ts +16 -0
  61. package/dist/pipeline/static-css.d.ts.map +1 -0
  62. package/dist/pipeline/structural-self-check.d.ts +12 -0
  63. package/dist/pipeline/structural-self-check.d.ts.map +1 -0
  64. package/dist/pipeline/svelte-source-adapter.d.ts +81 -0
  65. package/dist/pipeline/svelte-source-adapter.d.ts.map +1 -0
  66. package/dist/pipeline/system-config.d.ts +40 -0
  67. package/dist/pipeline/system-config.d.ts.map +1 -0
  68. package/dist/pipeline/system-props-module.d.ts +8 -0
  69. package/dist/pipeline/system-props-module.d.ts.map +1 -0
  70. package/dist/pipeline/timing-waterfall.d.ts +6 -0
  71. package/dist/pipeline/timing-waterfall.d.ts.map +1 -0
  72. package/dist/pipeline/tsconfig-paths.d.ts +7 -0
  73. package/dist/pipeline/tsconfig-paths.d.ts.map +1 -0
  74. package/dist/pipeline/unit-fallback.d.ts +2 -0
  75. package/dist/pipeline/unit-fallback.d.ts.map +1 -0
  76. package/dist/pipeline/utils.d.ts +3 -0
  77. package/dist/pipeline/utils.d.ts.map +1 -0
  78. package/dist/pipeline/watch-keys.d.ts +2 -0
  79. package/dist/pipeline/watch-keys.d.ts.map +1 -0
  80. package/dist/pipeline-DeEENy5C.mjs +2905 -0
  81. package/dist/pipeline-x1yBZ2UP.cjs +3386 -0
  82. package/dist/session/extraction-session.d.ts +240 -0
  83. package/dist/session/extraction-session.d.ts.map +1 -0
  84. package/dist/session/index.d.ts +10 -0
  85. package/dist/session/index.d.ts.map +1 -0
  86. package/dist/session/published-set.d.ts +52 -0
  87. package/dist/session/published-set.d.ts.map +1 -0
  88. package/dist/session/resolve-packages.d.ts +7 -0
  89. package/dist/session/resolve-packages.d.ts.map +1 -0
  90. package/dist/session/session-paths.d.ts +97 -0
  91. package/dist/session/session-paths.d.ts.map +1 -0
  92. package/dist/session/singleton.d.ts +85 -0
  93. package/dist/session/singleton.d.ts.map +1 -0
  94. package/dist/session/timing.d.ts +2 -0
  95. package/dist/session/timing.d.ts.map +1 -0
  96. package/dist/session/turbopack-orchestrator.d.ts +27 -0
  97. package/dist/session/turbopack-orchestrator.d.ts.map +1 -0
  98. package/dist/session.cjs +1999 -0
  99. package/dist/session.mjs +1955 -0
  100. package/index-v2.js +0 -9
  101. package/package.json +22 -9
  102. package/dist/analyze-project-args.d.ts.map +0 -1
  103. package/dist/assemble-stylesheet.d.ts +0 -58
  104. package/dist/assemble-stylesheet.d.ts.map +0 -1
  105. package/dist/asset-placeholders.d.ts +0 -29
  106. package/dist/asset-placeholders.d.ts.map +0 -1
  107. package/dist/content-hash.d.ts +0 -9
  108. package/dist/content-hash.d.ts.map +0 -1
  109. package/dist/correlate-external-tokens.d.ts +0 -46
  110. package/dist/correlate-external-tokens.d.ts.map +0 -1
  111. package/dist/discover-files.d.ts +0 -10
  112. package/dist/discover-files.d.ts.map +0 -1
  113. package/dist/discover-packages.d.ts +0 -192
  114. package/dist/discover-packages.d.ts.map +0 -1
  115. package/dist/dynamic-prop-config.d.ts +0 -37
  116. package/dist/dynamic-prop-config.d.ts.map +0 -1
  117. package/dist/engine-adapter.d.ts +0 -97
  118. package/dist/engine-adapter.d.ts.map +0 -1
  119. package/dist/engine-retirement.d.ts +0 -22
  120. package/dist/engine-retirement.d.ts.map +0 -1
  121. package/dist/error-diagnostics.d.ts +0 -32
  122. package/dist/error-diagnostics.d.ts.map +0 -1
  123. package/dist/external-keyframes.d.ts +0 -23
  124. package/dist/external-keyframes.d.ts.map +0 -1
  125. package/dist/index.d.ts.map +0 -1
  126. package/dist/manifest-diagnostics.d.ts +0 -62
  127. package/dist/manifest-diagnostics.d.ts.map +0 -1
  128. package/dist/mdx-preprocessor.d.ts +0 -39
  129. package/dist/mdx-preprocessor.d.ts.map +0 -1
  130. package/dist/path-aliases.d.ts +0 -34
  131. package/dist/path-aliases.d.ts.map +0 -1
  132. package/dist/post-process-css.d.ts.map +0 -1
  133. package/dist/prefix.d.ts +0 -15
  134. package/dist/prefix.d.ts.map +0 -1
  135. package/dist/replacement-plans.d.ts +0 -46
  136. package/dist/replacement-plans.d.ts.map +0 -1
  137. package/dist/resolve-asset.d.ts +0 -14
  138. package/dist/resolve-asset.d.ts.map +0 -1
  139. package/dist/run-analysis.d.ts +0 -74
  140. package/dist/run-analysis.d.ts.map +0 -1
  141. package/dist/source-identity.d.ts +0 -93
  142. package/dist/source-identity.d.ts.map +0 -1
  143. package/dist/static-css.d.ts +0 -25
  144. package/dist/static-css.d.ts.map +0 -1
  145. package/dist/system-config.d.ts +0 -57
  146. package/dist/system-config.d.ts.map +0 -1
  147. package/dist/system-props-module.d.ts +0 -22
  148. package/dist/system-props-module.d.ts.map +0 -1
  149. package/dist/timing-waterfall.d.ts +0 -17
  150. package/dist/timing-waterfall.d.ts.map +0 -1
  151. package/dist/tsconfig-paths.d.ts +0 -8
  152. package/dist/tsconfig-paths.d.ts.map +0 -1
  153. package/dist/unit-fallback.d.ts +0 -7
  154. package/dist/unit-fallback.d.ts.map +0 -1
  155. package/dist/utils.d.ts +0 -3
  156. package/dist/utils.d.ts.map +0 -1
  157. package/dist/watch-keys.d.ts +0 -12
  158. package/dist/watch-keys.d.ts.map +0 -1
@@ -0,0 +1,2905 @@
1
+ import { createRequire } from "node:module";
2
+ import path, { basename, dirname, join, normalize, posix } from "node:path";
3
+ import { createHash } from "crypto";
4
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "fs";
5
+ import { dirname as dirname$1, extname, isAbsolute, join as join$1, relative, resolve } from "path";
6
+ import { realpathSync as realpathSync$1 } from "node:fs";
7
+ import { createRequire as createRequire$1 } from "module";
8
+ import browserslist from "browserslist";
9
+ import { browserslistToTargets, transform } from "lightningcss";
10
+ import { isUnitlessProperty } from "@animus-ui/properties";
11
+ //#region \0rolldown/runtime.js
12
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
13
+ //#endregion
14
+ //#region pipeline/assemble-stylesheet.ts
15
+ const ANIMUS_LAYERS = [
16
+ "anm-global",
17
+ "anm-base",
18
+ "anm-variants",
19
+ "anm-compounds",
20
+ "anm-states",
21
+ "anm-system",
22
+ "anm-custom"
23
+ ];
24
+ function buildLayerDeclaration(layers, isCustom) {
25
+ return `@layer ${(isCustom ? layers : [...ANIMUS_LAYERS]).join(", ")};\n`;
26
+ }
27
+ function validateLayerOrder(layers) {
28
+ const expected = [...ANIMUS_LAYERS];
29
+ let cursor = 0;
30
+ for (const layer of layers) if (cursor < expected.length && layer === expected[cursor]) cursor++;
31
+ if (cursor < expected.length) {
32
+ const missing = expected.slice(cursor);
33
+ const found = expected.slice(0, cursor);
34
+ if (!expected.every((l) => layers.includes(l))) {
35
+ const absent = expected.filter((l) => !layers.includes(l));
36
+ throw new Error(`[animus-extract] Custom layers missing required layers: ${absent.join(", ")}. All 7 Animus layers must be present: ${expected.join(", ")}`);
37
+ }
38
+ throw new Error(`[animus-extract] Custom layers have wrong order. Found ${found.join(", ")} but then expected ${missing[0]}. Required order: (${expected.join(" < ")}). You may interleave custom layers but must preserve this subsequence.`);
39
+ }
40
+ }
41
+ function stripLeadingLayerDeclaration(css) {
42
+ return css.replace(/^@layer\s+[^;{]+;\s*\n?/, "");
43
+ }
44
+ function assembleStylesheet(options) {
45
+ const hasCustomLayers = !!options.layers;
46
+ const layers = options.layers ?? ANIMUS_LAYERS;
47
+ if (options.layers) validateLayerOrder(options.layers);
48
+ const declaration = buildLayerDeclaration(layers, hasCustomLayers);
49
+ const variables = options.variableCss || "";
50
+ const componentCss = options.componentCss ? stripLeadingLayerDeclaration(options.componentCss) : "";
51
+ const body = [options.globalCss || "", componentCss].filter(Boolean).join("\n");
52
+ if (options.split) return {
53
+ declaration,
54
+ variables,
55
+ body
56
+ };
57
+ return [
58
+ declaration,
59
+ variables,
60
+ body
61
+ ].filter(Boolean).join("\n");
62
+ }
63
+ //#endregion
64
+ //#region pipeline/analyze-project-args.ts
65
+ /** @internal */
66
+ function buildAnalyzeProjectArgs(inputs) {
67
+ return [
68
+ inputs.filesJson,
69
+ inputs.scalesJson,
70
+ inputs.variableMapJson,
71
+ inputs.contextualVarsJson,
72
+ inputs.propConfigJson,
73
+ inputs.groupRegistryJson,
74
+ inputs.packageResolutionJson,
75
+ inputs.devMode,
76
+ inputs.emitterConfigJson,
77
+ inputs.selectorAliasesJson,
78
+ null,
79
+ inputs.globalStyleBlocksJson,
80
+ inputs.pathAliasesJson,
81
+ inputs.keyframesJson,
82
+ inputs.staticCssJson,
83
+ inputs.conditionAliasesJson,
84
+ inputs.externalDirsJson,
85
+ inputs.transformSourcesJson
86
+ ];
87
+ }
88
+ //#endregion
89
+ //#region pipeline/content-hash.ts
90
+ function contentHash(source) {
91
+ return createHash("md5").update(source).digest("hex");
92
+ }
93
+ //#endregion
94
+ //#region pipeline/internal-wire.ts
95
+ function parseInternalWire(json, wire) {
96
+ let parsed;
97
+ try {
98
+ parsed = JSON.parse(json);
99
+ } catch (error) {
100
+ throw new TypeError(`[animus] ${wire} is not valid JSON. animus produces this wire, so this is an engine bug rather than a configuration error: ${String(error)}`, { cause: error });
101
+ }
102
+ return parsed;
103
+ }
104
+ //#endregion
105
+ //#region pipeline/mdx-preprocessor.ts
106
+ /**
107
+ * MDX compiled to scanner-consumable JSX, plus the extension sets every
108
+ * driver imports. `@mdx-js/mdx` is an optional peer, so its import is lazy.
109
+ */
110
+ const DEFAULT_EXTENSIONS = [
111
+ ".ts",
112
+ ".tsx",
113
+ ".js",
114
+ ".jsx",
115
+ ".mdx"
116
+ ];
117
+ /**
118
+ * The one engine-transform file gate every driver imports — the suffixes the
119
+ * engine's parser accepts. `.mdx` is preprocessed to `.tsx` before it.
120
+ */
121
+ const ENGINE_TRANSFORM_EXTENSIONS = [
122
+ "ts",
123
+ "tsx",
124
+ "js",
125
+ "jsx",
126
+ "mjs"
127
+ ];
128
+ const ENGINE_TRANSFORM_RE = new RegExp(`\\.(?:${ENGINE_TRANSFORM_EXTENSIONS.join("|")})$`);
129
+ function isEngineTransformExtension(path) {
130
+ return ENGINE_TRANSFORM_RE.test(path);
131
+ }
132
+ /**
133
+ * Compile MDX to scanner-consumable JSX. `missing-dep` means the consumer
134
+ * must install `@mdx-js/mdx`; hosts warn and skip the file on `error`.
135
+ */
136
+ async function preprocessMdx(source, filename) {
137
+ const mdxMod = await import("@mdx-js/mdx").catch(() => null);
138
+ if (mdxMod === null) return { kind: "missing-dep" };
139
+ try {
140
+ const vfile = await mdxMod.compile(source, {
141
+ outputFormat: "program",
142
+ development: false,
143
+ jsx: true
144
+ });
145
+ return {
146
+ kind: "ok",
147
+ source: `/* @mdx-source: ${filename} */\n${String(vfile)}`
148
+ };
149
+ } catch (err) {
150
+ return {
151
+ kind: "error",
152
+ error: err instanceof Error ? err.message : String(err)
153
+ };
154
+ }
155
+ }
156
+ //#endregion
157
+ //#region pipeline/svelte-source-adapter.ts
158
+ const isCompilerReference = (value) => Object(value) === value;
159
+ const isCompilerCallable = (value) => {
160
+ if (!isCompilerReference(value)) return false;
161
+ try {
162
+ Function.prototype.toString.call(value);
163
+ return true;
164
+ } catch {
165
+ return false;
166
+ }
167
+ };
168
+ const isCompilerRecord = (value) => isCompilerReference(value) && !isCompilerCallable(value);
169
+ const acceptsCompilerPrimitive = (value, read) => {
170
+ if (isCompilerReference(value)) return false;
171
+ try {
172
+ read();
173
+ return true;
174
+ } catch {
175
+ return false;
176
+ }
177
+ };
178
+ const isCompilerString = (value) => acceptsCompilerPrimitive(value, () => String.prototype.valueOf.call(value));
179
+ const isCompilerNumber = (value) => acceptsCompilerPrimitive(value, () => Number.prototype.valueOf.call(value));
180
+ function isNode(value) {
181
+ return isCompilerRecord(value) && isCompilerString(value.type) && isCompilerNumber(value.start) && isCompilerNumber(value.end);
182
+ }
183
+ function childNode(value) {
184
+ return isNode(value) ? value : null;
185
+ }
186
+ function isProgramNode(value) {
187
+ return isNode(value) && Array.isArray(value.body) && value.body.every((child) => isNode(child));
188
+ }
189
+ function isScriptNode(value) {
190
+ return isNode(value) && isProgramNode(value.content);
191
+ }
192
+ function parseSvelteAst(value) {
193
+ if (value.module !== void 0 && value.module !== null && !isScriptNode(value.module)) throw new TypeError("svelte/compiler returned an invalid module script");
194
+ if (value.instance !== void 0 && value.instance !== null && !isScriptNode(value.instance)) throw new TypeError("svelte/compiler returned an invalid instance script");
195
+ if (!isCompilerRecord(value.fragment)) throw new TypeError("svelte/compiler returned an invalid fragment");
196
+ return {
197
+ module: value.module,
198
+ instance: value.instance,
199
+ fragment: value.fragment
200
+ };
201
+ }
202
+ function nodeName(node) {
203
+ return node?.type === "Identifier" && isCompilerString(node.name) ? node.name : null;
204
+ }
205
+ /** Adaptation touches one source at a time, so a last-value memo runs the
206
+ * all-ASCII check once per file rather than per span endpoint. */
207
+ let asciiMemoSource = null;
208
+ let asciiMemoValue = false;
209
+ function isAsciiOnly(source) {
210
+ if (asciiMemoSource !== source) {
211
+ asciiMemoSource = source;
212
+ asciiMemoValue = Buffer.byteLength(source) === source.length;
213
+ }
214
+ return asciiMemoValue;
215
+ }
216
+ function byteOffset(source, characterOffset) {
217
+ if (isAsciiOnly(source)) return characterOffset;
218
+ return Buffer.byteLength(source.slice(0, characterOffset));
219
+ }
220
+ function byteSpan(source, node) {
221
+ return {
222
+ start: byteOffset(source, node.start),
223
+ end: byteOffset(source, node.end)
224
+ };
225
+ }
226
+ function sourcePosition(source, characterOffset) {
227
+ let line = 1;
228
+ let lineStart = 0;
229
+ for (let index = 0; index < characterOffset; index += 1) {
230
+ const code = source.charCodeAt(index);
231
+ if (code === 13) {
232
+ if (index + 1 < characterOffset && source.charCodeAt(index + 1) === 10) index += 1;
233
+ line += 1;
234
+ lineStart = index + 1;
235
+ } else if (code === 10 || code === 8232 || code === 8233) {
236
+ line += 1;
237
+ lineStart = index + 1;
238
+ }
239
+ }
240
+ return {
241
+ line,
242
+ column: characterOffset - lineStart
243
+ };
244
+ }
245
+ function sourceLocation(source, node) {
246
+ return {
247
+ start: sourcePosition(source, node.start),
248
+ end: sourcePosition(source, node.end)
249
+ };
250
+ }
251
+ function createsNestedBindingScope(node) {
252
+ return node.type === "BlockStatement" || node.type === "CatchClause" || node.type === "ClassBody" || node.type === "ClassDeclaration" || node.type === "ClassExpression" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ForStatement" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "StaticBlock" || node.type === "SwitchStatement" || node.type === "TSModuleBlock";
253
+ }
254
+ function walk(node, visit, nestedScopeDepth = 0) {
255
+ visit(node, nestedScopeDepth);
256
+ const childScopeDepth = nestedScopeDepth + (createsNestedBindingScope(node) ? 1 : 0);
257
+ for (const [key, value] of Object.entries(node)) {
258
+ if (key === "loc" || key === "start" || key === "end") continue;
259
+ if (Array.isArray(value)) {
260
+ for (const item of value) if (isNode(item)) walk(item, visit, childScopeDepth);
261
+ } else if (isNode(value)) walk(value, visit, childScopeDepth);
262
+ }
263
+ }
264
+ /**
265
+ * Svelte `Fragment` containers carry no `start`/`end`, so the `isNode` gate
266
+ * would stop at each one: descend plain objects, visit span-carrying nodes.
267
+ */
268
+ function walkFragment(value, visit) {
269
+ const seen = /* @__PURE__ */ new WeakSet();
270
+ const descend = (current) => {
271
+ if (Array.isArray(current)) {
272
+ for (const item of current) descend(item);
273
+ return;
274
+ }
275
+ if (!isCompilerRecord(current)) return;
276
+ if (seen.has(current)) return;
277
+ seen.add(current);
278
+ if (isNode(current)) visit(current);
279
+ for (const [key, child] of Object.entries(current)) {
280
+ if (key === "loc" || key === "start" || key === "end" || key === "metadata" || key === "parent") continue;
281
+ descend(child);
282
+ }
283
+ };
284
+ descend(value);
285
+ }
286
+ function importBindings(program) {
287
+ const bindings = /* @__PURE__ */ new Map();
288
+ for (const declaration of program.body) {
289
+ if (declaration.type !== "ImportDeclaration" || declaration.importKind === "type" || !Array.isArray(declaration.specifiers)) continue;
290
+ for (const value of declaration.specifiers) {
291
+ if (!isNode(value) || value.importKind === "type") continue;
292
+ const local = childNode(value.local);
293
+ const localName = nodeName(local);
294
+ if (!local || !localName) continue;
295
+ const sourceNode = childNode(declaration.source);
296
+ const importSource = sourceNode && isCompilerString(sourceNode.value) ? sourceNode.value : null;
297
+ if (!importSource) continue;
298
+ const namedImported = importedName(value);
299
+ if (value.type === "ImportSpecifier" && namedImported === null) continue;
300
+ const importedNode = childNode(value.imported);
301
+ const kind = value.type === "ImportSpecifier" ? importedNode?.type === "Literal" ? "string-named" : namedImported === "default" ? "named-default" : "named" : value.type === "ImportDefaultSpecifier" ? "default" : value.type === "ImportNamespaceSpecifier" ? "namespace" : null;
302
+ if (kind) bindings.set(localName, {
303
+ kind,
304
+ source: importSource,
305
+ imported: kind === "default" || kind === "named-default" ? "default" : kind === "namespace" ? "*" : namedImported,
306
+ declaration,
307
+ specifier: value,
308
+ local
309
+ });
310
+ }
311
+ }
312
+ return bindings;
313
+ }
314
+ function diagnostic(code, message, originalPath, source, node, location) {
315
+ const result = {
316
+ code,
317
+ message,
318
+ originalPath
319
+ };
320
+ if (node) {
321
+ result.span = byteSpan(source, node);
322
+ result.location = location ?? sourceLocation(source, node);
323
+ }
324
+ return result;
325
+ }
326
+ function parseCompilerError(value) {
327
+ if (!isCompilerRecord(value)) return null;
328
+ const parsed = { positions: Array.isArray(value.position) ? value.position : [] };
329
+ if (isCompilerRecord(value.start)) parsed.start = value.start;
330
+ if (isCompilerRecord(value.end)) parsed.end = value.end;
331
+ return parsed;
332
+ }
333
+ function compilerParseErrorRange(error, source) {
334
+ const candidate = parseCompilerError(error);
335
+ if (candidate === null) return null;
336
+ const positions = candidate.positions;
337
+ const startCharacter = isCompilerNumber(candidate.start?.character) ? candidate.start.character : isCompilerNumber(positions[0]) ? positions[0] : null;
338
+ const endCharacter = isCompilerNumber(candidate.end?.character) ? candidate.end.character : isCompilerNumber(positions[1]) ? positions[1] : startCharacter;
339
+ if (startCharacter === null || endCharacter === null || startCharacter < 0 || endCharacter < startCharacter || endCharacter > source.length) return null;
340
+ const node = {
341
+ type: "SvelteParseError",
342
+ start: startCharacter,
343
+ end: endCharacter
344
+ };
345
+ const fallback = sourceLocation(source, node);
346
+ const point = (value, fallbackPoint) => ({
347
+ line: isCompilerNumber(value?.line) ? value.line : fallbackPoint.line,
348
+ column: isCompilerNumber(value?.column) ? value.column : fallbackPoint.column
349
+ });
350
+ return {
351
+ node,
352
+ location: {
353
+ start: point(candidate.start, fallback.start),
354
+ end: point(candidate.end, fallback.end)
355
+ }
356
+ };
357
+ }
358
+ function calleeParts(node) {
359
+ if (node.type !== "CallExpression") return null;
360
+ const callee = childNode(node.callee);
361
+ if (!callee || callee.type !== "MemberExpression") return null;
362
+ const object = childNode(callee.object);
363
+ const property = childNode(callee.property);
364
+ if (!object || !property) return null;
365
+ if ((nodeName(property) ?? (property.type === "Literal" && property.value === "attrs" ? "attrs" : null)) !== "attrs") return null;
366
+ return {
367
+ object,
368
+ property,
369
+ computed: callee.computed === true,
370
+ optional: callee.optional === true || node.optional === true
371
+ };
372
+ }
373
+ function resolverImportAccess(receiver, bindings) {
374
+ const directName = nodeName(receiver);
375
+ if (directName) {
376
+ const binding = bindings.get(directName);
377
+ if (!binding) return null;
378
+ return {
379
+ binding,
380
+ resolver: receiver,
381
+ displayName: directName,
382
+ request: {
383
+ source: binding.source,
384
+ imported: binding.imported,
385
+ local: directName,
386
+ access: {
387
+ kind: "direct",
388
+ importKind: binding.kind
389
+ }
390
+ }
391
+ };
392
+ }
393
+ if (receiver.type !== "MemberExpression" || receiver.computed === true || receiver.optional === true) return null;
394
+ const namespaceNode = childNode(receiver.object);
395
+ const memberNode = childNode(receiver.property);
396
+ const namespaceName = nodeName(namespaceNode);
397
+ const memberName = nodeName(memberNode) ?? (memberNode?.type === "Literal" && isCompilerString(memberNode.value) ? memberNode.value : null);
398
+ if (!namespaceNode || !namespaceName || !memberName) return null;
399
+ const binding = bindings.get(namespaceName);
400
+ if (!binding || binding.kind !== "namespace") return null;
401
+ return {
402
+ binding,
403
+ resolver: receiver,
404
+ displayName: `${namespaceName}.${memberName}`,
405
+ request: {
406
+ source: binding.source,
407
+ imported: memberName,
408
+ local: namespaceName,
409
+ access: {
410
+ kind: "namespace-member",
411
+ importKind: "namespace"
412
+ }
413
+ }
414
+ };
415
+ }
416
+ function isSupportedResolverAccess(access) {
417
+ return access.binding.kind === "named" && access.request.access.kind === "direct";
418
+ }
419
+ function resolverCallSite(node, bindings) {
420
+ if (node.type !== "CallExpression") return null;
421
+ const parts = calleeParts(node);
422
+ if (parts) {
423
+ const access = resolverImportAccess(parts.object, bindings);
424
+ if (!access) return null;
425
+ return {
426
+ access,
427
+ form: "attrs",
428
+ computed: parts.computed,
429
+ optional: parts.optional
430
+ };
431
+ }
432
+ const callee = childNode(node.callee);
433
+ if (!callee) return null;
434
+ const access = resolverImportAccess(callee, bindings);
435
+ if (!access) return null;
436
+ return {
437
+ access,
438
+ form: "callable",
439
+ computed: callee.type === "MemberExpression" && callee.computed === true,
440
+ optional: node.optional === true || callee.type === "MemberExpression" && callee.optional === true
441
+ };
442
+ }
443
+ function argumentSpan(argumentsList, fallback) {
444
+ if (argumentsList.length === 0) return fallback;
445
+ return {
446
+ type: "ArgumentRange",
447
+ start: argumentsList[0].start,
448
+ end: argumentsList[argumentsList.length - 1].end
449
+ };
450
+ }
451
+ function propertyName(node) {
452
+ const key = childNode(node.key);
453
+ if (!key) return null;
454
+ if (key.type === "Identifier" && isCompilerString(key.name)) return key.name;
455
+ if (key.type === "Literal" && isCompilerString(key.value)) return key.value;
456
+ return null;
457
+ }
458
+ function isJsxAttributeName(name) {
459
+ return /^[A-Za-z_$][A-Za-z0-9_$-]*$/.test(name);
460
+ }
461
+ function tsxIncompatibleValue(value) {
462
+ let incompatible = null;
463
+ walk(value, (node) => {
464
+ if (incompatible) return;
465
+ if (node.type === "TSTypeAssertion" || node.type === "ArrowFunctionExpression" && childNode(node.typeParameters) !== null) incompatible = node;
466
+ });
467
+ return incompatible;
468
+ }
469
+ function readProperties(argument, source, originalPath) {
470
+ if (!Array.isArray(argument.properties)) return diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", "Resolver .attrs() requires an object literal with statically named properties.", originalPath, source, argument);
471
+ const properties = [];
472
+ for (const value of argument.properties) {
473
+ if (!isNode(value)) continue;
474
+ if (value.type === "SpreadElement") return diagnostic("SVELTE_ATTRS_SPREAD_UNRESOLVED", "Resolver .attrs() object spreads are unresolved; enumerate properties explicitly.", originalPath, source, value);
475
+ if (value.type !== "Property") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() accepts ordinary object properties only.", originalPath, source, value);
476
+ if (value.computed === true) return diagnostic("SVELTE_ATTRS_COMPUTED_KEY", "Resolver .attrs() computed property names cannot be attributed safely.", originalPath, source, value);
477
+ if (value.method === true || value.kind !== "init") return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() methods and accessors cannot be projected as attributes.", originalPath, source, value);
478
+ const key = childNode(value.key);
479
+ const propertyValue = childNode(value.value);
480
+ const name = propertyName(value);
481
+ if (!key || !propertyValue || !name || !isJsxAttributeName(name)) return diagnostic("SVELTE_ATTRS_PROPERTY_UNSUPPORTED", "Resolver .attrs() property names must be JSX-compatible identifier or string names.", originalPath, source, value);
482
+ const incompatibleValue = tsxIncompatibleValue(propertyValue);
483
+ if (incompatibleValue) return diagnostic("SVELTE_ATTRS_VALUE_UNSUPPORTED", "Resolver .attrs() values using angle-bracket type assertions or generic arrow parameters cannot be projected into TSX safely.", originalPath, source, incompatibleValue);
484
+ properties.push({
485
+ name,
486
+ property: value,
487
+ key,
488
+ value: propertyValue
489
+ });
490
+ }
491
+ return properties;
492
+ }
493
+ var VirtualSourceBuilder = class {
494
+ originalSource;
495
+ chunks = [];
496
+ bytes = 0;
497
+ mappings = [];
498
+ constructor(originalSource) {
499
+ this.originalSource = originalSource;
500
+ }
501
+ append(text) {
502
+ this.chunks.push(text);
503
+ this.bytes += Buffer.byteLength(text);
504
+ }
505
+ appendMapped(text, kind, original) {
506
+ const start = this.bytes;
507
+ this.append(text);
508
+ this.mappings.push({
509
+ kind,
510
+ generated: {
511
+ start,
512
+ end: this.bytes
513
+ },
514
+ original: byteSpan(this.originalSource, original)
515
+ });
516
+ }
517
+ toString() {
518
+ return this.chunks.join("");
519
+ }
520
+ };
521
+ function importedName(specifier) {
522
+ const imported = childNode(specifier.imported);
523
+ if (!imported) return null;
524
+ if (imported.type === "Identifier" && isCompilerString(imported.name)) return imported.name;
525
+ if (imported.type === "Literal" && isCompilerString(imported.value)) return imported.value;
526
+ return null;
527
+ }
528
+ function renderImport(declaration, witnessedBindings, source) {
529
+ if (!Array.isArray(declaration.specifiers)) return null;
530
+ const names = [];
531
+ const locals = [];
532
+ for (const value of declaration.specifiers) {
533
+ if (!isNode(value) || value.type !== "ImportSpecifier") continue;
534
+ const localName = nodeName(childNode(value.local));
535
+ if (!localName || !witnessedBindings.has(localName)) continue;
536
+ names.push(source.slice(value.start, value.end));
537
+ locals.push(localName);
538
+ }
539
+ if (names.length === 0) return null;
540
+ const sourceNode = childNode(declaration.source);
541
+ const sourceText = sourceNode && isCompilerString(sourceNode.raw) ? sourceNode.raw : sourceNode && isCompilerString(sourceNode.value) ? JSON.stringify(sourceNode.value) : null;
542
+ return sourceText ? {
543
+ text: `import { ${names.join(", ")} } from ${sourceText};\n`,
544
+ locals
545
+ } : null;
546
+ }
547
+ /** Memoizes success only: a missing optional peer retries per file, so
548
+ * installing `svelte` mid-session recovers without a restart. */
549
+ let compilerMemo = null;
550
+ async function loadCompiler() {
551
+ if (compilerMemo) return compilerMemo;
552
+ compilerMemo = await import("svelte/compiler").catch(() => null);
553
+ return compilerMemo;
554
+ }
555
+ function projectScope(script, scope, source, originalPath, options, inherited, precomputedOwnBindings) {
556
+ const ownBindings = precomputedOwnBindings ?? importBindings(script.content);
557
+ const bindings = inherited ? new Map([...inherited.bindings, ...ownBindings]) : ownBindings;
558
+ const witnesses = [];
559
+ const diagnostics = [];
560
+ if (bindings.size > 0) walk(script.content, (node, nestedScopeDepth) => {
561
+ const site = resolverCallSite(node, bindings);
562
+ if (!site) return;
563
+ const { access, form } = site;
564
+ const attribution = options.attributeResolver(access.request);
565
+ if (attribution === "other") return;
566
+ const resolverName = access.displayName;
567
+ const callDisplay = form === "attrs" ? `${resolverName}.attrs()` : `${resolverName}()`;
568
+ if (attribution === "unsupported-resolver-form" || !isSupportedResolverAccess(access)) {
569
+ diagnostics.push(diagnostic("SVELTE_ATTRS_IMPORT_UNSUPPORTED", `Resolver '${resolverName}' uses an import/access form that the extraction engine cannot attribute to its exported .asClass() binding.`, originalPath, source, access.resolver));
570
+ return;
571
+ }
572
+ if (nestedScopeDepth > 0) {
573
+ diagnostics.push(diagnostic("SVELTE_ATTRS_SCOPE_UNSUPPORTED", `Resolver '${callDisplay}' is inside a nested binding scope; move the call to the top-level script scope so its import identity is unambiguous.`, originalPath, source, access.resolver));
574
+ return;
575
+ }
576
+ if (site.computed || site.optional) {
577
+ diagnostics.push(diagnostic("SVELTE_ATTRS_CALLEE_UNSUPPORTED", `Resolver '${resolverName}' must be called directly as ${resolverName}(...) or ${resolverName}.attrs(...).`, originalPath, source, node));
578
+ return;
579
+ }
580
+ const argumentsList = Array.isArray(node.arguments) ? node.arguments.filter(isNode) : [];
581
+ if (argumentsList.length === 0) {
582
+ witnesses.push({
583
+ binding: access.binding,
584
+ resolver: access.resolver,
585
+ properties: []
586
+ });
587
+ return;
588
+ }
589
+ if (argumentsList.length !== 1 || argumentsList[0].type !== "ObjectExpression") {
590
+ diagnostics.push(diagnostic("SVELTE_ATTRS_ARGUMENT_UNRESOLVED", `Resolver '${callDisplay}' accepts no argument or one object literal in Svelte projection.`, originalPath, source, argumentSpan(argumentsList, node)));
591
+ return;
592
+ }
593
+ const properties = readProperties(argumentsList[0], source, originalPath);
594
+ if (!Array.isArray(properties)) {
595
+ diagnostics.push(properties);
596
+ return;
597
+ }
598
+ witnesses.push({
599
+ binding: access.binding,
600
+ resolver: access.resolver,
601
+ properties
602
+ });
603
+ });
604
+ if (diagnostics.length > 0 || witnesses.length === 0) return { diagnostics };
605
+ const witnessedBindings = new Set(witnesses.map((witness) => nodeName(witness.binding.local)));
606
+ const builder = new VirtualSourceBuilder(source);
607
+ const pendingNames = new Set(witnessedBindings);
608
+ const renderDeclarations = (declarations) => {
609
+ for (const declaration of declarations) {
610
+ if (declaration.type !== "ImportDeclaration") continue;
611
+ const rendered = renderImport(declaration, pendingNames, source);
612
+ if (!rendered) continue;
613
+ builder.append(rendered.text);
614
+ for (const local of rendered.locals) pendingNames.delete(local);
615
+ }
616
+ };
617
+ renderDeclarations(script.content.body);
618
+ if (inherited) renderDeclarations(inherited.importDeclarations);
619
+ for (const witness of witnesses) {
620
+ const resolverName = nodeName(witness.resolver);
621
+ builder.append("<");
622
+ builder.appendMapped(resolverName, "resolver", witness.resolver);
623
+ for (const property of witness.properties) {
624
+ builder.append(" ");
625
+ builder.appendMapped(property.name, "attribute", property.key);
626
+ builder.append("={");
627
+ builder.appendMapped(source.slice(property.value.start, property.value.end), "value", property.value);
628
+ builder.append("}");
629
+ }
630
+ builder.append(" />;\n");
631
+ }
632
+ return {
633
+ diagnostics: [],
634
+ entry: {
635
+ scope,
636
+ path: `${originalPath}.${scope}.tsx`,
637
+ source: builder.toString(),
638
+ mappings: builder.mappings
639
+ }
640
+ };
641
+ }
642
+ /**
643
+ * Project Svelte script scopes into parser-only TSX witnesses from SOURCE,
644
+ * never compiler output. Unsupported call shapes fail the whole source closed.
645
+ */
646
+ async function adaptSvelteSource(source, originalPath, options) {
647
+ const original = {
648
+ path: originalPath,
649
+ hash: contentHash(source)
650
+ };
651
+ const compiler = await loadCompiler();
652
+ if (!compiler) return {
653
+ kind: "missing-dep",
654
+ original,
655
+ dependency: "svelte/compiler"
656
+ };
657
+ let compilerAst;
658
+ try {
659
+ compilerAst = compiler.parse(source, {
660
+ filename: originalPath,
661
+ modern: true
662
+ });
663
+ } catch (error) {
664
+ const range = compilerParseErrorRange(error, source);
665
+ return {
666
+ kind: "error",
667
+ original,
668
+ diagnostics: [diagnostic("SVELTE_PARSE_ERROR", error instanceof Error ? error.message : String(error), originalPath, source, range?.node, range?.location)]
669
+ };
670
+ }
671
+ const ast = parseSvelteAst(compilerAst);
672
+ const projections = [];
673
+ const moduleBindings = ast.module ? importBindings(ast.module.content) : void 0;
674
+ const instanceBindings = ast.instance ? importBindings(ast.instance.content) : void 0;
675
+ const moduleScope = ast.module && moduleBindings ? {
676
+ bindings: moduleBindings,
677
+ importDeclarations: ast.module.content.body.filter((declaration) => declaration.type === "ImportDeclaration")
678
+ } : void 0;
679
+ if (ast.module) projections.push(projectScope(ast.module, "module", source, originalPath, options, void 0, moduleBindings));
680
+ if (ast.instance) projections.push(projectScope(ast.instance, "instance", source, originalPath, options, moduleScope, instanceBindings));
681
+ const diagnostics = projections.flatMap((projection) => projection.diagnostics);
682
+ if (ast.fragment) {
683
+ const scriptBindings = instanceBindings ?? /* @__PURE__ */ new Map();
684
+ const fragmentBindings = moduleScope ? new Map([...moduleScope.bindings, ...scriptBindings]) : scriptBindings;
685
+ if (fragmentBindings.size > 0) walkFragment(ast.fragment, (node) => {
686
+ const site = resolverCallSite(node, fragmentBindings);
687
+ if (!site) return;
688
+ if (options.attributeResolver(site.access.request) === "other") return;
689
+ diagnostics.push(diagnostic("SVELTE_ATTRS_TEMPLATE_UNSUPPORTED", `Resolver '${site.access.displayName}' is called inside the template; assign the result to a const in the instance script and spread that instead.`, originalPath, source, node));
690
+ });
691
+ }
692
+ if (diagnostics.length > 0) return {
693
+ kind: "error",
694
+ original,
695
+ diagnostics
696
+ };
697
+ return {
698
+ kind: "ok",
699
+ original,
700
+ entries: projections.flatMap((projection) => projection.entry ? [projection.entry] : [])
701
+ };
702
+ }
703
+ //#endregion
704
+ //#region pipeline/source-ingestion.ts
705
+ function isSerializedSourceEntry(value) {
706
+ if (!(value instanceof Object) || Array.isArray(value)) return false;
707
+ return String(value.path) === value.path && String(value.source) === value.source && (value.hash === void 0 || String(value.hash) === value.hash);
708
+ }
709
+ /**
710
+ * Throws on a payload that is not a corpus: an empty corpus is
711
+ * indistinguishable from "no files" and would publish an empty stylesheet.
712
+ */
713
+ function parseFilesJson(filesJson, context) {
714
+ const candidate = parseInternalWire(filesJson, `${context} filesJson`);
715
+ if (!Array.isArray(candidate) || !candidate.every(isSerializedSourceEntry)) throw new TypeError(`[${context}] analysis files JSON must be an array of {path, source} entries`);
716
+ return candidate;
717
+ }
718
+ /**
719
+ * Advisory diagnostics warn in every mode and never quarantine their file:
720
+ * OXC recovers parse errors the host bundler itself accepts. All else is fatal.
721
+ */
722
+ function isAdvisorySourceDiagnostic(diagnostic) {
723
+ return diagnostic.code === "SOURCE_NATIVE_PARSE_ERROR";
724
+ }
725
+ const RELATIVE_PROBE_SUFFIXES = [
726
+ "",
727
+ ".ts",
728
+ ".tsx",
729
+ ".js",
730
+ ".jsx",
731
+ "/index.ts",
732
+ "/index.tsx",
733
+ "/index.js",
734
+ "/index.jsx"
735
+ ];
736
+ function extension(path) {
737
+ const basename = posix.basename(path);
738
+ const dot = basename.lastIndexOf(".");
739
+ return dot === -1 ? "" : basename.slice(dot).toLowerCase();
740
+ }
741
+ function canonicalResolverPath(path) {
742
+ return posix.normalize(path.replaceAll("\\", "/"));
743
+ }
744
+ const NODE_NEXT_EXTENSION_MAP = {
745
+ ".js": [
746
+ ".ts",
747
+ ".tsx",
748
+ ".jsx"
749
+ ],
750
+ ".mjs": [".mts"],
751
+ ".cjs": [".cts"]
752
+ };
753
+ function resolveRelativeSource(importerPath, specifier, files) {
754
+ if (!specifier.startsWith(".")) return null;
755
+ const base = posix.normalize(posix.join(posix.dirname(canonicalResolverPath(importerPath)), specifier));
756
+ for (const suffix of RELATIVE_PROBE_SUFFIXES) {
757
+ const candidate = `${base}${suffix}`;
758
+ const actualPath = files.get(candidate);
759
+ if (actualPath !== void 0) return actualPath;
760
+ }
761
+ const explicitExtension = extension(base);
762
+ for (const sourceExtension of NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []) {
763
+ const candidate = `${base.slice(0, -explicitExtension.length)}${sourceExtension}`;
764
+ const actualPath = files.get(candidate);
765
+ if (actualPath !== void 0) return actualPath;
766
+ }
767
+ return null;
768
+ }
769
+ var ResolverExportIndex = class {
770
+ facts;
771
+ collisions;
772
+ files;
773
+ resolverBindings = /* @__PURE__ */ new Map();
774
+ /** Both memos live for one ingest only — the index is rebuilt per call,
775
+ * so neither needs invalidation. */
776
+ attributionMemo = /* @__PURE__ */ new Map();
777
+ resolveMemo = /* @__PURE__ */ new Map();
778
+ constructor(facts, analysisPaths) {
779
+ this.facts = facts;
780
+ const pathsByCanonical = /* @__PURE__ */ new Map();
781
+ for (const path of analysisPaths) {
782
+ const canonicalPath = canonicalResolverPath(path);
783
+ const paths = pathsByCanonical.get(canonicalPath) ?? [];
784
+ paths.push(path);
785
+ pathsByCanonical.set(canonicalPath, paths);
786
+ }
787
+ this.collisions = [...pathsByCanonical].filter(([, paths]) => paths.length > 1).map(([canonicalPath, paths]) => ({
788
+ canonicalPath,
789
+ paths
790
+ }));
791
+ const factPathsByCanonical = /* @__PURE__ */ new Map();
792
+ for (const path of Object.keys(facts.files)) {
793
+ const canonicalPath = canonicalResolverPath(path);
794
+ const paths = factPathsByCanonical.get(canonicalPath) ?? [];
795
+ paths.push(path);
796
+ factPathsByCanonical.set(canonicalPath, paths);
797
+ }
798
+ const files = /* @__PURE__ */ new Map();
799
+ for (const [canonicalPath, paths] of pathsByCanonical) {
800
+ const factPaths = factPathsByCanonical.get(canonicalPath) ?? [];
801
+ if (paths.length === 1 && factPaths.length === 1) files.set(canonicalPath, factPaths[0]);
802
+ }
803
+ this.files = files;
804
+ for (const [path, file] of Object.entries(facts.files)) this.resolverBindings.set(path, new Set(file.chains.filter((chain) => chain.descriptor.terminal === "asClass" && chain.descriptor.extractable && chain.fatalError === null).map((chain) => chain.descriptor.binding)));
805
+ }
806
+ attribute(importerPath, request) {
807
+ const memoKey = `${importerPath}\0${request.source}\0${request.imported}\0${request.access.kind}\0${request.access.importKind}`;
808
+ const memoized = this.attributionMemo.get(memoKey);
809
+ if (memoized !== void 0) return memoized;
810
+ const attribution = this.attributeUncached(importerPath, request);
811
+ this.attributionMemo.set(memoKey, attribution);
812
+ return attribution;
813
+ }
814
+ attributeUncached(importerPath, request) {
815
+ const importedFile = this.resolveSource(importerPath, request.source);
816
+ if (!importedFile) return "other";
817
+ if (this.resolveClassExport(importedFile, request.imported, /* @__PURE__ */ new Set()) === null) return "other";
818
+ return request.access.kind === "direct" && request.access.importKind === "named" ? "resolver" : "unsupported-resolver-form";
819
+ }
820
+ resolveSource(importerPath, specifier) {
821
+ const key = `${importerPath}\0${specifier}`;
822
+ let resolved = this.resolveMemo.get(key);
823
+ if (resolved === void 0) {
824
+ resolved = resolveRelativeSource(importerPath, specifier, this.files);
825
+ this.resolveMemo.set(key, resolved);
826
+ }
827
+ return resolved;
828
+ }
829
+ resolveClassExport(path, exportedName, seen) {
830
+ const identity = `${path}\0${exportedName}`;
831
+ if (seen.has(identity)) return null;
832
+ seen.add(identity);
833
+ const file = this.facts.files[path];
834
+ const exported = file?.exports.find((candidate) => candidate.exported === exportedName);
835
+ if (!file || !exported) return null;
836
+ if (exported.source === null) {
837
+ if (exported.local === null) return null;
838
+ if (this.resolverBindings.get(path)?.has(exported.local)) return exported.local;
839
+ const imported = file.imports.find((candidate) => candidate.local === exported.local);
840
+ if (!imported) return null;
841
+ const importedFile = this.resolveSource(path, imported.source);
842
+ return importedFile ? this.resolveClassExport(importedFile, imported.imported, seen) : null;
843
+ }
844
+ if (exported.original === null) return null;
845
+ const nextFile = this.resolveSource(path, exported.source);
846
+ return nextFile ? this.resolveClassExport(nextFile, exported.original, seen) : null;
847
+ }
848
+ };
849
+ /**
850
+ * Native and MDX entries must establish the resolver index before any Svelte
851
+ * projection; bare-package and aliased sources stay unattributed by design.
852
+ */
853
+ async function ingestSourceEntries(rawEntries, options) {
854
+ const mdxAdapter = options.preprocessMdx ?? preprocessMdx;
855
+ const svelteAdapter = options.adaptSvelte ?? adaptSvelteSource;
856
+ const originalEntries = rawEntries.map((entry) => ({
857
+ ...entry,
858
+ hash: entry.hash ?? contentHash(entry.source)
859
+ }));
860
+ const rawOriginalPaths = new Set(originalEntries.map((entry) => entry.path));
861
+ const analysisEntries = [];
862
+ const ownership = {};
863
+ const analysisOwner = /* @__PURE__ */ new Map();
864
+ const diagnostics = [];
865
+ const svelteEntries = [];
866
+ const addAnalysisEntry = (originalPath, path, source, hash) => {
867
+ analysisEntries.push({
868
+ path,
869
+ source,
870
+ hash: hash ?? contentHash(source)
871
+ });
872
+ ownership[originalPath].analysisPaths.push(path);
873
+ analysisOwner.set(path, originalPath);
874
+ };
875
+ const addAdaptedEntries = (originalPath, entries) => {
876
+ const pendingPaths = /* @__PURE__ */ new Set();
877
+ for (const entry of entries) {
878
+ const conflictingOriginalPath = rawOriginalPaths.has(entry.path) ? entry.path : analysisOwner.get(entry.path) ?? (pendingPaths.has(entry.path) ? originalPath : null);
879
+ if (conflictingOriginalPath !== null) {
880
+ diagnostics.push({
881
+ code: "SOURCE_ANALYSIS_PATH_COLLISION",
882
+ message: `Generated analysis path '${entry.path}' collides with '${conflictingOriginalPath}'. Rename one of the source files so adapted analysis paths remain unique.`,
883
+ originalPath,
884
+ analysisPath: entry.path,
885
+ conflictingOriginalPath
886
+ });
887
+ return;
888
+ }
889
+ pendingPaths.add(entry.path);
890
+ }
891
+ for (const entry of entries) addAnalysisEntry(originalPath, entry.path, entry.source);
892
+ };
893
+ for (const original of originalEntries) {
894
+ ownership[original.path] = {
895
+ originalPath: original.path,
896
+ originalHash: original.hash,
897
+ analysisPaths: []
898
+ };
899
+ const kind = extension(original.path);
900
+ if (kind === ".svelte") {
901
+ svelteEntries.push(original);
902
+ continue;
903
+ }
904
+ if (kind !== ".mdx") {
905
+ addAnalysisEntry(original.path, original.path, original.source, original.hash);
906
+ continue;
907
+ }
908
+ const result = await mdxAdapter(original.source, original.path);
909
+ if (result.kind === "missing-dep") {
910
+ diagnostics.push({
911
+ code: "SOURCE_MDX_DEPENDENCY_MISSING",
912
+ message: "Install optional peer dependency '@mdx-js/mdx' to analyze opted-in MDX source.",
913
+ originalPath: original.path
914
+ });
915
+ continue;
916
+ }
917
+ if (result.kind === "error" || result.source === void 0) {
918
+ diagnostics.push({
919
+ code: "SOURCE_MDX_PARSE_ERROR",
920
+ message: result.error ?? "MDX preprocessing produced no source.",
921
+ originalPath: original.path
922
+ });
923
+ continue;
924
+ }
925
+ addAdaptedEntries(original.path, [{
926
+ path: `${original.path}.tsx`,
927
+ source: result.source
928
+ }]);
929
+ }
930
+ const facts = collectFileFacts(analysisEntries, options);
931
+ for (const [analysisPath, file] of Object.entries(facts.files)) {
932
+ const originalPath = analysisOwner.get(analysisPath) ?? analysisPath;
933
+ for (const message of file.parseDiagnostics) diagnostics.push({
934
+ code: "SOURCE_NATIVE_PARSE_ERROR",
935
+ message,
936
+ originalPath,
937
+ analysisPath
938
+ });
939
+ }
940
+ const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path));
941
+ for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
942
+ const path = paths[index];
943
+ const conflictingPath = paths[(index + 1) % paths.length];
944
+ diagnostics.push({
945
+ code: "SOURCE_RESOLVER_IDENTITY_COLLISION",
946
+ message: `Resolver lookup paths '${path}' and '${conflictingPath}' normalize to the same private identity '${canonicalPath}'. Rename one of the source files so resolver identities remain unique.`,
947
+ originalPath: analysisOwner.get(path) ?? path,
948
+ canonicalPath,
949
+ conflictingOriginalPath: analysisOwner.get(conflictingPath) ?? conflictingPath
950
+ });
951
+ }
952
+ for (const original of svelteEntries) {
953
+ const result = await svelteAdapter(original.source, original.path, { attributeResolver: (request) => resolvers.attribute(original.path, request) });
954
+ if (result.kind === "missing-dep") {
955
+ diagnostics.push({
956
+ code: "SOURCE_SVELTE_DEPENDENCY_MISSING",
957
+ message: "Install optional peer dependency 'svelte' to analyze opted-in Svelte source.",
958
+ originalPath: original.path
959
+ });
960
+ continue;
961
+ }
962
+ if (result.kind === "error") {
963
+ diagnostics.push(...result.diagnostics);
964
+ continue;
965
+ }
966
+ addAdaptedEntries(original.path, result.entries);
967
+ }
968
+ return {
969
+ originalEntries,
970
+ analysisEntries,
971
+ ownership,
972
+ diagnostics
973
+ };
974
+ }
975
+ /**
976
+ * `parseCount` counts the parses performed by THIS call, so a fully-memoized
977
+ * pass reports zero.
978
+ */
979
+ function collectFileFacts(analysisEntries, options) {
980
+ const cache = options.factsCache;
981
+ if (!cache) return JSON.parse(options.extractFacts(JSON.stringify(analysisEntries)));
982
+ const pending = analysisEntries.filter((entry) => cache.get(entry.path)?.hash !== entry.hash);
983
+ let parseCount = 0;
984
+ if (pending.length > 0) {
985
+ const fresh = JSON.parse(options.extractFacts(JSON.stringify(pending)));
986
+ parseCount = fresh.parseCount;
987
+ for (const entry of pending) {
988
+ const facts = fresh.files[entry.path];
989
+ if (facts) cache.set(entry.path, {
990
+ hash: entry.hash,
991
+ facts
992
+ });
993
+ else cache.delete(entry.path);
994
+ }
995
+ }
996
+ const currentPaths = new Set(analysisEntries.map((entry) => entry.path));
997
+ for (const path of [...cache.keys()]) if (!currentPaths.has(path)) cache.delete(path);
998
+ const files = {};
999
+ for (const entry of analysisEntries) {
1000
+ const cached = cache.get(entry.path);
1001
+ if (cached) files[entry.path] = cached.facts;
1002
+ }
1003
+ return {
1004
+ files,
1005
+ parseCount
1006
+ };
1007
+ }
1008
+ /**
1009
+ * Drops each named original and the analysis children it owns. Every corpus
1010
+ * path applies it — skipping it on incremental passes freezes re-analysis.
1011
+ */
1012
+ function withoutInvalidOriginals(result, invalidOriginals) {
1013
+ if (invalidOriginals.size === 0) return result;
1014
+ const ownership = Object.fromEntries(Object.entries(result.ownership).filter(([originalPath]) => !invalidOriginals.has(originalPath)));
1015
+ const analysisPaths = new Set(Object.values(ownership).flatMap((owner) => owner.analysisPaths));
1016
+ return {
1017
+ ...result,
1018
+ originalEntries: result.originalEntries.filter((entry) => !invalidOriginals.has(entry.path)),
1019
+ analysisEntries: result.analysisEntries.filter((entry) => analysisPaths.has(entry.path)),
1020
+ ownership
1021
+ };
1022
+ }
1023
+ /**
1024
+ * Diagnostics name ANALYSIS paths while ownership is recorded for originals,
1025
+ * so projection is required; the result REPLACES the caller's owner map.
1026
+ */
1027
+ function projectExternalFileOwners(result, rawOwners) {
1028
+ const projected = {};
1029
+ for (const owner of Object.values(result.ownership)) {
1030
+ const packageOwner = rawOwners[owner.originalPath];
1031
+ if (!packageOwner) continue;
1032
+ projected[owner.originalPath] = packageOwner;
1033
+ for (const analysisPath of owner.analysisPaths) projected[analysisPath] = packageOwner;
1034
+ }
1035
+ return projected;
1036
+ }
1037
+ /**
1038
+ * One ingestion policy point for every host: a host supplies only a prefix, a
1039
+ * strict flag, and a warn sink; policy and per-host memos stay here.
1040
+ */
1041
+ function createSourceIngestor(host) {
1042
+ const factsCache = /* @__PURE__ */ new Map();
1043
+ /** Non-strict dedupe: a quarantined original re-ingests on every later
1044
+ * corpus pass, so re-warning it on each save is noise. */
1045
+ const warnedByOriginal = /* @__PURE__ */ new Map();
1046
+ return {
1047
+ async ingest(entries) {
1048
+ const extractFacts = host.engineApi().extractFacts;
1049
+ if (extractFacts === void 0) throw new Error(`${host.prefix} native engine does not expose extractFacts required for source adaptation`);
1050
+ return ingestSourceEntries(entries, {
1051
+ extractFacts,
1052
+ factsCache
1053
+ });
1054
+ },
1055
+ surfaceDiagnostics(diagnostics) {
1056
+ const fatal = diagnostics.filter((diagnostic) => !isAdvisorySourceDiagnostic(diagnostic));
1057
+ const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
1058
+ if (diagnostics.length === 0) return invalidOriginals;
1059
+ if (host.strict() && fatal.length > 0) {
1060
+ const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`);
1061
+ throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
1062
+ }
1063
+ for (const diagnostic of diagnostics) {
1064
+ const line = `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`;
1065
+ let warned = warnedByOriginal.get(diagnostic.originalPath);
1066
+ if (!warned) {
1067
+ warned = /* @__PURE__ */ new Set();
1068
+ warnedByOriginal.set(diagnostic.originalPath, warned);
1069
+ }
1070
+ if (warned.has(line)) continue;
1071
+ warned.add(line);
1072
+ host.warn(`${host.prefix} ${line}`);
1073
+ }
1074
+ return invalidOriginals;
1075
+ },
1076
+ markPublished(result) {
1077
+ for (const originalPath of Object.keys(result.ownership)) warnedByOriginal.delete(originalPath);
1078
+ }
1079
+ };
1080
+ }
1081
+ //#endregion
1082
+ //#region pipeline/engine-adapter.ts
1083
+ /**
1084
+ * One engine API for both plugins. Imports neither the native binding nor any
1085
+ * plugin or bundler — every such dependency arrives injected.
1086
+ */
1087
+ /**
1088
+ * Returns a function, invoked per call site, so `isV2()` and
1089
+ * `loadNativeEngine()` re-evaluate each time instead of being captured.
1090
+ */
1091
+ function createV2EngineApi(deps) {
1092
+ const { label, isV2, loadNativeEngine, store } = deps;
1093
+ return () => {
1094
+ if (!isV2()) return loadNativeEngine();
1095
+ const native = loadNativeEngine();
1096
+ return {
1097
+ loadSystemModule: (...args) => native.loadSystemModule(...args),
1098
+ extractFacts: (filesJson) => native.extractFacts(filesJson),
1099
+ analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson) => {
1100
+ const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
1101
+ const sent = /* @__PURE__ */ new Map();
1102
+ for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
1103
+ store.setSentSources(sent);
1104
+ const emitterConfig = emitterConfigJson ? JSON.parse(emitterConfigJson) : {};
1105
+ store.setEngine(null);
1106
+ const config = {
1107
+ runtimeImport: emitterConfig.runtime_import ?? void 0,
1108
+ cssModuleId: emitterConfig.css_module_id ?? void 0,
1109
+ systemPropsModuleId: emitterConfig.system_props_module_id ?? void 0,
1110
+ themeJson: scalesJson,
1111
+ variableMapJson,
1112
+ contextualVarsJson: contextualVarsJson ?? void 0,
1113
+ configJson: propConfigJson,
1114
+ groupRegistryJson,
1115
+ selectorAliasesJson: selectorAliasesJson ?? void 0,
1116
+ conditionAliasesJson: conditionAliasesJson ?? void 0,
1117
+ globalStyleBlocksJson: globalStyleBlocksJson ?? void 0,
1118
+ keyframesJson: keyframesJson ?? void 0,
1119
+ packageResolutionJson: packageResolutionJson ?? void 0,
1120
+ pathAliasesJson: pathAliasesJson ?? void 0,
1121
+ staticCssJson: staticCssJson ?? void 0,
1122
+ externalDirsJson: externalDirsJson ?? void 0,
1123
+ transformSourcesJson: transformSourcesJson ?? void 0,
1124
+ devMode
1125
+ };
1126
+ const engine = new native.ExtractEngine(config);
1127
+ store.setEngine(engine);
1128
+ return engine.analyze(filesJson);
1129
+ },
1130
+ transformFile: (source, path, _manifest) => {
1131
+ const engine = store.getEngine();
1132
+ if (!engine) throw new Error(`[${label}] v2 transform before analyze — engine instance not initialized`);
1133
+ const sentMap = store.getSentSources();
1134
+ if (deps.passThroughUnknownPaths && sentMap && !sentMap.has(path)) return {
1135
+ code: source,
1136
+ hasComponents: false
1137
+ };
1138
+ const sent = sentMap?.get(path);
1139
+ if (sent !== void 0 && sent !== source && !store.getDriftWarned()) {
1140
+ store.setDriftWarned(true);
1141
+ console.warn(`[${label}] v2: transform-time source for ${path} differs from analyze-time source — an upstream transform may be reverted`);
1142
+ }
1143
+ return JSON.parse(engine.transformFile(path));
1144
+ },
1145
+ clearAnalysisCache: () => {
1146
+ const engine = store.getEngine();
1147
+ if (engine) engine.clearCache();
1148
+ store.setEngine(null);
1149
+ }
1150
+ };
1151
+ };
1152
+ }
1153
+ //#endregion
1154
+ //#region pipeline/engine-retirement.ts
1155
+ const RETIRED_ENGINE_MESSAGE = "animus: extraction engine 'v1' was retired (openspec: retire-extract-v1) — v2 is the only engine; remove the engine option / ANIMUS_ENGINE override";
1156
+ function assertNoRetiredEngineSelection(engineOption) {
1157
+ if (engineOption === "v1" || process.env.ANIMUS_ENGINE === "v1") throw new Error(RETIRED_ENGINE_MESSAGE);
1158
+ }
1159
+ //#endregion
1160
+ //#region pipeline/core-options.ts
1161
+ const DRIVER_NAMESPACES = [
1162
+ "vite",
1163
+ "next",
1164
+ "cli"
1165
+ ];
1166
+ const DRIVER_NAMESPACE_KEYS = new Set(DRIVER_NAMESPACES);
1167
+ /**
1168
+ * Applied unconditionally: `node_modules` belongs to external-package
1169
+ * collection, and `.next`/`.animus` hold output, never source.
1170
+ */
1171
+ const STRUCTURAL_EXCLUDE = [
1172
+ "node_modules",
1173
+ ".next",
1174
+ ".animus"
1175
+ ];
1176
+ /** Defaults a user `exclude` list replaces; the structural set it cannot. */
1177
+ const REPLACEABLE_DEFAULT_EXCLUDE = [
1178
+ "dist",
1179
+ ".test.",
1180
+ ".spec."
1181
+ ];
1182
+ const DEFAULT_EXCLUDE = [...STRUCTURAL_EXCLUDE, ...REPLACEABLE_DEFAULT_EXCLUDE];
1183
+ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
1184
+ "system",
1185
+ "exclude",
1186
+ "extensions",
1187
+ "strict",
1188
+ "verbose",
1189
+ "prefix",
1190
+ "targets",
1191
+ "minify",
1192
+ "staticCss",
1193
+ "layers",
1194
+ "engine",
1195
+ "mode",
1196
+ "root"
1197
+ ]);
1198
+ /** Carries a stable `name` so drivers can map it to their own config-error
1199
+ * surface (the CLI's exit code 2). */
1200
+ var AnimusConfigError = class extends Error {
1201
+ constructor(message) {
1202
+ super(message);
1203
+ this.name = "AnimusConfigError";
1204
+ }
1205
+ };
1206
+ /**
1207
+ * `ownKeys` is the caller's own top-level surface; `rejectKeys` names core
1208
+ * keys it does not honor, which must fail rather than be silently ignored.
1209
+ */
1210
+ function assertKnownOptionKeys(raw, ownKeys = [], rejectKeys = [], opts = {}) {
1211
+ const surface = (message) => {
1212
+ if (opts.onUnknownKey === "warn") {
1213
+ opts.warn(message);
1214
+ return;
1215
+ }
1216
+ throw new AnimusConfigError(message);
1217
+ };
1218
+ const own = new Set(ownKeys);
1219
+ for (const key of Object.keys(raw)) {
1220
+ if (raw[key] === void 0) continue;
1221
+ const rejected = rejectKeys.find((entry) => entry.key === key);
1222
+ if (rejected) {
1223
+ surface(`Option "${key}" is not supported by this driver: ${rejected.reason}`);
1224
+ continue;
1225
+ }
1226
+ if (CORE_OPTION_KEYS.has(key) || DRIVER_NAMESPACE_KEYS.has(key) || own.has(key)) continue;
1227
+ const nearest = suggestNearest(key);
1228
+ surface(`Unknown option "${key}".${nearest ? ` Did you mean "${nearest}"?` : ""} Core keys: ${[...CORE_OPTION_KEYS].join(", ")}. Driver namespaces: ${DRIVER_NAMESPACES.join(", ")}.`);
1229
+ }
1230
+ const mode = raw["mode"];
1231
+ if (mode !== void 0 && mode !== "development" && mode !== "production") throw new AnimusConfigError(`Invalid mode "${String(mode)}" — expected "development" or "production".`);
1232
+ for (const { key, ok, expected } of CORE_VALUE_GATES) {
1233
+ const value = raw[key];
1234
+ if (value === void 0 || ok(value)) continue;
1235
+ throw new AnimusConfigError(`Invalid value for "${key}" — expected ${expected}, got ${JSON.stringify(value)}.`);
1236
+ }
1237
+ }
1238
+ /**
1239
+ * Options are foreign JS, so a boxed `String`/`Boolean` must not pass: a
1240
+ * boxed value never behaves as the primitive its consumers expect.
1241
+ */
1242
+ const isString = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
1243
+ const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
1244
+ const isStringArray = (value) => Array.isArray(value) && value.every(isString);
1245
+ const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
1246
+ const CORE_VALUE_GATES = [
1247
+ {
1248
+ key: "system",
1249
+ ok: isString,
1250
+ expected: "a string path"
1251
+ },
1252
+ {
1253
+ key: "exclude",
1254
+ ok: isStringArray,
1255
+ expected: "an array of string patterns"
1256
+ },
1257
+ {
1258
+ key: "extensions",
1259
+ ok: isStringArray,
1260
+ expected: "an array of string extensions"
1261
+ },
1262
+ {
1263
+ key: "strict",
1264
+ ok: isBoolean,
1265
+ expected: "a boolean"
1266
+ },
1267
+ {
1268
+ key: "verbose",
1269
+ ok: isBoolean,
1270
+ expected: "a boolean"
1271
+ },
1272
+ {
1273
+ key: "minify",
1274
+ ok: isBoolean,
1275
+ expected: "a boolean"
1276
+ },
1277
+ {
1278
+ key: "prefix",
1279
+ ok: isString,
1280
+ expected: "a string"
1281
+ },
1282
+ {
1283
+ key: "targets",
1284
+ ok: isStringOrStringArray,
1285
+ expected: "a string or an array of strings"
1286
+ },
1287
+ {
1288
+ key: "layers",
1289
+ ok: isStringArray,
1290
+ expected: "an array of layer names"
1291
+ }
1292
+ ];
1293
+ function suggestNearest(key) {
1294
+ const lower = key.toLowerCase();
1295
+ let best = null;
1296
+ let bestScore = 3;
1297
+ for (const candidate of CORE_OPTION_KEYS) {
1298
+ const score = editDistance(lower, candidate.toLowerCase());
1299
+ if (score < bestScore) {
1300
+ bestScore = score;
1301
+ best = candidate;
1302
+ }
1303
+ }
1304
+ return best;
1305
+ }
1306
+ function editDistance(a, b) {
1307
+ const dp = Array.from({ length: a.length + 1 }, (_, i) => [i]);
1308
+ for (let j = 1; j <= b.length; j++) dp[0][j] = j;
1309
+ for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
1310
+ return dp[a.length][b.length];
1311
+ }
1312
+ const GLOB_META = /[*?]/;
1313
+ /**
1314
+ * Globs compile to a RegExp over the root-relative path, matching the path or
1315
+ * any ancestor directory; plain patterns are substrings. `./` is stripped.
1316
+ */
1317
+ function compilePattern(raw) {
1318
+ let pattern = raw.split("\\").join("/");
1319
+ while (pattern.startsWith("./")) pattern = pattern.slice(2);
1320
+ if (pattern === "") return () => false;
1321
+ if (!GLOB_META.test(pattern)) return (full, rel) => full.includes(pattern) || rel.includes(pattern);
1322
+ const regex = globToRegExp(pattern);
1323
+ return (_full, rel) => {
1324
+ const normalized = rel.split("\\").join("/");
1325
+ if (regex.test(normalized)) return true;
1326
+ for (let slash = normalized.indexOf("/"); slash !== -1; slash = normalized.indexOf("/", slash + 1)) if (regex.test(normalized.slice(0, slash))) return true;
1327
+ return false;
1328
+ };
1329
+ }
1330
+ /** Minimal glob → RegExp: `**` spans segments, `*`/`?` stay within one. */
1331
+ function globToRegExp(glob) {
1332
+ let out = "";
1333
+ let i = 0;
1334
+ const normalized = glob.split("\\").join("/");
1335
+ while (i < normalized.length) {
1336
+ const ch = normalized[i];
1337
+ if (ch === "*") {
1338
+ if (normalized[i + 1] === "*") {
1339
+ if (normalized[i + 2] === "/") {
1340
+ out += "(?:[^/]+/)*";
1341
+ i += 3;
1342
+ } else {
1343
+ out += ".*";
1344
+ i += 2;
1345
+ }
1346
+ } else {
1347
+ out += "[^/]*";
1348
+ i += 1;
1349
+ }
1350
+ } else if (ch === "?") {
1351
+ out += "[^/]";
1352
+ i += 1;
1353
+ } else {
1354
+ out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1355
+ i += 1;
1356
+ }
1357
+ }
1358
+ return new RegExp(`^${out}$`);
1359
+ }
1360
+ /**
1361
+ * A user list replaces only the replaceable defaults. Driver-owned
1362
+ * invariants belong in `extraStructural`; a user list would replace those.
1363
+ */
1364
+ function createExcludeMatcher(userPatterns, extraStructural = []) {
1365
+ const merged = [...STRUCTURAL_EXCLUDE];
1366
+ for (const pattern of extraStructural) if (!merged.includes(pattern)) merged.push(pattern);
1367
+ for (const pattern of userPatterns ?? REPLACEABLE_DEFAULT_EXCLUDE) if (!merged.includes(pattern)) merged.push(pattern);
1368
+ const compiled = merged.map((pattern) => [pattern, compilePattern(pattern)]);
1369
+ const hits = new Map(merged.map((p) => [p, 0]));
1370
+ const explain = (full, rel) => {
1371
+ for (const [pattern, test] of compiled) if (test(full, rel)) {
1372
+ hits.set(pattern, (hits.get(pattern) ?? 0) + 1);
1373
+ return pattern;
1374
+ }
1375
+ return null;
1376
+ };
1377
+ return {
1378
+ patterns: merged,
1379
+ matches: (full, rel) => explain(full, rel) !== null,
1380
+ explain,
1381
+ stats: () => hits
1382
+ };
1383
+ }
1384
+ function resolveMode(explicit, driverDefault) {
1385
+ if (explicit === "development" || explicit === "production") return {
1386
+ mode: explicit,
1387
+ provenance: "explicit"
1388
+ };
1389
+ return {
1390
+ mode: driverDefault(),
1391
+ provenance: "driver-default"
1392
+ };
1393
+ }
1394
+ //#endregion
1395
+ //#region pipeline/discover-files.ts
1396
+ function pathCharRank(code) {
1397
+ return code === 47 || code === 92 ? -1 : code;
1398
+ }
1399
+ function compareDiscoveryOrder(a, b) {
1400
+ const shared = Math.min(a.length, b.length);
1401
+ for (let i = 0; i < shared; i++) {
1402
+ const left = pathCharRank(a.charCodeAt(i));
1403
+ const right = pathCharRank(b.charCodeAt(i));
1404
+ if (left !== right) return left - right;
1405
+ }
1406
+ return a.length - b.length;
1407
+ }
1408
+ function discoverFiles(dir, rootDir, exclude, extensionsSet) {
1409
+ return walkFiles(dir, rootDir, exclude, extensionsSet).sort(compareDiscoveryOrder);
1410
+ }
1411
+ function walkFiles(dir, rootDir, exclude, extensionsSet) {
1412
+ const results = [];
1413
+ let entries;
1414
+ try {
1415
+ entries = readdirSync(dir, { encoding: "utf8" });
1416
+ } catch {
1417
+ return results;
1418
+ }
1419
+ const isExcluded = exclude ? (full, rel) => exclude.matches(full, rel) : () => false;
1420
+ for (const entry of entries) {
1421
+ const fullPath = join$1(dir, entry);
1422
+ if (isExcluded(fullPath, relative(rootDir, fullPath))) continue;
1423
+ let stat;
1424
+ try {
1425
+ stat = statSync(fullPath);
1426
+ } catch {
1427
+ continue;
1428
+ }
1429
+ if (stat.isDirectory()) results.push(...walkFiles(fullPath, rootDir, exclude, extensionsSet));
1430
+ else if (extensionsSet.has(extname(entry))) results.push(fullPath);
1431
+ }
1432
+ return results;
1433
+ }
1434
+ //#endregion
1435
+ //#region pipeline/source-identity.ts
1436
+ /**
1437
+ * One derivation of source identity for discovery, watch ingestion, ownership
1438
+ * and deletion: event paths are lookup INPUTS only and never fork identity.
1439
+ */
1440
+ /**
1441
+ * True when `target` is `root` or a descendant, decided by `relative()`
1442
+ * shape: string prefixing would let `/ui` claim `/ui-old`.
1443
+ */
1444
+ function isPathWithinRoot(root, target, pathApi = path) {
1445
+ const rel = pathApi.relative(root, target);
1446
+ return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
1447
+ }
1448
+ /**
1449
+ * True when two paths share a platform root (win32 drive letter or UNC
1450
+ * share; always true on posix). Drive letters compare case-insensitively.
1451
+ */
1452
+ function sharesVolumeRoot(a, b, pathApi = path) {
1453
+ const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
1454
+ return volumeOf(a) === volumeOf(b);
1455
+ }
1456
+ function canonicalize(p) {
1457
+ try {
1458
+ return realpathSync$1(p);
1459
+ } catch {
1460
+ return null;
1461
+ }
1462
+ }
1463
+ function createSourceIdentity(rootDir) {
1464
+ const normalizedRootDir = path.normalize(rootDir);
1465
+ const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
1466
+ const roots = [];
1467
+ const aliases = /* @__PURE__ */ new Map();
1468
+ const owningRecord = (canonicalPath) => {
1469
+ let owner = null;
1470
+ for (const record of roots) {
1471
+ if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
1472
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
1473
+ }
1474
+ return owner;
1475
+ };
1476
+ return {
1477
+ rootDir: normalizedRootDir,
1478
+ canonicalRootDir,
1479
+ registerExternalRoot(root) {
1480
+ const asRegistered = path.normalize(root);
1481
+ const canonical = canonicalize(asRegistered) ?? asRegistered;
1482
+ const existing = roots.find((record) => record.canonical === canonical);
1483
+ if (existing) return existing.canonical;
1484
+ roots.push({
1485
+ asRegistered,
1486
+ canonical
1487
+ });
1488
+ return canonical;
1489
+ },
1490
+ externalRoots() {
1491
+ return roots.map((record) => record.canonical);
1492
+ },
1493
+ resolveSourceId(inputPath) {
1494
+ const lexical = path.normalize(inputPath);
1495
+ const canonical = canonicalize(lexical);
1496
+ if (canonical === null) return null;
1497
+ const owner = owningRecord(canonical);
1498
+ const ownedBy = (record) => {
1499
+ const pathInRoot = path.relative(record.canonical, canonical);
1500
+ return {
1501
+ sourceKey: path.relative(normalizedRootDir, path.join(record.asRegistered, pathInRoot)),
1502
+ owningRoot: record.canonical,
1503
+ pathInRoot
1504
+ };
1505
+ };
1506
+ const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
1507
+ let resolved;
1508
+ if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
1509
+ const key = path.relative(canonicalRootDir, canonical);
1510
+ resolved = {
1511
+ sourceKey: key,
1512
+ owningRoot: null,
1513
+ pathInRoot: key
1514
+ };
1515
+ } else if (owner) resolved = ownedBy(owner);
1516
+ else return null;
1517
+ aliases.set(lexical, resolved);
1518
+ aliases.set(canonical, resolved);
1519
+ return resolved;
1520
+ },
1521
+ resolveDeletedSourceId(inputPath) {
1522
+ return aliases.get(path.normalize(inputPath)) ?? null;
1523
+ },
1524
+ containingExternalRoot(inputPath) {
1525
+ const lexical = path.normalize(inputPath);
1526
+ let owner = null;
1527
+ for (const record of roots) {
1528
+ if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
1529
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
1530
+ }
1531
+ if (owner) return owner.canonical;
1532
+ const canonical = canonicalize(lexical);
1533
+ if (canonical === null) return null;
1534
+ for (const record of roots) {
1535
+ if (!isPathWithinRoot(record.canonical, canonical)) continue;
1536
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
1537
+ }
1538
+ return owner?.canonical ?? null;
1539
+ }
1540
+ };
1541
+ }
1542
+ //#endregion
1543
+ //#region pipeline/discover-packages.ts
1544
+ function findPackageRoot(absEntryPath) {
1545
+ let pkgRoot = dirname$1(absEntryPath);
1546
+ while (pkgRoot !== dirname$1(pkgRoot) && !existsSync(join$1(pkgRoot, "package.json"))) pkgRoot = dirname$1(pkgRoot);
1547
+ return pkgRoot;
1548
+ }
1549
+ const PACKAGE_SRC_EXCLUDES = [
1550
+ "dist",
1551
+ "node_modules",
1552
+ ".test.",
1553
+ ".spec."
1554
+ ];
1555
+ function isExcludedPackageRelativePath(rel) {
1556
+ return PACKAGE_SRC_EXCLUDES.some((pattern) => rel.includes(pattern));
1557
+ }
1558
+ function walkPackageSources(packageDir, extensionsSet) {
1559
+ return discoverFiles(packageDir, packageDir, void 0, extensionsSet).filter((absPath) => !isExcludedPackageRelativePath(relative(packageDir, absPath)));
1560
+ }
1561
+ const PACKAGE_OUTPUT_EXCLUDES = [
1562
+ "node_modules",
1563
+ ".test.",
1564
+ ".spec.",
1565
+ ".d.ts",
1566
+ ".map"
1567
+ ];
1568
+ const isFile = (path) => {
1569
+ try {
1570
+ return statSync(path).isFile();
1571
+ } catch {
1572
+ return false;
1573
+ }
1574
+ };
1575
+ function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
1576
+ return [
1577
+ absSpecifier,
1578
+ ...Array.from(extensionsSet, (ext) => absSpecifier + ext),
1579
+ ...Array.from(extensionsSet, (ext) => join$1(absSpecifier, `index${ext}`))
1580
+ ].find(isFile) ?? null;
1581
+ }
1582
+ function bareSpecifierPackageName(specifier) {
1583
+ const segments = specifier.split("/");
1584
+ return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
1585
+ }
1586
+ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
1587
+ if (isAbsolute(specifier)) {
1588
+ const resolved = resolveAbsolutePathSpecifier(specifier, extensionsSet);
1589
+ if (!resolved) return null;
1590
+ return isPathWithinRoot(srcDir, resolved) ? resolved : null;
1591
+ }
1592
+ const packageName = bareSpecifierPackageName(specifier);
1593
+ const subpath = specifier.slice(packageName.length + 1);
1594
+ return resolveAbsolutePathSpecifier(join$1(srcDir, subpath || "index"), extensionsSet);
1595
+ }
1596
+ function distEntryIsStale(absEntry, srcDir, srcFiles) {
1597
+ if (isPathWithinRoot(srcDir, absEntry)) return false;
1598
+ let distMtime;
1599
+ try {
1600
+ distMtime = statSync(absEntry).mtimeMs;
1601
+ } catch {
1602
+ return false;
1603
+ }
1604
+ let newestSrcMtime = -Infinity;
1605
+ for (const srcFile of srcFiles) try {
1606
+ newestSrcMtime = Math.max(newestSrcMtime, statSync(srcFile).mtimeMs);
1607
+ } catch {}
1608
+ return distMtime < newestSrcMtime;
1609
+ }
1610
+ async function collectExternalPackageSources(opts) {
1611
+ const { specifiers, resolveSpecifier, rootDir, extensionsSet, hasEntry, onSourceRead, onUnreadable, onPackageResolved } = opts;
1612
+ const entries = [];
1613
+ const pushed = /* @__PURE__ */ new Set();
1614
+ const packageMap = {};
1615
+ const sourceEntries = /* @__PURE__ */ new Map();
1616
+ const packageDirs = [];
1617
+ const dirOwnerSets = {};
1618
+ const dirExtensions = {};
1619
+ const fileOwners = {};
1620
+ const outcomes = [];
1621
+ const claimDir = (dir, specifier) => {
1622
+ (dirOwnerSets[dir] ??= []).push(specifier);
1623
+ };
1624
+ const alreadyIngested = (relPath) => hasEntry(relPath) || pushed.has(relPath);
1625
+ for (const specifier of specifiers) {
1626
+ let absEntry;
1627
+ try {
1628
+ absEntry = await resolveSpecifier(specifier);
1629
+ } catch {
1630
+ absEntry = null;
1631
+ }
1632
+ if (!absEntry && isAbsolute(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
1633
+ if (!absEntry) {
1634
+ outcomes.push({
1635
+ specifier,
1636
+ outcome: "unresolvable",
1637
+ fileCount: 0
1638
+ });
1639
+ continue;
1640
+ }
1641
+ const pkgRoot = findPackageRoot(absEntry);
1642
+ const srcDir = join$1(pkgRoot, "src");
1643
+ let fileCount = 0;
1644
+ let staleDist = false;
1645
+ if (existsSync(srcDir)) {
1646
+ packageDirs.push(srcDir);
1647
+ claimDir(srcDir, specifier);
1648
+ dirExtensions[srcDir] = [...extensionsSet];
1649
+ onPackageResolved?.(specifier, srcDir);
1650
+ const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet);
1651
+ if (srcEntry) {
1652
+ packageMap[specifier] = relative(rootDir, srcEntry);
1653
+ sourceEntries.set(specifier, srcEntry);
1654
+ } else packageMap[specifier] = relative(rootDir, absEntry);
1655
+ if (!isAbsolute(specifier)) {
1656
+ const packageName = bareSpecifierPackageName(specifier);
1657
+ if (packageName !== specifier && !(packageName in packageMap)) {
1658
+ const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
1659
+ if (rootEntry) {
1660
+ packageMap[packageName] = relative(rootDir, rootEntry);
1661
+ sourceEntries.set(packageName, rootEntry);
1662
+ }
1663
+ }
1664
+ }
1665
+ const pkgFiles = walkPackageSources(srcDir, extensionsSet);
1666
+ staleDist = distEntryIsStale(absEntry, srcDir, pkgFiles);
1667
+ for (const pkgFile of pkgFiles) {
1668
+ const relPath = relative(rootDir, pkgFile);
1669
+ if (alreadyIngested(relPath)) {
1670
+ fileCount++;
1671
+ continue;
1672
+ }
1673
+ let source;
1674
+ try {
1675
+ source = readFileSync(pkgFile, "utf-8");
1676
+ } catch (err) {
1677
+ onUnreadable(relPath, err);
1678
+ continue;
1679
+ }
1680
+ onSourceRead?.(source, relPath, pkgFile);
1681
+ entries.push({
1682
+ path: relPath,
1683
+ source
1684
+ });
1685
+ pushed.add(relPath);
1686
+ fileOwners[relPath] ??= specifier;
1687
+ fileCount++;
1688
+ }
1689
+ } else {
1690
+ const outputDir = dirname$1(absEntry);
1691
+ packageDirs.push(outputDir);
1692
+ claimDir(outputDir, specifier);
1693
+ onPackageResolved?.(specifier, outputDir);
1694
+ packageMap[specifier] = relative(rootDir, absEntry);
1695
+ const outputExtensions = new Set(extensionsSet);
1696
+ outputExtensions.add(extname(absEntry));
1697
+ dirExtensions[outputDir] = [...outputExtensions];
1698
+ const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
1699
+ const relToOutput = relative(outputDir, file);
1700
+ return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
1701
+ });
1702
+ if (!outputFiles.includes(absEntry)) outputFiles.unshift(absEntry);
1703
+ for (const outputFile of outputFiles) {
1704
+ const outputRelPath = relative(rootDir, outputFile);
1705
+ if (alreadyIngested(outputRelPath)) {
1706
+ fileCount++;
1707
+ continue;
1708
+ }
1709
+ let source;
1710
+ try {
1711
+ source = readFileSync(outputFile, "utf-8");
1712
+ } catch (err) {
1713
+ onUnreadable(outputRelPath, err);
1714
+ continue;
1715
+ }
1716
+ onSourceRead?.(source, outputRelPath, outputFile);
1717
+ entries.push({
1718
+ path: outputRelPath,
1719
+ source
1720
+ });
1721
+ pushed.add(outputRelPath);
1722
+ fileOwners[outputRelPath] ??= specifier;
1723
+ fileCount++;
1724
+ }
1725
+ }
1726
+ outcomes.push({
1727
+ specifier,
1728
+ outcome: staleDist ? "stale-dist" : fileCount > 0 ? "resolved" : "empty",
1729
+ fileCount
1730
+ });
1731
+ }
1732
+ return {
1733
+ entries,
1734
+ packageMap,
1735
+ sourceEntries,
1736
+ packageDirs,
1737
+ dirOwnerSets,
1738
+ dirExtensions,
1739
+ fileOwners,
1740
+ outcomes
1741
+ };
1742
+ }
1743
+ function firstOwners(dirOwnerSets) {
1744
+ const owners = {};
1745
+ for (const [dir, specifiers] of Object.entries(dirOwnerSets)) if (specifiers.length > 0) owners[dir] = specifiers[0];
1746
+ return owners;
1747
+ }
1748
+ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
1749
+ if (rejectedSpecifiers.size === 0) return collected;
1750
+ const rejectedDirs = Object.entries(collected.dirOwnerSets).filter(([, specs]) => specs.every((s) => rejectedSpecifiers.has(s))).map(([dir]) => dir);
1751
+ const underRejectedDir = (absPath) => rejectedDirs.some((dir) => isPathWithinRoot(dir, absPath));
1752
+ const targetRejected = (specifier, absTarget) => rejectedSpecifiers.has(specifier) || underRejectedDir(absTarget);
1753
+ const packageMap = {};
1754
+ for (const [specifier, relTarget] of Object.entries(collected.packageMap)) {
1755
+ if (targetRejected(specifier, resolve(rootDir, relTarget))) continue;
1756
+ packageMap[specifier] = relTarget;
1757
+ }
1758
+ const sourceEntries = /* @__PURE__ */ new Map();
1759
+ for (const [specifier, absEntry] of collected.sourceEntries) {
1760
+ if (targetRejected(specifier, absEntry)) continue;
1761
+ sourceEntries.set(specifier, absEntry);
1762
+ }
1763
+ const dirOwnerSets = {};
1764
+ for (const [dir, specs] of Object.entries(collected.dirOwnerSets)) {
1765
+ const kept = specs.filter((s) => !rejectedSpecifiers.has(s));
1766
+ if (kept.length === 0) continue;
1767
+ dirOwnerSets[dir] = kept;
1768
+ }
1769
+ const dirExtensions = {};
1770
+ for (const [dir, exts] of Object.entries(collected.dirExtensions)) {
1771
+ if (rejectedDirs.includes(dir)) continue;
1772
+ dirExtensions[dir] = exts;
1773
+ }
1774
+ const fileOwners = {};
1775
+ for (const [relPath, owner] of Object.entries(collected.fileOwners)) {
1776
+ if (rejectedSpecifiers.has(owner)) continue;
1777
+ fileOwners[relPath] = owner;
1778
+ }
1779
+ return {
1780
+ entries: collected.entries.filter((entry) => !rejectedSpecifiers.has(collected.fileOwners[entry.path])),
1781
+ packageMap,
1782
+ sourceEntries,
1783
+ packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
1784
+ dirOwnerSets,
1785
+ dirExtensions,
1786
+ fileOwners,
1787
+ outcomes: collected.outcomes
1788
+ };
1789
+ }
1790
+ function unresolvableIncludesMessage(outcomes) {
1791
+ const unresolvable = outcomes.filter((record) => record.outcome === "unresolvable").map((record) => record.specifier);
1792
+ if (unresolvable.length === 0) return null;
1793
+ return `[animus-extract] unresolvable include specifier(s): ${unresolvable.join(", ")}`;
1794
+ }
1795
+ /** A stale dist silently skews merged registry content while discovery
1796
+ * compiles the fresh sources; null when no specifier is stale. */
1797
+ function staleDistIncludesMessage(outcomes) {
1798
+ const stale = outcomes.filter((record) => record.outcome === "stale-dist").map((record) => record.specifier);
1799
+ if (stale.length === 0) return null;
1800
+ return `[animus-extract] stale dist for include specifier(s): ${stale.join(", ")} — dist entry is older than the newest src/ file; rebuild the package(s) before extracting`;
1801
+ }
1802
+ function extractSystemFilePackages(systemFilePath) {
1803
+ let source;
1804
+ try {
1805
+ source = readFileSync(systemFilePath, "utf-8");
1806
+ } catch {
1807
+ return [];
1808
+ }
1809
+ const identifiers = /* @__PURE__ */ new Set();
1810
+ const constructorRegex = /createSystem\s*\(\s*\{[^}]*?\bincludes\s*:\s*\[([^\]]*)\]/gs;
1811
+ const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
1812
+ const collectIdentifiers = (regex) => {
1813
+ let match;
1814
+ while ((match = regex.exec(source)) !== null) {
1815
+ const inner = match[1];
1816
+ for (const token of inner.split(",")) {
1817
+ const id = token.trim();
1818
+ if (id && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(id)) identifiers.add(id);
1819
+ }
1820
+ }
1821
+ };
1822
+ collectIdentifiers(constructorRegex);
1823
+ collectIdentifiers(chainRegex);
1824
+ const IDENT_START_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*/;
1825
+ const skipTrivia = (from) => {
1826
+ let pos = from;
1827
+ for (;;) {
1828
+ while (pos < source.length && /\s/.test(source[pos])) pos++;
1829
+ if (source.startsWith("//", pos)) {
1830
+ const newline = source.indexOf("\n", pos);
1831
+ pos = newline === -1 ? source.length : newline + 1;
1832
+ continue;
1833
+ }
1834
+ if (source.startsWith("/*", pos)) {
1835
+ const close = source.indexOf("*/", pos + 2);
1836
+ pos = close === -1 ? source.length : close + 2;
1837
+ continue;
1838
+ }
1839
+ return pos;
1840
+ }
1841
+ };
1842
+ const consumeChainLinks = (from) => {
1843
+ let pos = from;
1844
+ for (;;) {
1845
+ let cursor = skipTrivia(pos);
1846
+ if (source[cursor] !== ".") return pos;
1847
+ cursor = skipTrivia(cursor + 1);
1848
+ const method = IDENT_START_RE.exec(source.slice(cursor))?.[0];
1849
+ if (method !== "extend" && method !== "from") return pos;
1850
+ cursor = skipTrivia(cursor + method.length);
1851
+ if (source[cursor] !== "(") return pos;
1852
+ cursor = skipTrivia(cursor + 1);
1853
+ const base = IDENT_START_RE.exec(source.slice(cursor))?.[0];
1854
+ if (!base) return pos;
1855
+ cursor = skipTrivia(cursor + base.length);
1856
+ while (source[cursor] === ".") {
1857
+ const afterDot = skipTrivia(cursor + 1);
1858
+ const segment = IDENT_START_RE.exec(source.slice(afterDot))?.[0];
1859
+ if (!segment) break;
1860
+ cursor = skipTrivia(afterDot + segment.length);
1861
+ }
1862
+ if (source[cursor] === ",") cursor = skipTrivia(cursor + 1);
1863
+ if (source[cursor] !== ")") return pos;
1864
+ identifiers.add(base);
1865
+ pos = cursor + 1;
1866
+ }
1867
+ };
1868
+ const boundIdentifierBefore = (index) => {
1869
+ const match = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*$/.exec(source.slice(0, index));
1870
+ return match ? match[1] : null;
1871
+ };
1872
+ const chainRootIdentifiers = /* @__PURE__ */ new Set();
1873
+ const createSystemAnchor = /createSystem\s*\(/g;
1874
+ let anchorMatch;
1875
+ while ((anchorMatch = createSystemAnchor.exec(source)) !== null) {
1876
+ let pos = anchorMatch.index + anchorMatch[0].length;
1877
+ let depth = 1;
1878
+ while (pos < source.length && depth > 0) {
1879
+ const ch = source[pos];
1880
+ if (ch === "(") depth++;
1881
+ else if (ch === ")") depth--;
1882
+ pos++;
1883
+ }
1884
+ consumeChainLinks(pos);
1885
+ const bound = boundIdentifierBefore(anchorMatch.index);
1886
+ if (bound) chainRootIdentifiers.add(bound);
1887
+ }
1888
+ const scannedRoots = /* @__PURE__ */ new Set();
1889
+ for (;;) {
1890
+ const pendingRoots = [...chainRootIdentifiers].filter((root) => !scannedRoots.has(root));
1891
+ if (pendingRoots.length === 0) break;
1892
+ for (const root of pendingRoots) {
1893
+ scannedRoots.add(root);
1894
+ const rootUse = new RegExp(`(?<![a-zA-Z0-9_$])${root.replace(/\$/g, "\\$")}(?![a-zA-Z0-9_$])`, "g");
1895
+ let useMatch;
1896
+ while ((useMatch = rootUse.exec(source)) !== null) {
1897
+ const afterIdentifier = useMatch.index + useMatch[0].length;
1898
+ if (consumeChainLinks(afterIdentifier) === afterIdentifier) continue;
1899
+ const bound = boundIdentifierBefore(useMatch.index);
1900
+ if (bound) chainRootIdentifiers.add(bound);
1901
+ }
1902
+ }
1903
+ }
1904
+ if (identifiers.size === 0) return [];
1905
+ const importMap = /* @__PURE__ */ new Map();
1906
+ const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
1907
+ let importMatch;
1908
+ while ((importMatch = importRegex.exec(source)) !== null) {
1909
+ const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
1910
+ if (comboDefault) importMap.set(comboDefault, specifier);
1911
+ if (defaultImport) importMap.set(defaultImport, specifier);
1912
+ if (namedImports) for (const binding of namedImports.split(",")) {
1913
+ const parts = binding.trim().split(/\s+as\s+/);
1914
+ const localName = (parts[1] || parts[0]).trim();
1915
+ if (localName) importMap.set(localName, specifier);
1916
+ }
1917
+ }
1918
+ const systemFileDir = dirname$1(systemFilePath);
1919
+ const packages = /* @__PURE__ */ new Set();
1920
+ for (const id of identifiers) {
1921
+ const specifier = importMap.get(id);
1922
+ if (!specifier) continue;
1923
+ if (specifier.startsWith(".")) packages.add(resolve(systemFileDir, specifier));
1924
+ else packages.add(specifier);
1925
+ }
1926
+ return Array.from(packages);
1927
+ }
1928
+ //#endregion
1929
+ //#region pipeline/source-corpus.ts
1930
+ function assembleRawEntries({ fileCache, externalFileOwners }) {
1931
+ const project = [];
1932
+ const external = [];
1933
+ for (const [path, { hash, source }] of fileCache) (externalFileOwners[path] === void 0 ? project : external).push({
1934
+ path,
1935
+ source,
1936
+ hash
1937
+ });
1938
+ project.sort((a, b) => compareDiscoveryOrder(a.path, b.path));
1939
+ return [...project, ...external];
1940
+ }
1941
+ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
1942
+ let published = {
1943
+ analysisEntries: /* @__PURE__ */ new Map(),
1944
+ ownership: {}
1945
+ };
1946
+ return {
1947
+ async prepare(input) {
1948
+ const rawEntries = "fileCache" in input ? assembleRawEntries(input) : input;
1949
+ const ingested = await ingestor.ingest(rawEntries);
1950
+ return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
1951
+ },
1952
+ publish(accepted) {
1953
+ ingestor.markPublished(accepted);
1954
+ published = {
1955
+ analysisEntries: new Map(accepted.analysisEntries.map((entry) => [entry.path, {
1956
+ hash: entry.hash,
1957
+ source: entry.source
1958
+ }])),
1959
+ ownership: accepted.ownership
1960
+ };
1961
+ },
1962
+ get published() {
1963
+ return published;
1964
+ }
1965
+ };
1966
+ }
1967
+ //#endregion
1968
+ //#region pipeline/asset-placeholders.ts
1969
+ const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
1970
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1971
+ const PREFIX_RE = escapeRegExp(ASSET_PLACEHOLDER_PREFIX);
1972
+ const QUOTED_PLACEHOLDER_RE = new RegExp(`(['"])${PREFIX_RE}([^'"]*?)\\1`, "g");
1973
+ const BARE_PLACEHOLDER_RE = new RegExp(`${PREFIX_RE}([^'")\\s]+)`, "g");
1974
+ function findAssetSpecifiers(css) {
1975
+ if (!css.includes("animus-asset:")) return [];
1976
+ const seen = /* @__PURE__ */ new Set();
1977
+ const remainder = css.replace(QUOTED_PLACEHOLDER_RE, (_match, _quote, specifier) => {
1978
+ seen.add(specifier);
1979
+ return "";
1980
+ });
1981
+ for (const match of remainder.matchAll(BARE_PLACEHOLDER_RE)) seen.add(match[1]);
1982
+ return [...seen];
1983
+ }
1984
+ function substituteAssetPlaceholders(css, urlBySpecifier) {
1985
+ if (urlBySpecifier.size === 0) return css;
1986
+ if (!css.includes("animus-asset:")) return css;
1987
+ const specifiers = [...urlBySpecifier.keys()].sort((a, b) => b.length - a.length);
1988
+ let out = css;
1989
+ for (const specifier of specifiers) {
1990
+ const placeholder = new RegExp(escapeRegExp(ASSET_PLACEHOLDER_PREFIX + specifier) + String.raw`(?=['")\s]|$)`, "g");
1991
+ out = out.replace(placeholder, () => urlBySpecifier.get(specifier));
1992
+ }
1993
+ return out;
1994
+ }
1995
+ //#endregion
1996
+ //#region pipeline/resolve-asset.ts
1997
+ /**
1998
+ * Asset resolution for hosts with no bundler hook. Host path aliases apply
1999
+ * FIRST, so `asset('@fonts/x')` resolves as the alias does in app modules.
2000
+ */
2001
+ const requireByRoot = /* @__PURE__ */ new Map();
2002
+ function requireAnchoredAt(rootDir) {
2003
+ let req = requireByRoot.get(rootDir);
2004
+ if (!req) {
2005
+ req = createRequire$1(join$1(rootDir, "package.json"));
2006
+ requireByRoot.set(rootDir, req);
2007
+ }
2008
+ return req;
2009
+ }
2010
+ function packageRootFromEntry(entry) {
2011
+ let current = dirname$1(entry);
2012
+ while (true) {
2013
+ if (existsSync(join$1(current, "package.json"))) return current;
2014
+ const parent = dirname$1(current);
2015
+ if (parent === current) return null;
2016
+ current = parent;
2017
+ }
2018
+ }
2019
+ const aliasTableCache = /* @__PURE__ */ new Map();
2020
+ /**
2021
+ * A parse failure throws: an empty table would disable every alias-based
2022
+ * `asset()` and ship dangling `url()`s as a successful build.
2023
+ */
2024
+ function parseAliasTable(pathAliasesJson) {
2025
+ const cached = aliasTableCache.get(pathAliasesJson);
2026
+ if (cached) return cached;
2027
+ const aliases = parseInternalWire(pathAliasesJson, "pathAliasesJson (the host alias table from buildPathAliasesJson)").aliases ?? [];
2028
+ aliasTableCache.set(pathAliasesJson, aliases);
2029
+ return aliases;
2030
+ }
2031
+ /**
2032
+ * Map a specifier through the host alias table, whose entries arrive sorted
2033
+ * longest-pattern-first. Returns an absolute path to an EXISTING file.
2034
+ */
2035
+ function resolveThroughPathAliases(specifier, rootDir, pathAliasesJson) {
2036
+ if (!pathAliasesJson) return null;
2037
+ const aliases = parseAliasTable(pathAliasesJson);
2038
+ for (const alias of aliases) {
2039
+ let mapped = null;
2040
+ if (alias.type === "exact") {
2041
+ if (alias.pattern === specifier) mapped = alias.replacement;
2042
+ } else if (specifier.startsWith(alias.pattern)) mapped = alias.replacement + specifier.slice(alias.pattern.length);
2043
+ if (mapped === null) continue;
2044
+ const absolute = isAbsolute(mapped) ? mapped : join$1(rootDir, mapped);
2045
+ if (existsSync(absolute)) return absolute;
2046
+ }
2047
+ return null;
2048
+ }
2049
+ /**
2050
+ * Resolve an asset specifier: host aliases, then Node resolution anchored at
2051
+ * `rootDir`, then the package root. Strict gating stays at the caller.
2052
+ */
2053
+ function resolveAssetFile(specifier, rootDir, pathAliasesJson) {
2054
+ const aliased = resolveThroughPathAliases(specifier, rootDir, pathAliasesJson);
2055
+ if (aliased) return aliased;
2056
+ const requireFromRoot = requireAnchoredAt(rootDir);
2057
+ try {
2058
+ return requireFromRoot.resolve(specifier);
2059
+ } catch {}
2060
+ const segments = specifier.split("/");
2061
+ const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
2062
+ const subpath = specifier.slice(packageName.length + 1);
2063
+ if (!subpath) return null;
2064
+ for (const modulesDir of requireFromRoot.resolve.paths(packageName) ?? []) {
2065
+ const packageRoot = join$1(modulesDir, packageName);
2066
+ if (!existsSync(join$1(packageRoot, "package.json"))) continue;
2067
+ const candidate = join$1(packageRoot, subpath);
2068
+ if (existsSync(candidate)) return candidate;
2069
+ }
2070
+ try {
2071
+ const packageRoot = packageRootFromEntry(requireFromRoot.resolve(packageName));
2072
+ if (!packageRoot) return null;
2073
+ const candidate = join$1(packageRoot, subpath);
2074
+ return existsSync(candidate) ? candidate : null;
2075
+ } catch {
2076
+ return null;
2077
+ }
2078
+ }
2079
+ //#endregion
2080
+ //#region pipeline/correlate-external-tokens.ts
2081
+ /**
2082
+ * Index the loader-captured theme manifests by owning specifier. An
2083
+ * unparseable capture throws: an empty index reads as "no source defines it".
2084
+ */
2085
+ function buildSourceTokenIndex(opts) {
2086
+ const index = /* @__PURE__ */ new Map();
2087
+ if (!opts.sourceThemeManifestsJson) return index;
2088
+ const manifests = parseInternalWire(opts.sourceThemeManifestsJson, "sourceThemeManifestsJson (the system loader's built-theme capture)");
2089
+ const realDirOwners = [];
2090
+ const addOwnerDir = (dir, specifier) => {
2091
+ if (!realDirOwners.some((entry) => entry.dir === dir)) realDirOwners.push({
2092
+ dir,
2093
+ specifier
2094
+ });
2095
+ };
2096
+ for (const [dir, specifier] of Object.entries(opts.dirOwners)) {
2097
+ let real = dir;
2098
+ try {
2099
+ real = realpathSync(dir);
2100
+ } catch {}
2101
+ addOwnerDir(real, specifier);
2102
+ let packageRoot = real;
2103
+ while (packageRoot !== dirname$1(packageRoot) && !existsSync(join$1(packageRoot, "package.json"))) packageRoot = dirname$1(packageRoot);
2104
+ if (existsSync(join$1(packageRoot, "package.json"))) addOwnerDir(packageRoot, specifier);
2105
+ }
2106
+ for (const [modulePath, exports] of Object.entries(manifests)) {
2107
+ const owner = realDirOwners.find(({ dir }) => isPathWithinRoot(dir, modulePath));
2108
+ if (!owner) continue;
2109
+ let tokens = index.get(owner.specifier);
2110
+ if (!tokens) {
2111
+ tokens = /* @__PURE__ */ new Set();
2112
+ index.set(owner.specifier, tokens);
2113
+ }
2114
+ for (const paths of Object.values(exports)) for (const token of paths) tokens.add(token);
2115
+ }
2116
+ return index;
2117
+ }
2118
+ /**
2119
+ * One message per distinct (component, token, specifier) whose source
2120
+ * defines the token. Severity routing stays at the caller.
2121
+ */
2122
+ function correlateExternalTokenDiagnostics(opts) {
2123
+ const messages = [];
2124
+ const seen = /* @__PURE__ */ new Set();
2125
+ for (const diagnostic of opts.diagnostics ?? []) {
2126
+ if (diagnostic.kind !== "external-token-candidate" || !diagnostic.token) continue;
2127
+ const specifier = opts.fileOwners[diagnostic.file];
2128
+ if (!specifier) continue;
2129
+ if (!opts.sourceTokens.get(specifier)?.has(diagnostic.token)) continue;
2130
+ const key = `${diagnostic.component}\u0000${diagnostic.token}\u0000${specifier}`;
2131
+ if (seen.has(key)) continue;
2132
+ seen.add(key);
2133
+ messages.push(`${diagnostic.component} (from '${specifier}') references token '${diagnostic.token}', which the consumer theme does not define — inherit the source's tokens with createTheme().extend(...) using the tokens (or bundle) export of '${specifier}'`);
2134
+ }
2135
+ return messages;
2136
+ }
2137
+ const indexCache = /* @__PURE__ */ new WeakMap();
2138
+ /**
2139
+ * The one cross-source token-contract gate both plugins share, so the hosts
2140
+ * cannot drift on wiring or severity.
2141
+ */
2142
+ function enforceExternalTokenContracts(opts) {
2143
+ const manifestsJson = opts.sourceThemeManifestsJson ?? "";
2144
+ const cached = indexCache.get(opts.dirOwners);
2145
+ let sourceTokens;
2146
+ if (cached && cached.manifestsJson === manifestsJson) sourceTokens = cached.index;
2147
+ else {
2148
+ sourceTokens = buildSourceTokenIndex({
2149
+ sourceThemeManifestsJson: opts.sourceThemeManifestsJson,
2150
+ dirOwners: opts.dirOwners
2151
+ });
2152
+ indexCache.set(opts.dirOwners, {
2153
+ manifestsJson,
2154
+ index: sourceTokens
2155
+ });
2156
+ }
2157
+ const messages = correlateExternalTokenDiagnostics({
2158
+ diagnostics: opts.diagnostics,
2159
+ fileOwners: opts.fileOwners,
2160
+ sourceTokens
2161
+ });
2162
+ if (messages.length === 0) return;
2163
+ if (opts.strict) throw new Error(`${opts.prefix} ${messages.join("\n")}`);
2164
+ for (const message of messages) opts.warn(message);
2165
+ }
2166
+ //#endregion
2167
+ //#region pipeline/path-aliases.ts
2168
+ function buildPathAliasesJson(pairs, rootDir) {
2169
+ const entries = [];
2170
+ for (const { pattern, target, kind } of pairs) {
2171
+ const replacement = target.startsWith(rootDir) ? target.slice(rootDir.length + 1) : target;
2172
+ if (kind ? kind === "exact" : /\.\w+$/.test(replacement)) entries.push({
2173
+ pattern,
2174
+ replacement,
2175
+ type: "exact"
2176
+ });
2177
+ else entries.push({
2178
+ pattern: pattern.endsWith("/") ? pattern : pattern + "/",
2179
+ replacement: replacement.endsWith("/") ? replacement : replacement + "/",
2180
+ type: "prefix"
2181
+ });
2182
+ }
2183
+ entries.sort((a, b) => b.pattern.length - a.pattern.length);
2184
+ if (entries.length === 0) return null;
2185
+ return {
2186
+ json: JSON.stringify({ aliases: entries }),
2187
+ count: entries.length
2188
+ };
2189
+ }
2190
+ //#endregion
2191
+ //#region pipeline/post-process-css.ts
2192
+ function resolveLightningTargets(explicitTargets, rootDir) {
2193
+ let queries;
2194
+ if (explicitTargets) queries = Array.isArray(explicitTargets) ? explicitTargets : [explicitTargets];
2195
+ else {
2196
+ const detected = browserslist(void 0, { path: rootDir });
2197
+ queries = detected.length > 0 ? detected : browserslist("defaults");
2198
+ }
2199
+ const [firstQuery] = queries;
2200
+ return browserslistToTargets(firstQuery !== void 0 && firstQuery.includes(" ") ? browserslist(queries) : queries);
2201
+ }
2202
+ function postProcessCss(css, opts) {
2203
+ if (!css) return css;
2204
+ try {
2205
+ return transform({
2206
+ filename: "animus-extracted.css",
2207
+ code: Buffer.from(css),
2208
+ minify: opts.minify,
2209
+ targets: opts.targets
2210
+ }).code.toString();
2211
+ } catch (e) {
2212
+ const msg = e instanceof Error ? e.message : String(e);
2213
+ (opts.warnFn ?? console.warn)(`[animus] Lightning CSS post-processing failed: ${msg}`);
2214
+ return css;
2215
+ }
2216
+ }
2217
+ //#endregion
2218
+ //#region pipeline/manifest-diagnostics.ts
2219
+ /** Stable code for selector forms with no substitutable subject; the
2220
+ * extraction engine mints the same string. */
2221
+ const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
2222
+ /** The one matcher for the engine's unresolved-parent chain-drop message;
2223
+ * consumers match through the exported helpers, never their own copy. */
2224
+ const UNRESOLVED_PARENT_RE = /chain dropped: could not resolve parent component '([^']+)'/;
2225
+ function isUnresolvedParentDrop(diagnostic) {
2226
+ return UNRESOLVED_PARENT_RE.test(diagnostic.message);
2227
+ }
2228
+ function unresolvedParentName(diagnostic) {
2229
+ return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
2230
+ }
2231
+ /** True when the selector carries at least one substitutable `&` subject
2232
+ * outside quoted text. */
2233
+ function hasSelectorSubject(value) {
2234
+ let quote = null;
2235
+ let escaped = false;
2236
+ for (const c of value) {
2237
+ if (escaped) {
2238
+ escaped = false;
2239
+ continue;
2240
+ }
2241
+ if (c === "\\") {
2242
+ escaped = true;
2243
+ continue;
2244
+ }
2245
+ if (quote !== null) {
2246
+ if (c === quote) quote = null;
2247
+ } else if (c === "\"" || c === "'") quote = c;
2248
+ else if (c === "&") return true;
2249
+ }
2250
+ return false;
2251
+ }
2252
+ /**
2253
+ * Coded diagnostics for selector-shaped alias values with no substitutable
2254
+ * subject; alias values never reach the evaluator's own key guard.
2255
+ */
2256
+ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
2257
+ if (!selectorAliasesJson) return [];
2258
+ const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
2259
+ const diagnostics = [];
2260
+ for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
2261
+ file: "system",
2262
+ component: name,
2263
+ kind: "warn",
2264
+ message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
2265
+ code: SELECTOR_UNSUPPORTED_SUBJECT,
2266
+ severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
2267
+ });
2268
+ return diagnostics;
2269
+ }
2270
+ const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
2271
+ /**
2272
+ * A configured source file that could not be read is a LOST INPUT: the
2273
+ * emitted artifacts silently lack whatever that file declared.
2274
+ */
2275
+ function unreadableSourceDiagnostic(relPath, error) {
2276
+ return {
2277
+ file: relPath,
2278
+ component: "source",
2279
+ kind: "warn",
2280
+ message: `configured source file ${relPath} could not be read and was skipped: ${String(error)}`,
2281
+ code: UNREADABLE_SOURCE_FILE,
2282
+ severity: severityFor(UNREADABLE_SOURCE_FILE)
2283
+ };
2284
+ }
2285
+ /** The collision entry code minted by the system package's merge. */
2286
+ const VOCABULARY_COLLISION = "animus.vocabulary.collision";
2287
+ /** Minted when a sealed kit with registered vocabulary is consumed through
2288
+ * the deprecated `from()`/`includes:` verbs. */
2289
+ const VOCABULARY_LEGACY_VERB = "animus.vocabulary.legacy-verb";
2290
+ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
2291
+ [SELECTOR_UNSUPPORTED_SUBJECT, "error"],
2292
+ [UNREADABLE_SOURCE_FILE, "error"],
2293
+ [VOCABULARY_COLLISION, "warn"],
2294
+ [VOCABULARY_LEGACY_VERB, "warn"]
2295
+ ]);
2296
+ /** An unlisted code is `warn`: a witness kind from a newer system package
2297
+ * must not fail the strict build of a host that predates its writer. */
2298
+ function severityFor(code) {
2299
+ return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
2300
+ }
2301
+ /**
2302
+ * The one mapper of a sealed system's witness entries for every host: the
2303
+ * loader's evaluation host shims `console`, so the record is the channel.
2304
+ */
2305
+ function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
2306
+ if (!vocabularyWitnessesJson) return [];
2307
+ const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
2308
+ const diagnostics = [];
2309
+ for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
2310
+ file: "system",
2311
+ component: entry.name ?? "keyframes",
2312
+ kind: "warn",
2313
+ message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
2314
+ code: entry.code,
2315
+ severity: severityFor(entry.code)
2316
+ });
2317
+ else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
2318
+ file: "system",
2319
+ component: "keyframes",
2320
+ kind: "warn",
2321
+ message: `a sealed system (${entry.source ?? `'${entry.verb}' source`}) with registered vocabulary [${(entry.names ?? []).join(", ")}] was consumed through the deprecated '${entry.verb}' verb, which cannot carry it — those collections do NOT reach this consumer; use createSystem().extend(source) (${entry.code})`,
2322
+ code: entry.code,
2323
+ severity: severityFor(entry.code)
2324
+ });
2325
+ else diagnostics.push({
2326
+ file: "system",
2327
+ component: "vocabulary",
2328
+ kind: "warn",
2329
+ message: `unrecognized vocabulary witness entry ${JSON.stringify(entry)} — a newer @animus-ui/system may have recorded a witness kind this host predates${entry.code ? ` (${entry.code})` : ""}`,
2330
+ code: entry.code,
2331
+ severity: severityFor(entry.code)
2332
+ });
2333
+ return diagnostics;
2334
+ }
2335
+ /**
2336
+ * The single strict-escalation policy point for both extraction plugins:
2337
+ * error-severity diagnostics throw together under `strict`, else warn.
2338
+ */
2339
+ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
2340
+ const errors = [];
2341
+ const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
2342
+ for (const diagnostic of diagnostics) {
2343
+ let line = null;
2344
+ if (diagnostic.kind === "bail") line = `⚠ ${diagnostic.component} not extracted: ${diagnostic.message}`;
2345
+ else if (diagnostic.kind === "skip") line = `⚠ ${diagnostic.component}: skipped ${diagnostic.message}`;
2346
+ else if (diagnostic.kind === "warn") line = `⚠ ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`;
2347
+ if (line === null) continue;
2348
+ if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
2349
+ if (policy.strict && diagnostic.severity === "error") {
2350
+ errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${diagnostic.message}`);
2351
+ continue;
2352
+ }
2353
+ warn(line);
2354
+ }
2355
+ if (errors.length > 0) throw new Error(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
2356
+ }
2357
+ //#endregion
2358
+ //#region pipeline/unit-fallback.ts
2359
+ function applyUnitFallback(css) {
2360
+ return css.replace(/([a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
2361
+ if (isUnitlessProperty(prop)) return match;
2362
+ let depth = 0;
2363
+ let fixed = "";
2364
+ let i = 0;
2365
+ while (i < value.length) if (value[i] === "(") {
2366
+ depth++;
2367
+ fixed += value[i];
2368
+ i++;
2369
+ } else if (value[i] === ")") {
2370
+ depth--;
2371
+ fixed += value[i];
2372
+ i++;
2373
+ } else if (depth > 0) {
2374
+ fixed += value[i];
2375
+ i++;
2376
+ } else {
2377
+ const numMatch = value.slice(i).match(/^(-?\d+\.?\d*)/);
2378
+ if (numMatch) {
2379
+ const num = numMatch[1];
2380
+ const after = value[i + num.length];
2381
+ if (after && /[a-z%]/i.test(after)) fixed += num;
2382
+ else fixed += num + "px";
2383
+ i += num.length;
2384
+ } else {
2385
+ fixed += value[i];
2386
+ i++;
2387
+ }
2388
+ }
2389
+ return fixed !== value ? `${prop}:${fixed};` : match;
2390
+ });
2391
+ }
2392
+ //#endregion
2393
+ //#region pipeline/run-analysis.ts
2394
+ /** The named `analyzeProject` input set, and the persistence shape an
2395
+ * isolated process replays the analysis from. */
2396
+ function buildAnalysisInputs(opts) {
2397
+ const emitterConfig = {
2398
+ runtime_import: opts.emitter.runtimeImport,
2399
+ css_module_id: opts.emitter.cssModuleId
2400
+ };
2401
+ if (opts.emitter.systemPropsModuleId) emitterConfig.system_props_module_id = opts.emitter.systemPropsModuleId;
2402
+ return {
2403
+ filesJson: JSON.stringify(opts.fileEntries),
2404
+ scalesJson: opts.system.scalesJson,
2405
+ variableMapJson: opts.system.variableMapJson,
2406
+ contextualVarsJson: opts.system.contextualVarsJson,
2407
+ propConfigJson: opts.system.propConfigJson,
2408
+ groupRegistryJson: opts.system.groupRegistryJson,
2409
+ packageResolutionJson: JSON.stringify(opts.packageMap),
2410
+ devMode: opts.devMode,
2411
+ emitterConfigJson: JSON.stringify(emitterConfig),
2412
+ selectorAliasesJson: opts.system.selectorAliasesJson,
2413
+ globalStyleBlocksJson: opts.system.globalStyleBlocksJson,
2414
+ pathAliasesJson: opts.pathAliasesJson,
2415
+ keyframesJson: opts.system.keyframesJson,
2416
+ staticCssJson: opts.staticCssJson ?? null,
2417
+ conditionAliasesJson: opts.system.conditionAliasesJson ?? null,
2418
+ transformSourcesJson: opts.system.transformSourcesJson ?? null,
2419
+ externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
2420
+ };
2421
+ }
2422
+ function hasSourceThemeManifests(system) {
2423
+ const json = system.sourceThemeManifestsJson ?? "";
2424
+ return json.length > 0 && json !== "{}";
2425
+ }
2426
+ /**
2427
+ * The one analysis invocation both plugins share. Error handling stays at
2428
+ * the call site (strict-mode throw vs warn).
2429
+ */
2430
+ function runProjectAnalysis(engineApi, opts) {
2431
+ const { analyzeProject } = engineApi();
2432
+ let t = performance.now();
2433
+ const inputs = buildAnalysisInputs(opts);
2434
+ const serializeMs = Math.round(performance.now() - t);
2435
+ t = performance.now();
2436
+ const manifestJson = analyzeProject(...buildAnalyzeProjectArgs(inputs));
2437
+ const extractMs = Math.round(performance.now() - t);
2438
+ t = performance.now();
2439
+ const manifest = JSON.parse(manifestJson);
2440
+ surfaceManifestDiagnostics(manifest, opts.warn, {
2441
+ strict: opts.strict,
2442
+ prepend: [...collectSelectorAliasDiagnostics(opts.system.selectorAliasesJson), ...opts.extraDiagnostics ?? []]
2443
+ });
2444
+ const parseMs = Math.round(performance.now() - t);
2445
+ return {
2446
+ manifest,
2447
+ manifestJson,
2448
+ globalCss: manifest.sheets.global,
2449
+ componentCss: applyUnitFallback(manifest.css),
2450
+ inputs,
2451
+ timings: {
2452
+ serializeMs,
2453
+ extractMs,
2454
+ parseMs
2455
+ }
2456
+ };
2457
+ }
2458
+ /**
2459
+ * Clear the engine's per-file analysis cache so stale results never bleed
2460
+ * into a fresh run. Engines without the capability are tolerated.
2461
+ */
2462
+ function clearEngineCache(engineApi) {
2463
+ try {
2464
+ const { clearAnalysisCache } = engineApi();
2465
+ clearAnalysisCache();
2466
+ } catch {}
2467
+ }
2468
+ //#endregion
2469
+ //#region pipeline/utils.ts
2470
+ function camelToKebab(str) {
2471
+ return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
2472
+ }
2473
+ function stableStringify(value) {
2474
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
2475
+ if (isKeyedReference(value)) return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
2476
+ return JSON.stringify(value);
2477
+ }
2478
+ /** Decided by an intrinsic only functions accept, so a spoofed
2479
+ * `Symbol.toStringTag` cannot pass a plain object off as a function. */
2480
+ function isCallable(value) {
2481
+ try {
2482
+ Function.prototype.toString.call(value);
2483
+ return true;
2484
+ } catch {
2485
+ return false;
2486
+ }
2487
+ }
2488
+ function isKeyedReference(value) {
2489
+ return Object(value) === value && !isCallable(value);
2490
+ }
2491
+ //#endregion
2492
+ //#region pipeline/static-css.ts
2493
+ /** The `staticCss` option declares emission the JSX scanner cannot observe
2494
+ * (config-driven variants, spread props); the engine applies it as usage. */
2495
+ function serializeStaticCss(config) {
2496
+ if (!config) return null;
2497
+ const json = stableStringify(config);
2498
+ return json === "{}" ? null : json;
2499
+ }
2500
+ //#endregion
2501
+ //#region pipeline/prefix.ts
2502
+ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson) {
2503
+ if (!prefix) return {
2504
+ variableMapJson,
2505
+ variableCss,
2506
+ themeJson,
2507
+ contextualVarsJson
2508
+ };
2509
+ const varRefRe = /var\(--([a-zA-Z][\w-]*)\)/g;
2510
+ const map = JSON.parse(variableMapJson);
2511
+ const prefixed = {};
2512
+ for (const [key, varName] of Object.entries(map)) prefixed[key] = varName.startsWith("--") ? `--${prefix}-${varName.slice(2)}` : varName;
2513
+ let css = variableCss;
2514
+ css = css.replace(/--([a-zA-Z][\w-]*)\s*:/g, `--${prefix}-$1:`);
2515
+ css = css.replace(varRefRe, `var(--${prefix}-$1)`);
2516
+ const result = {
2517
+ variableMapJson: JSON.stringify(prefixed),
2518
+ variableCss: css
2519
+ };
2520
+ if (themeJson) result.themeJson = themeJson.replace(varRefRe, `var(--${prefix}-$1)`);
2521
+ if (contextualVarsJson) {
2522
+ const ctxVars = JSON.parse(contextualVarsJson);
2523
+ const prefixedCtx = {};
2524
+ for (const [scale, names] of Object.entries(ctxVars)) prefixedCtx[scale] = names.map((name) => `${prefix}-${name}`);
2525
+ result.contextualVarsJson = JSON.stringify(prefixedCtx);
2526
+ }
2527
+ return result;
2528
+ }
2529
+ //#endregion
2530
+ //#region pipeline/system-config.ts
2531
+ /**
2532
+ * Load and normalize a SystemInstance; `prefix` namespaces every CSS
2533
+ * variable name. Error handling stays at the call site.
2534
+ */
2535
+ function loadSystemConfig(engineApi, opts) {
2536
+ const { loadSystemModule } = engineApi();
2537
+ const config = loadSystemModule(opts.systemPath, opts.rootDir);
2538
+ let scalesJson = config.scalesJson;
2539
+ let variableMapJson = config.variableMapJson;
2540
+ let variableCss = config.variableCss;
2541
+ let contextualVarsJson = config.contextualVarsJson || null;
2542
+ if (opts.prefix) {
2543
+ const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
2544
+ variableMapJson = prefixed.variableMapJson;
2545
+ variableCss = prefixed.variableCss;
2546
+ if (prefixed.themeJson) scalesJson = prefixed.themeJson;
2547
+ if (prefixed.contextualVarsJson) contextualVarsJson = prefixed.contextualVarsJson;
2548
+ }
2549
+ return {
2550
+ propConfigJson: config.propConfig,
2551
+ groupRegistryJson: config.groupRegistry,
2552
+ scalesJson,
2553
+ variableMapJson,
2554
+ variableCss,
2555
+ contextualVarsJson,
2556
+ selectorAliasesJson: config.selectorAliases || null,
2557
+ conditionAliasesJson: config.conditionAliases || null,
2558
+ transformSourcesJson: config.transformSources || null,
2559
+ globalStyleBlocksJson: config.globalStyleBlocks || null,
2560
+ keyframesJson: config.keyframesBlocks || null,
2561
+ vocabularyWitnessesJson: config.vocabularyWitnesses || null,
2562
+ dependencies: config.dependencies ?? [],
2563
+ sourceThemeManifestsJson: config.sourceThemeManifests || null
2564
+ };
2565
+ }
2566
+ //#endregion
2567
+ //#region pipeline/structural-self-check.ts
2568
+ function runStructuralSelfCheck(input) {
2569
+ const failures = [];
2570
+ if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
2571
+ else if (input.assembledCss !== void 0 ? !/@layer\s+anm-base\s*\{/.test(input.assembledCss) : input.componentCss.trim().length === 0) failures.push(`${input.componentCount} component(s) discovered but the emitted component CSS is empty`);
2572
+ for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources`);
2573
+ else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
2574
+ if (!input.variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
2575
+ if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
2576
+ if (input.componentCount > 0 && (input.componentCss.length > 0 || input.assembledCss !== void 0)) {
2577
+ const assembled = input.assembledCss ?? assembleStylesheet({
2578
+ layers: input.layers,
2579
+ variableCss: input.variableCss,
2580
+ globalCss: input.globalCss,
2581
+ componentCss: input.componentCss
2582
+ });
2583
+ const baseIdx = assembled.search(/@layer\s+anm-base\s*\{/);
2584
+ const variantsIdx = assembled.search(/@layer\s+anm-variants\s*\{/);
2585
+ if (baseIdx !== -1 && variantsIdx !== -1 && baseIdx >= variantsIdx) failures.push(`CSS layer ordering violated — @layer anm-base (offset ${baseIdx}) must precede @layer anm-variants (offset ${variantsIdx})`);
2586
+ }
2587
+ return failures;
2588
+ }
2589
+ //#endregion
2590
+ //#region pipeline/dynamic-prop-config.ts
2591
+ function buildDynamicPropConfig(dynamicProps) {
2592
+ const configEntries = {};
2593
+ for (const [propName, meta] of Object.entries(dynamicProps)) {
2594
+ if (!meta.varName || !meta.slotClass) throw new Error(`buildDynamicPropConfig: dynamic prop '${propName}' carries no slot metadata — expected varName and slotClass, got keys [${Object.keys(meta).join(", ")}].`);
2595
+ const entry = {
2596
+ varName: meta.varName,
2597
+ slotClass: meta.slotClass
2598
+ };
2599
+ if (meta.property) entry.property = meta.property;
2600
+ if (meta.properties && meta.properties.length > 0) entry.properties = meta.properties;
2601
+ if (meta.transformName) entry.transformName = meta.transformName;
2602
+ if (meta.scaleValues && Object.keys(meta.scaleValues).length > 0) entry.scaleValues = meta.scaleValues;
2603
+ configEntries[propName] = entry;
2604
+ }
2605
+ return configEntries;
2606
+ }
2607
+ //#endregion
2608
+ //#region pipeline/system-props-module.ts
2609
+ function buildSystemPropsModule(opts) {
2610
+ const dynamicPropConfig = buildDynamicPropConfig(opts.dynamicProps);
2611
+ return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${opts.transformsSource ?? "{}"};\n`;
2612
+ }
2613
+ //#endregion
2614
+ //#region pipeline/timing-waterfall.ts
2615
+ const RUST_TIMING_PHASES = [
2616
+ ["parseAndWalk", "parse+walk"],
2617
+ ["importResolution", "imports"],
2618
+ ["extensionProvenance", "provenance"],
2619
+ ["topologicalSort", "topo-sort"],
2620
+ ["chainEvaluation", "chains"],
2621
+ ["jsxScanning", "jsx-scan"],
2622
+ ["systemPropAggregation", "sys-props"],
2623
+ ["usageLedger", "usage"],
2624
+ ["reconciliation", "reconcile"],
2625
+ ["cssGeneration", "css-gen"],
2626
+ ["manifestSerialization", "serialize"]
2627
+ ];
2628
+ function formatRustTimingWaterfall(timing, opts) {
2629
+ const lines = [];
2630
+ for (const [key, label] of RUST_TIMING_PHASES) {
2631
+ const ms = timing[key] ?? 0;
2632
+ const pad = " ".repeat(Math.max(0, opts.labelWidth - label.length));
2633
+ const extra = key === "parseAndWalk" ? ` (${timing.fileCount ?? 0} files, ${timing.cacheHits ?? 0} cached)` : "";
2634
+ lines.push(`${opts.indent}${label}${pad}${String(ms).padStart(5)}ms${extra}`);
2635
+ }
2636
+ return lines;
2637
+ }
2638
+ //#endregion
2639
+ //#region pipeline/tsconfig-paths.ts
2640
+ /**
2641
+ * tsconfig `paths` → alias pairs, for drivers with no live bundler config.
2642
+ * The nearest config declaring `paths` wins wholesale — TS never merges.
2643
+ */
2644
+ function stripJsonc(text) {
2645
+ let out = "";
2646
+ let inString = false;
2647
+ let inLine = false;
2648
+ let inBlock = false;
2649
+ for (let i = 0; i < text.length; i++) {
2650
+ const ch = text[i];
2651
+ const next = text[i + 1];
2652
+ if (inLine) {
2653
+ if (ch === "\n") {
2654
+ inLine = false;
2655
+ out += ch;
2656
+ }
2657
+ continue;
2658
+ }
2659
+ if (inBlock) {
2660
+ if (ch === "*" && next === "/") {
2661
+ inBlock = false;
2662
+ i++;
2663
+ }
2664
+ continue;
2665
+ }
2666
+ if (inString) {
2667
+ out += ch;
2668
+ if (ch === "\\") {
2669
+ out += next ?? "";
2670
+ i++;
2671
+ } else if (ch === "\"") inString = false;
2672
+ continue;
2673
+ }
2674
+ if (ch === "\"") {
2675
+ inString = true;
2676
+ out += ch;
2677
+ continue;
2678
+ }
2679
+ if (ch === "/" && next === "/") {
2680
+ inLine = true;
2681
+ i++;
2682
+ continue;
2683
+ }
2684
+ if (ch === "/" && next === "*") {
2685
+ inBlock = true;
2686
+ i++;
2687
+ continue;
2688
+ }
2689
+ out += ch;
2690
+ }
2691
+ return out.replace(/,(\s*[}\]])/g, "$1");
2692
+ }
2693
+ /** `Object(value) === value` holds for exactly the blocks and lists
2694
+ * `JSON.parse` produces; the `[object Object]` tag separates the two. */
2695
+ function isJsonBlock(value) {
2696
+ return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
2697
+ }
2698
+ /** A block or a list. A declared `paths` OWNS the setting and blocks its
2699
+ * parents' even when it contributes nothing usable. */
2700
+ function isJsonKeyed(value) {
2701
+ return Object(value) === value;
2702
+ }
2703
+ /** A JSON string, excluding the boxed `String` object, which no path join
2704
+ * accepts. */
2705
+ function isJsonString(value) {
2706
+ return Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
2707
+ }
2708
+ function decodePaths(value) {
2709
+ if (!isJsonKeyed(value)) return null;
2710
+ const decoded = /* @__PURE__ */ new Map();
2711
+ for (const [pattern, targets] of Object.entries(value)) {
2712
+ const [first] = Array.isArray(targets) ? targets : [];
2713
+ if (isJsonString(first)) decoded.set(pattern, first);
2714
+ }
2715
+ return decoded;
2716
+ }
2717
+ /** TypeScript accepts one specifier or an array; a non-string member names
2718
+ * no config and is dropped here. */
2719
+ function decodeExtends(value) {
2720
+ return (Array.isArray(value) ? value : [value]).filter(isJsonString);
2721
+ }
2722
+ function readConfig(path) {
2723
+ let raw;
2724
+ try {
2725
+ raw = readFileSync(path, "utf-8");
2726
+ } catch {
2727
+ return null;
2728
+ }
2729
+ let parsed;
2730
+ try {
2731
+ parsed = JSON.parse(stripJsonc(raw));
2732
+ } catch {
2733
+ return null;
2734
+ }
2735
+ const root = isJsonBlock(parsed) ? parsed : {};
2736
+ const compilerOptions = isJsonBlock(root.compilerOptions) ? root.compilerOptions : {};
2737
+ return {
2738
+ dir: dirname$1(path),
2739
+ baseUrl: isJsonString(compilerOptions.baseUrl) ? compilerOptions.baseUrl : null,
2740
+ paths: decodePaths(compilerOptions.paths),
2741
+ extends: decodeExtends(root.extends)
2742
+ };
2743
+ }
2744
+ function resolveExtendsTarget(specifier, fromDir) {
2745
+ if (specifier.startsWith(".") || isAbsolute(specifier)) {
2746
+ const base = resolve(fromDir, specifier);
2747
+ for (const candidate of [
2748
+ base,
2749
+ `${base}.json`,
2750
+ join$1(base, "tsconfig.json")
2751
+ ]) if (existsSync(candidate)) return candidate;
2752
+ return null;
2753
+ }
2754
+ try {
2755
+ return __require.resolve(specifier.endsWith(".json") ? specifier : `${specifier}/tsconfig.json`, { paths: [fromDir] });
2756
+ } catch {
2757
+ try {
2758
+ return __require.resolve(specifier, { paths: [fromDir] });
2759
+ } catch {
2760
+ return null;
2761
+ }
2762
+ }
2763
+ }
2764
+ /** Child-first flattened extends chain starting at `entryPath`. */
2765
+ function loadChain(entryPath) {
2766
+ const chain = [];
2767
+ const visited = /* @__PURE__ */ new Set();
2768
+ const queue = [entryPath];
2769
+ while (queue.length > 0) {
2770
+ const path = queue.shift();
2771
+ const key = resolve(path);
2772
+ if (visited.has(key)) continue;
2773
+ visited.add(key);
2774
+ const node = readConfig(key);
2775
+ if (!node) continue;
2776
+ chain.push(node);
2777
+ for (const parent of node.extends) {
2778
+ const resolved = resolveExtendsTarget(parent, node.dir);
2779
+ if (resolved) queue.push(resolved);
2780
+ }
2781
+ }
2782
+ return chain;
2783
+ }
2784
+ /**
2785
+ * The project's tsconfig path aliases as `buildPathAliasesJson` pairs; empty
2786
+ * when no readable tsconfig, or no usable `paths`, exists.
2787
+ */
2788
+ function readTsconfigAliasPairs(rootDir) {
2789
+ const chain = loadChain(join$1(rootDir, "tsconfig.json"));
2790
+ if (chain.length === 0) return [];
2791
+ const pathsOwner = chain.find((node) => node.paths !== null);
2792
+ const paths = pathsOwner?.paths ?? null;
2793
+ if (pathsOwner === void 0 || paths === null) return [];
2794
+ const baseOwner = chain.find((node) => node.baseUrl !== null);
2795
+ const baseUrl = baseOwner?.baseUrl ?? null;
2796
+ const base = baseOwner !== void 0 && baseUrl !== null ? resolve(baseOwner.dir, baseUrl) : pathsOwner.dir;
2797
+ const pairs = [];
2798
+ for (const [pattern, target] of paths) {
2799
+ const patternStars = pattern.split("*").length - 1;
2800
+ const targetStars = target.split("*").length - 1;
2801
+ if (pattern === "*" || patternStars > 1 || targetStars > 1) continue;
2802
+ if (pattern.endsWith("/*")) {
2803
+ if (!target.endsWith("/*")) continue;
2804
+ pairs.push({
2805
+ pattern: pattern.slice(0, -2),
2806
+ target: resolve(base, target.slice(0, -2)),
2807
+ kind: "prefix"
2808
+ });
2809
+ continue;
2810
+ }
2811
+ if (patternStars > 0 || targetStars > 0) continue;
2812
+ pairs.push({
2813
+ pattern,
2814
+ target: resolve(base, target),
2815
+ kind: "exact"
2816
+ });
2817
+ }
2818
+ return pairs;
2819
+ }
2820
+ //#endregion
2821
+ //#region pipeline/error-diagnostics.ts
2822
+ function assertNoErrorDiagnostics(diagnostics) {
2823
+ const errors = (diagnostics ?? []).filter((diagnostic) => diagnostic.kind === "error");
2824
+ if (errors.length === 0) return;
2825
+ const lines = [...new Set(errors.map((diagnostic) => `[animus] ${diagnostic.component || "<unknown component>"} (${diagnostic.file || "<unknown file>"}): ${diagnostic.message || "<no message>"}`))];
2826
+ throw new Error(lines.join("\n"));
2827
+ }
2828
+ //#endregion
2829
+ //#region pipeline/replacement-plans.ts
2830
+ const snapshotByManifest = /* @__PURE__ */ new WeakMap();
2831
+ /** `null` (a context before its first analysis) snapshots to the empty plan
2832
+ * set — the same state as an empty universe. */
2833
+ function snapshotFilePlans(manifest) {
2834
+ if (manifest === null) return /* @__PURE__ */ new Map();
2835
+ const cached = snapshotByManifest.get(manifest);
2836
+ if (cached) return cached;
2837
+ const entriesByFile = /* @__PURE__ */ new Map();
2838
+ for (const [id, desc] of Object.entries(manifest.components)) {
2839
+ const list = entriesByFile.get(desc.file) ?? [];
2840
+ list.push([id, desc.replacement]);
2841
+ entriesByFile.set(desc.file, list);
2842
+ }
2843
+ const snapshot = /* @__PURE__ */ new Map();
2844
+ for (const [file, entries] of entriesByFile) {
2845
+ entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
2846
+ snapshot.set(file, JSON.stringify(entries));
2847
+ }
2848
+ snapshotByManifest.set(manifest, snapshot);
2849
+ return snapshot;
2850
+ }
2851
+ /**
2852
+ * Paths whose plan changed: absent↔present, membership, or content.
2853
+ * `exclude` drops one path whose own modules are already being updated.
2854
+ */
2855
+ function diffFilePlans(prev, next, opts) {
2856
+ const changed = /* @__PURE__ */ new Set();
2857
+ for (const [file, plan] of next) if (prev.get(file) !== plan) changed.add(file);
2858
+ for (const file of prev.keys()) if (!next.has(file)) changed.add(file);
2859
+ if (opts?.exclude) changed.delete(opts.exclude);
2860
+ return [...changed].sort();
2861
+ }
2862
+ /** Domain prefix keeping this hash disjoint from every other `contentHash`
2863
+ * use in the pipeline. */
2864
+ const REPLACEMENT_PLANS_DOMAIN = "animus-replacement-plans-v1\0";
2865
+ /** Leads with NUL: entries end with `\0` and contain none, so `\0\0` is
2866
+ * unreachable from entries alone and the section cannot be forged. */
2867
+ const SERVED_DEPENDENCY_DOMAIN = "\0animus-served-dependency-v1\0";
2868
+ /**
2869
+ * THE replacement epoch: integrations consume this hash, never their own.
2870
+ * `servedDependencyWitness` folds in content the plans cannot see.
2871
+ */
2872
+ function hashReplacementPlans(snapshot, servedDependencyWitness) {
2873
+ const entries = [...snapshot.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
2874
+ let serialized = REPLACEMENT_PLANS_DOMAIN;
2875
+ for (const [file, plan] of entries) serialized += file + "\0" + plan + "\0";
2876
+ if (servedDependencyWitness !== void 0) serialized += SERVED_DEPENDENCY_DOMAIN + servedDependencyWitness;
2877
+ return contentHash(serialized);
2878
+ }
2879
+ //#endregion
2880
+ //#region pipeline/watch-keys.ts
2881
+ function toWatchKeys(input) {
2882
+ const lexical = normalize(input);
2883
+ const canonical = realpathThroughNearestExistingAncestor(lexical);
2884
+ return canonical === lexical ? [lexical] : [lexical, canonical];
2885
+ }
2886
+ /** Canonicalizes a path that may not exist, through its nearest existing
2887
+ * ancestor. A symlink retargeted after load fails open: no match. */
2888
+ function realpathThroughNearestExistingAncestor(path) {
2889
+ try {
2890
+ return realpathSync$1(path);
2891
+ } catch {}
2892
+ const suffix = [];
2893
+ let current = path;
2894
+ for (;;) {
2895
+ const parent = dirname(current);
2896
+ if (parent === current) return path;
2897
+ suffix.unshift(basename(current));
2898
+ current = parent;
2899
+ try {
2900
+ return join(realpathSync$1(current), ...suffix);
2901
+ } catch {}
2902
+ }
2903
+ }
2904
+ //#endregion
2905
+ export { DEFAULT_EXCLUDE as $, enforceExternalTokenContracts as A, firstOwners as B, surfaceManifestDiagnostics as C, validateLayerOrder as Ct, postProcessCss as D, vocabularyWitnessDiagnostics as E, createSourceCorpus as F, walkPackageSources as G, resolveAbsolutePathSpecifier as H, collectExternalPackageSources as I, sharesVolumeRoot as J, createSourceIdentity as K, excludeCollectedPackages as L, resolveThroughPathAliases as M, findAssetSpecifiers as N, resolveLightningTargets as O, substituteAssetPlaceholders as P, CORE_OPTION_KEYS as Q, extractSystemFilePackages as R, isUnresolvedParentDrop as S, stripLeadingLayerDeclaration as St, unresolvedParentName as T, staleDistIncludesMessage as U, isExcludedPackageRelativePath as V, unresolvableIncludesMessage as W, discoverFiles as X, compareDiscoveryOrder as Y, AnimusConfigError as Z, clearEngineCache as _, preprocessMdx as _t, assertNoErrorDiagnostics as a, resolveMode as at, VOCABULARY_COLLISION as b, ANIMUS_LAYERS as bt, buildSystemPropsModule as c, createV2EngineApi as ct, loadSystemConfig as d, parseFilesJson as dt, DRIVER_NAMESPACES as et, applyPrefix as f, projectExternalFileOwners as ft, buildAnalysisInputs as g, isEngineTransformExtension as gt, stableStringify as h, ENGINE_TRANSFORM_EXTENSIONS as ht, snapshotFilePlans as i, createExcludeMatcher as it, resolveAssetFile as j, buildPathAliasesJson as k, buildDynamicPropConfig as l, ingestSourceEntries as lt, camelToKebab as m, DEFAULT_EXTENSIONS as mt, diffFilePlans as n, STRUCTURAL_EXCLUDE as nt, readTsconfigAliasPairs as o, RETIRED_ENGINE_MESSAGE as ot, serializeStaticCss as p, adaptSvelteSource as pt, isPathWithinRoot as q, hashReplacementPlans as r, assertKnownOptionKeys as rt, formatRustTimingWaterfall as s, assertNoRetiredEngineSelection as st, toWatchKeys as t, REPLACEABLE_DEFAULT_EXCLUDE as tt, runStructuralSelfCheck as u, isAdvisorySourceDiagnostic as ut, runProjectAnalysis as v, contentHash as vt, unreadableSourceDiagnostic as w, __require as wt, VOCABULARY_LEGACY_VERB as x, assembleStylesheet as xt, applyUnitFallback as y, buildAnalyzeProjectArgs as yt, findPackageRoot as z };