@animus-ui/extract 0.1.24 → 0.1.26

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 (83) hide show
  1. package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
  2. package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
  3. package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
  4. package/crates/extract-v2/index.d.ts +107 -6
  5. package/dist/index.cjs +48 -5
  6. package/dist/index.mjs +2 -2
  7. package/dist/pipeline/alias-agreement.d.ts +107 -0
  8. package/dist/pipeline/alias-agreement.d.ts.map +1 -0
  9. package/dist/pipeline/analyze-project-args.d.ts +11 -2
  10. package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
  11. package/dist/pipeline/asset-copies.d.ts +20 -0
  12. package/dist/pipeline/asset-copies.d.ts.map +1 -0
  13. package/dist/pipeline/built-in-asset-types.d.ts +9 -0
  14. package/dist/pipeline/built-in-asset-types.d.ts.map +1 -0
  15. package/dist/pipeline/core-options.d.ts +31 -7
  16. package/dist/pipeline/core-options.d.ts.map +1 -1
  17. package/dist/pipeline/discover-packages.d.ts +98 -12
  18. package/dist/pipeline/discover-packages.d.ts.map +1 -1
  19. package/dist/pipeline/engine-adapter.d.ts +41 -3
  20. package/dist/pipeline/engine-adapter.d.ts.map +1 -1
  21. package/dist/pipeline/engine-retirement.d.ts +7 -1
  22. package/dist/pipeline/engine-retirement.d.ts.map +1 -1
  23. package/dist/pipeline/extension-policy.d.ts +95 -0
  24. package/dist/pipeline/extension-policy.d.ts.map +1 -0
  25. package/dist/pipeline/host-assets.d.ts +32 -0
  26. package/dist/pipeline/host-assets.d.ts.map +1 -0
  27. package/dist/pipeline/host-expansions.d.ts +92 -0
  28. package/dist/pipeline/host-expansions.d.ts.map +1 -0
  29. package/dist/pipeline/host-extensions.d.ts +64 -0
  30. package/dist/pipeline/host-extensions.d.ts.map +1 -0
  31. package/dist/pipeline/host-package-resolver.d.ts +40 -0
  32. package/dist/pipeline/host-package-resolver.d.ts.map +1 -0
  33. package/dist/pipeline/index.d.ts +32 -8
  34. package/dist/pipeline/index.d.ts.map +1 -1
  35. package/dist/pipeline/kit-publication.d.ts.map +1 -1
  36. package/dist/pipeline/manifest-diagnostics.d.ts +27 -6
  37. package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
  38. package/dist/pipeline/manifest-schema.d.ts +16 -0
  39. package/dist/pipeline/manifest-schema.d.ts.map +1 -1
  40. package/dist/pipeline/output-targets.d.ts +52 -0
  41. package/dist/pipeline/output-targets.d.ts.map +1 -0
  42. package/dist/pipeline/probe-evidence.d.ts +58 -0
  43. package/dist/pipeline/probe-evidence.d.ts.map +1 -0
  44. package/dist/pipeline/run-analysis.d.ts +26 -0
  45. package/dist/pipeline/run-analysis.d.ts.map +1 -1
  46. package/dist/pipeline/runtime-import.d.ts +10 -0
  47. package/dist/pipeline/runtime-import.d.ts.map +1 -0
  48. package/dist/pipeline/served-files.d.ts +16 -0
  49. package/dist/pipeline/served-files.d.ts.map +1 -0
  50. package/dist/pipeline/source-corpus.d.ts +4 -0
  51. package/dist/pipeline/source-corpus.d.ts.map +1 -1
  52. package/dist/pipeline/source-ingestion.d.ts +37 -5
  53. package/dist/pipeline/source-ingestion.d.ts.map +1 -1
  54. package/dist/pipeline/system-config.d.ts +18 -0
  55. package/dist/pipeline/system-config.d.ts.map +1 -1
  56. package/dist/pipeline/system-instances.d.ts +27 -0
  57. package/dist/pipeline/system-instances.d.ts.map +1 -0
  58. package/dist/pipeline/tsconfig-paths.d.ts +4 -0
  59. package/dist/pipeline/tsconfig-paths.d.ts.map +1 -1
  60. package/dist/pipeline/vite-expansions.d.ts +40 -0
  61. package/dist/pipeline/vite-expansions.d.ts.map +1 -0
  62. package/dist/pipeline/webpack-expansions.d.ts +55 -0
  63. package/dist/pipeline/webpack-expansions.d.ts.map +1 -0
  64. package/dist/pipeline/workspace-packages.d.ts +13 -0
  65. package/dist/pipeline/workspace-packages.d.ts.map +1 -0
  66. package/dist/{pipeline-Bl20tbu_.mjs → pipeline-BzMQ_Z0M.mjs} +2293 -751
  67. package/dist/{pipeline-CAH9mxnK.cjs → pipeline-DyUf-eKZ.cjs} +2564 -746
  68. package/dist/session/coalescing-window.d.ts +34 -0
  69. package/dist/session/coalescing-window.d.ts.map +1 -0
  70. package/dist/session/extraction-session.d.ts +297 -35
  71. package/dist/session/extraction-session.d.ts.map +1 -1
  72. package/dist/session/index.d.ts +7 -3
  73. package/dist/session/index.d.ts.map +1 -1
  74. package/dist/session/published-set.d.ts +6 -1
  75. package/dist/session/published-set.d.ts.map +1 -1
  76. package/dist/session/resolve-packages.d.ts +9 -5
  77. package/dist/session/resolve-packages.d.ts.map +1 -1
  78. package/dist/session/singleton.d.ts +218 -53
  79. package/dist/session/singleton.d.ts.map +1 -1
  80. package/dist/session/turbopack-orchestrator.d.ts.map +1 -1
  81. package/dist/session.cjs +1462 -510
  82. package/dist/session.mjs +1430 -489
  83. package/package.json +4 -4
@@ -1,9 +1,9 @@
1
1
  import { createRequire, isBuiltin } from "node:module";
2
- import path, { basename, dirname, extname, isAbsolute, join, normalize, posix, relative, resolve, sep } from "node:path";
2
+ import path, { basename, dirname, extname, isAbsolute, join, normalize, parse, posix, relative, resolve, sep } from "node:path";
3
3
  import { createHash } from "crypto";
4
- import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "fs";
5
- import { dirname as dirname$1, extname as extname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
6
- import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, statSync as statSync$1 } from "node:fs";
4
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
5
+ import { existsSync as existsSync$1, mkdirSync, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, statSync as statSync$1, unlinkSync, writeFileSync } from "fs";
6
+ import { basename as basename$1, dirname as dirname$1, extname as extname$1, isAbsolute as isAbsolute$1, join as join$1, normalize as normalize$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
7
7
  import { createRequire as createRequire$1 } from "module";
8
8
  import browserslist from "browserslist";
9
9
  import { browserslistToTargets, transform } from "lightningcss";
@@ -69,7 +69,7 @@ function assembleStylesheet(options) {
69
69
  //#endregion
70
70
  //#region pipeline/analyze-project-args.ts
71
71
  /** @internal */
72
- function buildAnalyzeProjectArgs(inputs) {
72
+ function buildAnalyzeProjectArgs(inputs, probeEvidence) {
73
73
  const args = [
74
74
  inputs.filesJson,
75
75
  inputs.scalesJson,
@@ -94,18 +94,15 @@ function buildAnalyzeProjectArgs(inputs) {
94
94
  const tail = [
95
95
  inputs.declarationScalesJson,
96
96
  inputs.analysisContextJson,
97
- inputs.generatedNamesJson
97
+ inputs.generatedNamesJson,
98
+ inputs.propertyRecordsJson
98
99
  ];
99
- const last = tail.findLastIndex((slot) => slot !== void 0 && slot !== "");
100
+ const last = probeEvidence === void 0 ? tail.findLastIndex((slot) => slot !== void 0 && slot !== "") : tail.length - 1;
100
101
  for (const slot of tail.slice(0, last + 1)) args.push(slot || void 0);
102
+ if (probeEvidence !== void 0) args.push(probeEvidence);
101
103
  return args;
102
104
  }
103
105
  //#endregion
104
- //#region pipeline/content-hash.ts
105
- function contentHash(source) {
106
- return createHash("md5").update(source).digest("hex");
107
- }
108
- //#endregion
109
106
  //#region pipeline/internal-wire.ts
110
107
  function parseInternalWire(json, wire) {
111
108
  let parsed;
@@ -117,6 +114,160 @@ function parseInternalWire(json, wire) {
117
114
  return parsed;
118
115
  }
119
116
  //#endregion
117
+ //#region pipeline/content-hash.ts
118
+ function contentHash(source) {
119
+ return createHash("md5").update(source).digest("hex");
120
+ }
121
+ //#endregion
122
+ //#region pipeline/extension-policy.ts
123
+ /**
124
+ * The one extension policy: the order in which a relative or aliased
125
+ * specifier's candidate files are tried. Source ingestion reads it here. The
126
+ * system loader and the extractor read `extension_policy.json`, which
127
+ * `node packages/extract/scripts/extension-policy.ts --write` generates from
128
+ * it, so every resolver tries the same files in the same order.
129
+ *
130
+ * The order: the base itself, the base with each appended extension, its
131
+ * `index` with each, then the sources an emitted NodeNext extension stands
132
+ * for (`./x.js` for `x.ts`), spelled exactly.
133
+ */
134
+ const EXTENSION_POLICY = {
135
+ /** Appended to the base, then to `{base}/index`. */
136
+ appended: [
137
+ ".ts",
138
+ ".tsx",
139
+ ".js",
140
+ ".jsx",
141
+ ".mjs"
142
+ ],
143
+ /** An emitted extension, and the source extensions it names, in order. */
144
+ nodeNext: [
145
+ [".js", [
146
+ ".ts",
147
+ ".tsx",
148
+ ".jsx"
149
+ ]],
150
+ [".jsx", [".tsx"]],
151
+ [".mjs", [".mts"]],
152
+ [".cjs", [".cts"]]
153
+ ]
154
+ };
155
+ const BUILT_IN_ORDER = {
156
+ appended: EXTENSION_POLICY.appended,
157
+ nodeNext: EXTENSION_POLICY.nodeNext,
158
+ alias: []
159
+ };
160
+ /** The files `base` can name, in policy order: itself, with each appended
161
+ * extension, as a directory's index with each, then the sources its
162
+ * emitted NodeNext extension stands for (`x.js` names `x.ts`), spelled
163
+ * exactly. Given `sources`, only extensions in it are tried, and under the
164
+ * built-in order those it lacks (`.mdx`) follow its own. A configured
165
+ * alias of the base's suffix replaces all of these. `index` spells a
166
+ * directory's index path. */
167
+ function policyCandidates(base, options = {}) {
168
+ const { sources, index = (directory, file) => `${directory}/${file}`, policy = BUILT_IN_ORDER } = options;
169
+ const alias = policy.alias.find(([from]) => base.endsWith(from));
170
+ if (alias !== void 0) {
171
+ const [from, sequence] = alias;
172
+ const stem = base.slice(0, base.length - from.length);
173
+ return sequence.map((to) => `${stem}${to}`);
174
+ }
175
+ const admitted = (extension) => !sources || sources.has(extension);
176
+ const appended = [...policy.appended.filter(admitted), ...sources && policy.appended === EXTENSION_POLICY.appended ? [...sources].filter((s) => !policy.appended.includes(s)) : []];
177
+ const name = base.slice(Math.max(base.lastIndexOf("/"), base.lastIndexOf("\\")) + 1);
178
+ const dot = name.lastIndexOf(".");
179
+ const emitted = dot === -1 ? "" : name.slice(dot);
180
+ const stem = base.slice(0, base.length - emitted.length);
181
+ const nodeNext = (policy.nodeNext.find(([from]) => from === emitted)?.[1] ?? []).filter(admitted);
182
+ return [
183
+ base,
184
+ ...appended.map((extension) => `${base}${extension}`),
185
+ ...appended.map((extension) => index(base, `index${extension}`)),
186
+ ...nodeNext.map((source) => `${stem}${source}`)
187
+ ];
188
+ }
189
+ /** The first of `candidates` that `see` finds, in order; a candidate found
190
+ * only under another case ends the probe as a mismatch. */
191
+ function probeCandidates(candidates, see) {
192
+ for (const candidate of candidates) {
193
+ const seen = see(candidate);
194
+ if (seen.kind === "file") return {
195
+ kind: "found",
196
+ path: candidate
197
+ };
198
+ if (seen.kind === "case-only") return {
199
+ kind: "case-mismatch",
200
+ candidate,
201
+ actual: seen.actual
202
+ };
203
+ }
204
+ return { kind: "absent" };
205
+ }
206
+ const isFile = (path) => {
207
+ try {
208
+ return statSync(path).isFile();
209
+ } catch {
210
+ return false;
211
+ }
212
+ };
213
+ /** Whether `directory` lists an entry spelled exactly `name`. */
214
+ function lists(directory, name) {
215
+ try {
216
+ return readdirSync(directory || ".").includes(name);
217
+ } catch {
218
+ return false;
219
+ }
220
+ }
221
+ /** `path`'s real path as the filesystem reports it; as given when it cannot
222
+ * be read. */
223
+ const reported = (path) => {
224
+ try {
225
+ return realpathSync.native(path);
226
+ } catch {
227
+ return path;
228
+ }
229
+ };
230
+ /** What the filesystem holds at `candidate`: a file it opens under exactly
231
+ * that spelling, or one it opens although a directory below the one the
232
+ * candidate shares with `within` lists no entry spelled as the candidate
233
+ * spells it (a case- or normalization-insensitive filesystem opening
234
+ * another spelling). Nothing is folded: the filesystem decides what opens,
235
+ * and its listings decide the spelling, so a file a case-sensitive
236
+ * filesystem cannot open is simply missing. The shared directory is
237
+ * spelled as given, as a specifier spells only the path from its
238
+ * importer's directory and a manifest only the paths in its package. `..`
239
+ * is taken back lexically, as Node and the bundlers resolve a specifier
240
+ * before opening it. The system loader reads it alike. */
241
+ function seeOnDisk(candidate, within) {
242
+ const path = normalize(candidate);
243
+ if (!isFile(path)) return { kind: "missing" };
244
+ const { root } = parse(path);
245
+ const names = path.slice(root.length).split(sep).filter(Boolean);
246
+ const baseNames = within === void 0 ? [] : normalize(within).slice(root.length).split(sep).filter(Boolean);
247
+ let shared = 0;
248
+ while (shared < baseNames.length && names[shared] === baseNames[shared] && names[shared] !== "..") shared++;
249
+ let directory = join(root, ...names.slice(0, shared));
250
+ for (const name of names.slice(shared)) {
251
+ if (!lists(directory, name)) return {
252
+ kind: "case-only",
253
+ actual: reported(path)
254
+ };
255
+ directory = join(directory, name);
256
+ }
257
+ return { kind: "file" };
258
+ }
259
+ /** The first of `candidates` the filesystem holds, spelled exactly below
260
+ * `within` when given. */
261
+ function probeOnDisk(candidates, within) {
262
+ return probeCandidates(candidates, (candidate) => seeOnDisk(candidate, within));
263
+ }
264
+ /** Why `subject` names no file: the file it would load is spelled otherwise
265
+ * on disk. `remedy` says what to respell. The system loader words it
266
+ * alike. */
267
+ function caseMismatchReason(subject, mismatch, remedy) {
268
+ return `${subject} names ${mismatch.candidate}, but the file on disk is ${mismatch.actual}, spelled differently: a case- or normalization-insensitive filesystem loads it and a sensitive one does not — ${remedy}`;
269
+ }
270
+ //#endregion
120
271
  //#region pipeline/asset-placeholders.ts
121
272
  const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
122
273
  const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
@@ -294,15 +445,6 @@ function warnLine(diagnostic) {
294
445
  /** Stable code for selector forms with no substitutable subject; the
295
446
  * extraction engine mints the same string. */
296
447
  const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
297
- /** The one matcher for the engine's unresolved-parent chain-drop message;
298
- * consumers match through the exported helpers, never their own copy. */
299
- const UNRESOLVED_PARENT_RE = /chain dropped: could not resolve parent component '([^']+)'/;
300
- function isUnresolvedParentDrop(diagnostic) {
301
- return UNRESOLVED_PARENT_RE.test(diagnostic.message);
302
- }
303
- function unresolvedParentName(diagnostic) {
304
- return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
305
- }
306
448
  const DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set([
307
449
  "off",
308
450
  "info",
@@ -420,11 +562,11 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
420
562
  const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
421
563
  /** A kit the system extends resolved, yet discovery found none of its files. */
422
564
  const NO_KIT_FILES = "animus.discovery.no-kit-files";
423
- /** A kit with no `animus` export condition: discovery guesses its source
424
- * from `src/`. */
565
+ /** A kit with no `animus` export condition whose exports do not all target
566
+ * TypeScript source: discovery guesses its source from `src/`. */
425
567
  const KIT_WITHOUT_SOURCE_CONDITION = "animus.discovery.kit-without-source-condition";
426
568
  /** A kit's `animus` export condition names a file that is missing or outside
427
- * its package, so that entry is not read from source. */
569
+ * its package, so that entry is read from its runtime target instead. */
428
570
  const INVALID_KIT_SOURCE_CONDITION = "animus.discovery.invalid-source-condition";
429
571
  /** A kit the application imports whose system the application's system
430
572
  * does not include: its components are not extracted. */
@@ -432,10 +574,34 @@ const KIT_SYSTEM_NOT_INCLUDED = "animus.kit.system-not-included";
432
574
  /** A system file's `createSystem` binding that discovery could not follow to
433
575
  * Animus's factory, so it is no root. */
434
576
  const UNPROVEN_ROOT_BINDING = "animus.discovery.unproven-root-binding";
577
+ /** A system builder, a `createSystem` chain not yet built, leaves the
578
+ * bindings and chains discovery follows, so kits extended on it from there
579
+ * are not read as members. */
580
+ const UNFOLLOWED_SYSTEM_BUILDER = "animus.discovery.unfollowed-system-builder";
435
581
  /** A system file calls `createSystem` with no import or local binding. The
436
582
  * system loader evaluates the file without auto-imports, where the name is
437
583
  * undefined, so the call is no root. */
438
584
  const UNIMPORTED_CREATE_SYSTEM = "animus.discovery.unimported-create-system";
585
+ /** A tsconfig path alias the host bundler resolves to another file than
586
+ * analysis does (spec §2.2 A7): report only. */
587
+ const ALIAS_MISMATCH = "animus.discovery.alias-mismatch";
588
+ /** A package the host bundler resolves to a file in no package of its own,
589
+ * where discovery reads the installed package instead (spec §2.2 A4):
590
+ * report only. */
591
+ const PACKAGE_MISMATCH = "animus.discovery.package-mismatch";
592
+ /** A system-file kit whose analysed instance is not the one the system
593
+ * load evaluated (spec §2.2 A4): its components would be extracted
594
+ * against another instance's vocabulary. */
595
+ const SYSTEM_INSTANCE_MISMATCH = "animus.discovery.system-instance-mismatch";
596
+ /** An import the host resolves, from its importer, into another instance
597
+ * of a discovered package whose file is not analysed and that no source
598
+ * redirect covers: the importer would render unextracted builder code
599
+ * (spec §2.2 A4, SA1). */
600
+ const UNREAD_PACKAGE_INSTANCE = "animus.discovery.unread-package-instance";
601
+ /** An import the host resolves, from its importer, into another instance
602
+ * of a discovered package, which a request-keyed source redirect replaces
603
+ * with the instance discovery read (spec §2.2 A4). */
604
+ const REPLACED_PACKAGE_INSTANCE = "animus.discovery.replaced-package-instance";
439
605
  /** What a skipped source file costs: the analysis never sees what it
440
606
  * renders. */
441
607
  const SKIPPED_SOURCE_COST = "skipped: its renders are not seen, so nothing is pruned and every component, variant option and state is kept until it is analyzed";
@@ -521,6 +687,12 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
521
687
  [KIT_STALE_DESCRIPTOR, "warn"],
522
688
  [UNPROVEN_ROOT_BINDING, "warn"],
523
689
  [UNIMPORTED_CREATE_SYSTEM, "warn"],
690
+ [UNFOLLOWED_SYSTEM_BUILDER, "warn"],
691
+ [ALIAS_MISMATCH, "warn"],
692
+ [PACKAGE_MISMATCH, "warn"],
693
+ [SYSTEM_INSTANCE_MISMATCH, "error"],
694
+ [UNREAD_PACKAGE_INSTANCE, "error"],
695
+ [REPLACED_PACKAGE_INSTANCE, "warn"],
524
696
  [VOCABULARY_COLLISION, "warn"],
525
697
  [VOCABULARY_LEGACY_VERB, "warn"],
526
698
  [INVALID_PROPERTY_REGISTRATION, "error"],
@@ -722,6 +894,135 @@ async function preprocessMdx(source, filename) {
722
894
  }
723
895
  }
724
896
  //#endregion
897
+ //#region pipeline/host-expansions.ts
898
+ /**
899
+ * The host's expansion of each listed load the expander reproduces, with
900
+ * each file read as the analysis and the host read it: a module the
901
+ * analysis holds, loaded as code without a query; a file the host loads as
902
+ * an opaque value (spec §2.2 A8); otherwise a file the analysis does not
903
+ * read. A query can select other code than the module (a plugin's
904
+ * transform), so a queried file that is not opaque is never the analysed
905
+ * module, and it keeps its query.
906
+ */
907
+ function hostExpansions(loads, rootDir, analysed, expander, isAsset) {
908
+ const expansions = [];
909
+ for (const load of loads) {
910
+ const expanded = expander.expand(load, rootDir);
911
+ if (expanded === void 0) continue;
912
+ const files = /* @__PURE__ */ new Map();
913
+ for (const { path, query } of expanded) {
914
+ const opaque = isAsset?.(join(rootDir, path), query) ?? false;
915
+ const status = query === "" && analysed.has(path) ? "analysed" : opaque ? "opaque" : "unanalysed";
916
+ const key = `${path}${query}`;
917
+ files.set(key, {
918
+ path: status === "analysed" ? path : key,
919
+ status
920
+ });
921
+ }
922
+ expansions.push({
923
+ importer: load.importer,
924
+ start: load.start,
925
+ closed: true,
926
+ files: [...files.values()]
927
+ });
928
+ }
929
+ return expansions;
930
+ }
931
+ //#endregion
932
+ //#region pipeline/probe-evidence.ts
933
+ let generations = 0;
934
+ /** `rootDir` at its real path, where the filesystem reports spellings. */
935
+ function realRoot(rootDir) {
936
+ try {
937
+ return realpathSync.native(rootDir);
938
+ } catch {
939
+ return rootDir;
940
+ }
941
+ }
942
+ /**
943
+ * What the host's filesystem opens for `base` (spec §2.2 A2, A8): the first
944
+ * of its candidates, in the host's order, that the host opens. The
945
+ * filesystem decides what opens, and each name below the session root is
946
+ * checked against its own directory's listing (`seeOnDisk`); nothing is
947
+ * folded. Null where no candidate opens.
948
+ *
949
+ * A candidate the analysis holds answers before the filesystem is asked: an
950
+ * adapted original's analysis child need not exist on disk. A file the host
951
+ * opens that the analysis holds no module for is read as an adapted
952
+ * original (analysed as its first later child), an asset, or a file
953
+ * analysis never reads.
954
+ */
955
+ function hostCandidate(rootDir, base, analysed, order, readings = {}, root = realRoot(rootDir)) {
956
+ const at = base.indexOf("?");
957
+ const query = at === -1 ? "" : base.slice(at);
958
+ const candidates = policyCandidates(at === -1 ? base : base.slice(0, at), { policy: order });
959
+ for (const [index, file] of candidates.entries()) {
960
+ const candidate = `${file}${query}`;
961
+ const opaque = () => readings.isAsset?.(join(rootDir, file), query);
962
+ if (analysed.has(file)) return query !== "" && opaque() ? {
963
+ candidate,
964
+ actual: null,
965
+ analysedAs: null,
966
+ reading: "asset"
967
+ } : {
968
+ candidate,
969
+ actual: null,
970
+ analysedAs: file
971
+ };
972
+ const seen = seeOnDisk(join(rootDir, file), rootDir);
973
+ if (seen.kind === "missing") continue;
974
+ if (seen.kind === "case-only") return {
975
+ candidate,
976
+ actual: relative(root, seen.actual),
977
+ analysedAs: null
978
+ };
979
+ const children = readings.adapted?.get(file);
980
+ if (children !== void 0) return {
981
+ candidate,
982
+ actual: null,
983
+ analysedAs: candidates.slice(index + 1).find((later) => children.includes(later)) ?? null,
984
+ reading: "adapted"
985
+ };
986
+ return {
987
+ candidate,
988
+ actual: null,
989
+ analysedAs: null,
990
+ reading: opaque() ? "asset" : "unanalysed"
991
+ };
992
+ }
993
+ return null;
994
+ }
995
+ /**
996
+ * The session's host-candidate evidence for one analysis (spec §2.3): each
997
+ * probe base the engine's `prepare` names, as `hostCandidate` reads it, and
998
+ * the host's expansion of each computed load it lists (spec §6.3). The
999
+ * evidence carries the input identity `prepare` reported, and each call is a
1000
+ * new ingest generation, so no evidence is read for other files.
1001
+ */
1002
+ function gatherProbeEvidence(rootDir, analysed, order, readings = {}) {
1003
+ const root = realRoot(rootDir);
1004
+ return (bases, identity, loads = []) => {
1005
+ const byBase = {};
1006
+ for (const base of bases) {
1007
+ const host = hostCandidate(rootDir, base, analysed, order, readings, root);
1008
+ if (host !== null) byBase[base] = host;
1009
+ }
1010
+ const expansions = readings.expander === void 0 ? [] : hostExpansions(loads, rootDir, analysed, readings.expander, readings.isAsset);
1011
+ generations += 1;
1012
+ return JSON.stringify({
1013
+ generation: generations,
1014
+ identity,
1015
+ bases: byBase,
1016
+ ...expansions.length > 0 && { expansions }
1017
+ });
1018
+ };
1019
+ }
1020
+ /** Each adapted original and its analysis children, from ingestion's
1021
+ * ownership: an original whose children are other paths than its own. */
1022
+ function adaptedOriginals(ownership) {
1023
+ return new Map(Object.entries(ownership).filter(([original, { analysisPaths }]) => !(analysisPaths.length === 1 && analysisPaths[0] === original)).map(([original, { analysisPaths }]) => [original, analysisPaths]));
1024
+ }
1025
+ //#endregion
725
1026
  //#region pipeline/svelte-source-adapter.ts
726
1027
  const isCompilerReference = (value) => Object(value) === value;
727
1028
  const isCompilerCallable = (value) => {
@@ -819,14 +1120,14 @@ function sourceLocation(source, node) {
819
1120
  function createsNestedBindingScope(node) {
820
1121
  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";
821
1122
  }
822
- function walk(node, visit, nestedScopeDepth = 0) {
1123
+ function walk$1(node, visit, nestedScopeDepth = 0) {
823
1124
  visit(node, nestedScopeDepth);
824
1125
  const childScopeDepth = nestedScopeDepth + (createsNestedBindingScope(node) ? 1 : 0);
825
1126
  for (const [key, value] of Object.entries(node)) {
826
1127
  if (key === "loc" || key === "start" || key === "end") continue;
827
1128
  if (Array.isArray(value)) {
828
- for (const item of value) if (isNode(item)) walk(item, visit, childScopeDepth);
829
- } else if (isNode(value)) walk(value, visit, childScopeDepth);
1129
+ for (const item of value) if (isNode(item)) walk$1(item, visit, childScopeDepth);
1130
+ } else if (isNode(value)) walk$1(value, visit, childScopeDepth);
830
1131
  }
831
1132
  }
832
1133
  /**
@@ -1028,7 +1329,7 @@ function isJsxAttributeName(name) {
1028
1329
  }
1029
1330
  function tsxIncompatibleValue(value) {
1030
1331
  let incompatible = null;
1031
- walk(value, (node) => {
1332
+ walk$1(value, (node) => {
1032
1333
  if (incompatible) return;
1033
1334
  if (node.type === "TSTypeAssertion" || node.type === "ArrowFunctionExpression" && childNode(node.typeParameters) !== null) incompatible = node;
1034
1335
  });
@@ -1125,7 +1426,7 @@ function projectScope(script, scope, source, originalPath, options, inherited, p
1125
1426
  const bindings = inherited ? new Map([...inherited.bindings, ...ownBindings]) : ownBindings;
1126
1427
  const witnesses = [];
1127
1428
  const diagnostics = [];
1128
- if (bindings.size > 0) walk(script.content, (node, nestedScopeDepth) => {
1429
+ if (bindings.size > 0) walk$1(script.content, (node, nestedScopeDepth) => {
1129
1430
  const site = resolverCallSite(node, bindings);
1130
1431
  if (!site) return;
1131
1432
  const { access, form } = site;
@@ -1290,17 +1591,6 @@ function parseFilesJson(filesJson, context) {
1290
1591
  function isAdvisorySourceDiagnostic(diagnostic) {
1291
1592
  return diagnostic.code === "SOURCE_NATIVE_PARSE_ERROR";
1292
1593
  }
1293
- const RELATIVE_PROBE_SUFFIXES = [
1294
- "",
1295
- ".ts",
1296
- ".tsx",
1297
- ".js",
1298
- ".jsx",
1299
- "/index.ts",
1300
- "/index.tsx",
1301
- "/index.js",
1302
- "/index.jsx"
1303
- ];
1304
1594
  function extension(path) {
1305
1595
  const basename = posix.basename(path);
1306
1596
  const dot = basename.lastIndexOf(".");
@@ -1309,27 +1599,40 @@ function extension(path) {
1309
1599
  function canonicalResolverPath(path) {
1310
1600
  return posix.normalize(path.replaceAll("\\", "/"));
1311
1601
  }
1312
- const NODE_NEXT_EXTENSION_MAP = {
1313
- ".js": [
1314
- ".ts",
1315
- ".tsx",
1316
- ".jsx"
1317
- ],
1318
- ".jsx": [".tsx"],
1319
- ".mjs": [".mts"],
1320
- ".cjs": [".cts"]
1321
- };
1322
- /** The paths a relative specifier can name from `importerPath`, in probe
1323
- * order: as written with each source suffix, then a NodeNext emitted
1324
- * extension's sources. Empty for a non-relative specifier. */
1325
- function relativeSourceCandidates(importerPath, specifier) {
1602
+ /** The paths a relative specifier can name from `importerPath`, in the
1603
+ * extension policy's probe order. Empty for a non-relative specifier. */
1604
+ function relativeSourceCandidates(importerPath, specifier, policy) {
1326
1605
  if (!specifier.startsWith(".")) return [];
1327
- const base = posix.normalize(posix.join(posix.dirname(canonicalResolverPath(importerPath)), specifier));
1328
- const explicitExtension = extension(base);
1329
- return [...RELATIVE_PROBE_SUFFIXES.map((suffix) => `${base}${suffix}`), ...(NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []).map((sourceExtension) => `${base.slice(0, -explicitExtension.length)}${sourceExtension}`)];
1330
- }
1331
- function resolveRelativeSource(importerPath, specifier, files) {
1332
- for (const candidate of relativeSourceCandidates(importerPath, specifier)) {
1606
+ return policyCandidates(posix.normalize(posix.join(posix.dirname(canonicalResolverPath(importerPath)), specifier)), { policy });
1607
+ }
1608
+ function resolveRelativeSource(importerPath, specifier, files, analysed, opened, policy, host) {
1609
+ if (host !== void 0 && specifier.startsWith(".")) {
1610
+ const first = hostCandidate(host.rootDir, posix.normalize(posix.join(posix.dirname(canonicalResolverPath(importerPath)), specifier)), analysed, policy, { adapted: host.adapted });
1611
+ if (first !== null) {
1612
+ if (first.actual) {
1613
+ opened({
1614
+ candidate: first.candidate,
1615
+ opened: first.actual,
1616
+ why: "case-mismatch"
1617
+ });
1618
+ return null;
1619
+ }
1620
+ if (first.reading === "unanalysed") {
1621
+ const candidates = relativeSourceCandidates(importerPath, specifier, policy);
1622
+ const later = candidates.slice(candidates.indexOf(first.candidate) + 1).find((candidate) => files.has(candidate));
1623
+ const opening = {
1624
+ candidate: first.candidate,
1625
+ opened: first.candidate,
1626
+ why: "skipped"
1627
+ };
1628
+ if (later !== void 0) opening.later = later;
1629
+ opened(opening);
1630
+ return null;
1631
+ }
1632
+ return first.analysedAs === null ? null : files.get(first.analysedAs) ?? null;
1633
+ }
1634
+ }
1635
+ for (const candidate of relativeSourceCandidates(importerPath, specifier, policy)) {
1333
1636
  const actualPath = files.get(candidate);
1334
1637
  if (actualPath !== void 0) return actualPath;
1335
1638
  }
@@ -1337,15 +1640,22 @@ function resolveRelativeSource(importerPath, specifier, files) {
1337
1640
  }
1338
1641
  var ResolverExportIndex = class {
1339
1642
  facts;
1643
+ extensions;
1644
+ host;
1340
1645
  collisions;
1341
1646
  files;
1647
+ analysed;
1648
+ /** Each resolver call that ended Incomplete, once. */
1649
+ openings = /* @__PURE__ */ new Map();
1342
1650
  resolverBindings = /* @__PURE__ */ new Map();
1343
1651
  /** Both memos live for one ingest only — the index is rebuilt per call,
1344
1652
  * so neither needs invalidation. */
1345
1653
  attributionMemo = /* @__PURE__ */ new Map();
1346
1654
  resolveMemo = /* @__PURE__ */ new Map();
1347
- constructor(facts, analysisPaths) {
1655
+ constructor(facts, analysisPaths, extensions, host) {
1348
1656
  this.facts = facts;
1657
+ this.extensions = extensions;
1658
+ this.host = host;
1349
1659
  const pathsByCanonical = /* @__PURE__ */ new Map();
1350
1660
  for (const path of analysisPaths) {
1351
1661
  const canonicalPath = canonicalResolverPath(path);
@@ -1370,6 +1680,7 @@ var ResolverExportIndex = class {
1370
1680
  if (paths.length === 1 && factPaths.length === 1) files.set(canonicalPath, factPaths[0]);
1371
1681
  }
1372
1682
  this.files = files;
1683
+ this.analysed = new Set(files.keys());
1373
1684
  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)));
1374
1685
  }
1375
1686
  attribute(importerPath, request) {
@@ -1390,7 +1701,11 @@ var ResolverExportIndex = class {
1390
1701
  const key = `${importerPath}\0${specifier}`;
1391
1702
  let resolved = this.resolveMemo.get(key);
1392
1703
  if (resolved === void 0) {
1393
- resolved = resolveRelativeSource(importerPath, specifier, this.files);
1704
+ resolved = resolveRelativeSource(importerPath, specifier, this.files, this.analysed, (opening) => this.openings.set(key, {
1705
+ importer: importerPath,
1706
+ specifier,
1707
+ ...opening
1708
+ }), this.extensions, this.host);
1394
1709
  this.resolveMemo.set(key, resolved);
1395
1710
  }
1396
1711
  return resolved;
@@ -1512,7 +1827,10 @@ async function ingestSourceEntries(rawEntries, options) {
1512
1827
  messages: file.parseDiagnostics
1513
1828
  });
1514
1829
  }
1515
- const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path));
1830
+ const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path), options.extensions, options.rootDir === void 0 ? void 0 : {
1831
+ rootDir: options.rootDir,
1832
+ adapted: adaptedOriginals(ownership)
1833
+ });
1516
1834
  for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
1517
1835
  const path = paths[index];
1518
1836
  const conflictingPath = paths[(index + 1) % paths.length];
@@ -1547,13 +1865,14 @@ async function ingestSourceEntries(rawEntries, options) {
1547
1865
  diagnostics
1548
1866
  };
1549
1867
  if (abortedParses.length > 0) result.abortedParses = abortedParses;
1868
+ if (resolvers.openings.size > 0) result.openings = [...resolvers.openings.values()];
1550
1869
  return result;
1551
1870
  }
1552
1871
  /**
1553
1872
  * `parseCount` counts the parses performed by THIS call, so a fully-memoized
1554
1873
  * pass reports zero.
1555
1874
  */
1556
- function collectFileFacts(analysisEntries, options) {
1875
+ function collectFileFacts(analysisEntries, options, evict = true) {
1557
1876
  const cache = options.factsCache;
1558
1877
  if (!cache) return JSON.parse(options.extractFacts(JSON.stringify(analysisEntries)));
1559
1878
  const pending = analysisEntries.filter((entry) => cache.get(entry.path)?.hash !== entry.hash);
@@ -1570,8 +1889,10 @@ function collectFileFacts(analysisEntries, options) {
1570
1889
  else cache.delete(entry.path);
1571
1890
  }
1572
1891
  }
1573
- const currentPaths = new Set(analysisEntries.map((entry) => entry.path));
1574
- for (const path of [...cache.keys()]) if (!currentPaths.has(path)) cache.delete(path);
1892
+ if (evict) {
1893
+ const currentPaths = new Set(analysisEntries.map((entry) => entry.path));
1894
+ for (const path of [...cache.keys()]) if (!currentPaths.has(path)) cache.delete(path);
1895
+ }
1575
1896
  const files = {};
1576
1897
  for (const entry of analysisEntries) {
1577
1898
  const cached = cache.get(entry.path);
@@ -1621,15 +1942,26 @@ function createSourceIngestor(host) {
1621
1942
  /** Non-strict dedupe: a quarantined original re-ingests on every later
1622
1943
  * corpus pass, so re-warning it on each save is noise. */
1623
1944
  const warnedByOriginal = /* @__PURE__ */ new Map();
1945
+ const extractFacts = () => {
1946
+ const extract = host.engineApi().extractFacts;
1947
+ if (extract === void 0) throw new Error(`${host.prefix} native engine does not expose extractFacts required for source adaptation`);
1948
+ return extract;
1949
+ };
1624
1950
  return {
1625
1951
  async ingest(entries) {
1626
- const extractFacts = host.engineApi().extractFacts;
1627
- if (extractFacts === void 0) throw new Error(`${host.prefix} native engine does not expose extractFacts required for source adaptation`);
1628
1952
  return ingestSourceEntries(entries, {
1629
- extractFacts,
1630
- factsCache
1953
+ extractFacts: extractFacts(),
1954
+ factsCache,
1955
+ extensions: host.extensions?.(),
1956
+ rootDir: host.rootDir?.()
1631
1957
  });
1632
1958
  },
1959
+ facts(entries) {
1960
+ return collectFileFacts(entries, {
1961
+ extractFacts: extractFacts(),
1962
+ factsCache
1963
+ }, false);
1964
+ },
1633
1965
  surfaceDiagnostics(diagnostics) {
1634
1966
  const fatal = diagnostics.filter((diagnostic) => !isAdvisorySourceDiagnostic(diagnostic));
1635
1967
  const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
@@ -1677,9 +2009,16 @@ function createV2EngineApi(deps) {
1677
2009
  return {
1678
2010
  loadSystemModule: (...args) => native.loadSystemModule(...args),
1679
2011
  extractFacts: (filesJson) => native.extractFacts(filesJson),
1680
- moduleSpecifiers: (filesJson) => native.moduleSpecifiers(filesJson),
2012
+ moduleSpecifiers: (filesJson, pathAliasesJson) => native.moduleSpecifiers(filesJson, pathAliasesJson),
2013
+ systemMembership: (inputsJson) => native.systemMembership(inputsJson),
1681
2014
  diagnosticCodes: () => native.diagnosticCodes(),
1682
- analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson, generatedNamesJson) => {
2015
+ resolveSpecifier: native.resolveSpecifier === void 0 ? void 0 : (specifier, fromDir, conditions, workspaces, extensionPolicyJson) => native.resolveSpecifier(specifier, fromDir, conditions, workspaces, extensionPolicyJson),
2016
+ fingerprintFile: native.fingerprintFile === void 0 ? void 0 : (path) => native.fingerprintFile(path) ?? null,
2017
+ fingerprintBytes: native.fingerprintBytes === void 0 ? void 0 : (bytes) => native.fingerprintBytes(bytes),
2018
+ declaresKitSource: native.declaresKitSource === void 0 ? void 0 : (packageDir) => native.declaresKitSource(packageDir),
2019
+ locatePackage: native.locatePackage === void 0 ? void 0 : (name, fromDir, workspaces) => native.locatePackage(name, fromDir, workspaces) ?? null,
2020
+ resolveKitSourceRoot: native.resolveKitSourceRoot === void 0 ? void 0 : (specifier, fromDir, conditions, workspaces, extensionPolicyJson, hostEntry, packageDir) => native.resolveKitSourceRoot(specifier, fromDir, conditions, workspaces, extensionPolicyJson, hostEntry, packageDir),
2021
+ analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson, generatedNamesJson, propertyRecordsJson, probeEvidence) => {
1683
2022
  const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
1684
2023
  const sent = /* @__PURE__ */ new Map();
1685
2024
  for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
@@ -1708,11 +2047,17 @@ function createV2EngineApi(deps) {
1708
2047
  declarationScalesJson,
1709
2048
  analysisContextJson,
1710
2049
  generatedNamesJson,
2050
+ propertyRecordsJson,
1711
2051
  devMode
1712
2052
  };
1713
2053
  const engine = new native.ExtractEngine(config);
1714
2054
  store.setEngine(engine);
1715
- return engine.analyze(filesJson);
2055
+ let evidence;
2056
+ if (probeEvidence !== void 0) {
2057
+ const prepared = parseInternalWire(engine.prepare(filesJson), "ExtractEngine.prepare");
2058
+ evidence = probeEvidence(prepared.bases, prepared.identity, prepared.loads);
2059
+ }
2060
+ return engine.analyze(filesJson, evidence);
1716
2061
  },
1717
2062
  transformFile: (source, path, _manifest) => {
1718
2063
  const engine = store.getEngine();
@@ -1738,10 +2083,112 @@ function createV2EngineApi(deps) {
1738
2083
  };
1739
2084
  }
1740
2085
  //#endregion
1741
- //#region pipeline/engine-retirement.ts
1742
- const RETIRED_ENGINE_MESSAGE = "animus: the 'v1' extraction engine is no longer supported, and v2 is the only engine. Remove the engine option from your Animus config and unset the ANIMUS_ENGINE environment variable.";
1743
- function assertNoRetiredEngineSelection(engineOption) {
1744
- if (engineOption === "v1" || process.env.ANIMUS_ENGINE === "v1") throw new Error(RETIRED_ENGINE_MESSAGE);
2086
+ //#region pipeline/source-identity.ts
2087
+ /**
2088
+ * One derivation of source identity for discovery, watch ingestion, ownership
2089
+ * and deletion: event paths are lookup INPUTS only and never fork identity.
2090
+ */
2091
+ /**
2092
+ * True when `target` is `root` or a descendant, decided by `relative()`
2093
+ * shape: string prefixing would let `/ui` claim `/ui-old`.
2094
+ */
2095
+ function isPathWithinRoot(root, target, pathApi = path) {
2096
+ const rel = pathApi.relative(root, target);
2097
+ return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
2098
+ }
2099
+ /**
2100
+ * True when two paths share a platform root (win32 drive letter or UNC
2101
+ * share; always true on posix). Drive letters compare case-insensitively.
2102
+ */
2103
+ function sharesVolumeRoot(a, b, pathApi = path) {
2104
+ const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
2105
+ return volumeOf(a) === volumeOf(b);
2106
+ }
2107
+ function canonicalize(p) {
2108
+ try {
2109
+ return realpathSync(p);
2110
+ } catch {
2111
+ return null;
2112
+ }
2113
+ }
2114
+ function createSourceIdentity(rootDir) {
2115
+ const normalizedRootDir = path.normalize(rootDir);
2116
+ const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
2117
+ const roots = [];
2118
+ const aliases = /* @__PURE__ */ new Map();
2119
+ const owningRecord = (canonicalPath) => {
2120
+ let owner = null;
2121
+ for (const record of roots) {
2122
+ if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
2123
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2124
+ }
2125
+ return owner;
2126
+ };
2127
+ return {
2128
+ rootDir: normalizedRootDir,
2129
+ canonicalRootDir,
2130
+ registerExternalRoot(root) {
2131
+ const asRegistered = path.normalize(root);
2132
+ const canonical = canonicalize(asRegistered) ?? asRegistered;
2133
+ const existing = roots.find((record) => record.canonical === canonical);
2134
+ if (existing) return existing.canonical;
2135
+ roots.push({
2136
+ asRegistered,
2137
+ canonical
2138
+ });
2139
+ return canonical;
2140
+ },
2141
+ externalRoots() {
2142
+ return roots.map((record) => record.canonical);
2143
+ },
2144
+ resolveSourceId(inputPath) {
2145
+ const lexical = path.normalize(inputPath);
2146
+ const canonical = canonicalize(lexical);
2147
+ if (canonical === null) return null;
2148
+ const owner = owningRecord(canonical);
2149
+ const ownedBy = (record) => {
2150
+ const pathInRoot = path.relative(record.canonical, canonical);
2151
+ return {
2152
+ sourceKey: path.relative(normalizedRootDir, path.join(record.asRegistered, pathInRoot)),
2153
+ owningRoot: record.canonical,
2154
+ pathInRoot
2155
+ };
2156
+ };
2157
+ const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
2158
+ let resolved;
2159
+ if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
2160
+ const key = path.relative(canonicalRootDir, canonical);
2161
+ resolved = {
2162
+ sourceKey: key,
2163
+ owningRoot: null,
2164
+ pathInRoot: key
2165
+ };
2166
+ } else if (owner) resolved = ownedBy(owner);
2167
+ else return null;
2168
+ aliases.set(lexical, resolved);
2169
+ aliases.set(canonical, resolved);
2170
+ return resolved;
2171
+ },
2172
+ resolveDeletedSourceId(inputPath) {
2173
+ return aliases.get(path.normalize(inputPath)) ?? null;
2174
+ },
2175
+ containingExternalRoot(inputPath) {
2176
+ const lexical = path.normalize(inputPath);
2177
+ let owner = null;
2178
+ for (const record of roots) {
2179
+ if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
2180
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2181
+ }
2182
+ if (owner) return owner.canonical;
2183
+ const canonical = canonicalize(lexical);
2184
+ if (canonical === null) return null;
2185
+ for (const record of roots) {
2186
+ if (!isPathWithinRoot(record.canonical, canonical)) continue;
2187
+ if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2188
+ }
2189
+ return owner?.canonical ?? null;
2190
+ }
2191
+ };
1745
2192
  }
1746
2193
  //#endregion
1747
2194
  //#region pipeline/core-options.ts
@@ -1850,6 +2297,11 @@ const CORE_VALUE_GATES = [
1850
2297
  ok: isString,
1851
2298
  expected: "a string path"
1852
2299
  },
2300
+ {
2301
+ key: "root",
2302
+ ok: isString,
2303
+ expected: "a string path"
2304
+ },
1853
2305
  {
1854
2306
  key: "exclude",
1855
2307
  ok: isStringArray,
@@ -1968,30 +2420,101 @@ function globToRegExp(glob) {
1968
2420
  }
1969
2421
  return new RegExp(`^${out}$`);
1970
2422
  }
2423
+ /** Whether a root-relative path is `dir` or lies under it, a literal path
2424
+ * prefix: an output name is never a pattern. */
2425
+ function compileOutputDir(dir) {
2426
+ return (_full, rel) => {
2427
+ const normalized = rel.split("\\").join("/");
2428
+ return normalized === dir || normalized.startsWith(`${dir}/`);
2429
+ };
2430
+ }
1971
2431
  /**
1972
- * A user list replaces only the replaceable defaults. Driver-owned
1973
- * invariants belong in `extraStructural`; a user list would replace those.
2432
+ * A user list replaces only the replaceable defaults. A host's output
2433
+ * directories (`outputExclusion`) always apply, whatever the user's list.
1974
2434
  */
1975
- function createExcludeMatcher(userPatterns, extraStructural = []) {
1976
- const merged = [...STRUCTURAL_EXCLUDE];
1977
- for (const pattern of extraStructural) if (!merged.includes(pattern)) merged.push(pattern);
1978
- for (const pattern of userPatterns ?? REPLACEABLE_DEFAULT_EXCLUDE) if (!merged.includes(pattern)) merged.push(pattern);
1979
- const compiled = merged.map((pattern) => [pattern, compilePattern(pattern)]);
1980
- const hits = new Map(merged.map((p) => [p, 0]));
2435
+ function createExcludeMatcher(userPatterns, outputDirs = []) {
2436
+ const userList = userPatterns ?? REPLACEABLE_DEFAULT_EXCLUDE;
2437
+ const compiled = [];
2438
+ for (const pattern of STRUCTURAL_EXCLUDE) compiled.push({
2439
+ pattern,
2440
+ test: compilePattern(pattern),
2441
+ isUser: userList.includes(pattern)
2442
+ });
2443
+ for (const dir of outputDirs) compiled.push({
2444
+ pattern: dir,
2445
+ test: compileOutputDir(dir),
2446
+ isUser: false
2447
+ });
2448
+ for (const pattern of userList) {
2449
+ if (STRUCTURAL_EXCLUDE.includes(pattern)) continue;
2450
+ compiled.push({
2451
+ pattern,
2452
+ test: compilePattern(pattern),
2453
+ isUser: true
2454
+ });
2455
+ }
2456
+ const hits = new Map(compiled.map((c) => [c.pattern, 0]));
1981
2457
  const explain = (full, rel) => {
1982
- for (const [pattern, test] of compiled) if (test(full, rel)) {
2458
+ let excludedBy = null;
2459
+ for (const { pattern, test, isUser } of compiled) {
2460
+ if (excludedBy !== null && !isUser) continue;
2461
+ if (!test(full, rel)) continue;
1983
2462
  hits.set(pattern, (hits.get(pattern) ?? 0) + 1);
1984
- return pattern;
2463
+ excludedBy ??= pattern;
1985
2464
  }
1986
- return null;
2465
+ return excludedBy;
1987
2466
  };
1988
2467
  return {
1989
- patterns: merged,
2468
+ patterns: compiled.map((c) => c.pattern),
1990
2469
  matches: (full, rel) => explain(full, rel) !== null,
1991
2470
  explain,
1992
- stats: () => hits
2471
+ stats: () => hits,
2472
+ unmatched: () => (userPatterns ?? []).filter((pattern) => hits.get(pattern) === 0)
2473
+ };
2474
+ }
2475
+ /** One line per user pattern that matched nothing, which every host
2476
+ * prints to stderr after its first discovery, as the CLI always has. */
2477
+ function unmatchedExcludeLines(matcher) {
2478
+ return matcher.unmatched().map((pattern) => `[animus] exclude pattern '${pattern}' matched nothing`);
2479
+ }
2480
+ /**
2481
+ * A host's output directory as an exclusion: root-relative with `/`
2482
+ * separators, matched as a literal path prefix, so a user list, which
2483
+ * replaces the defaults, never re-admits output. Null when the directory is
2484
+ * outside the root or is the root itself.
2485
+ */
2486
+ function outputExclusion(root, outDir) {
2487
+ if (!isPathWithinRoot(root, outDir)) return null;
2488
+ const rel = relative$1(root, outDir);
2489
+ return rel === "" ? null : rel.split(sep$1).join("/");
2490
+ }
2491
+ /**
2492
+ * The output directory a source file's real path lies in, or null. Applied
2493
+ * where a host assembles its analysis inputs: a file reached under another
2494
+ * spelling, through a source-tree link or a package's entry, is output too.
2495
+ */
2496
+ function outputSourceGuard(root, outputDirs) {
2497
+ if (outputDirs.length === 0) return () => null;
2498
+ const realDirs = outputDirs.map((dir) => ({
2499
+ dir,
2500
+ real: realPathOf(resolve$1(root, dir))
2501
+ }));
2502
+ return (absPath) => {
2503
+ const real = realPathOf(absPath);
2504
+ return realDirs.find((o) => isPathWithinRoot(o.real, real))?.dir ?? null;
1993
2505
  };
1994
2506
  }
2507
+ /** The warning for a source a host refused as output. */
2508
+ function outputSourceMessage(relPath, outputDir) {
2509
+ return `${relPath} resolves into the output directory '${outputDir}', which is never source; it is not analysed`;
2510
+ }
2511
+ function realPathOf(path) {
2512
+ try {
2513
+ return realpathSync$1(path);
2514
+ } catch {
2515
+ return path;
2516
+ }
2517
+ }
1995
2518
  function resolveMode(explicit, driverDefault) {
1996
2519
  if (explicit === "development" || explicit === "production") return {
1997
2520
  mode: explicit,
@@ -2003,18 +2526,92 @@ function resolveMode(explicit, driverDefault) {
2003
2526
  };
2004
2527
  }
2005
2528
  //#endregion
2006
- //#region pipeline/discover-files.ts
2007
- function pathCharRank(code) {
2008
- return code === 47 || code === 92 ? -1 : code;
2009
- }
2010
- function compareDiscoveryOrder(a, b) {
2011
- const shared = Math.min(a.length, b.length);
2012
- for (let i = 0; i < shared; i++) {
2013
- const left = pathCharRank(a.charCodeAt(i));
2014
- const right = pathCharRank(b.charCodeAt(i));
2015
- if (left !== right) return left - right;
2529
+ //#region pipeline/engine-retirement.ts
2530
+ const RETIRED_ENGINE_MESSAGE = "animus: the 'v1' extraction engine is no longer supported, and v2 is the only engine. Remove the engine option from your Animus config and unset the ANIMUS_ENGINE environment variable.";
2531
+ /**
2532
+ * v2 is the only engine (SA2): the option and `ANIMUS_ENGINE`, a startup
2533
+ * input a worker may pass as captured, accept `v2` or nothing. Hosts call
2534
+ * this before the native module loads, so another selection never reaches
2535
+ * it; either fails as a configuration error naming its source.
2536
+ */
2537
+ function assertSupportedEngine(engineOption, environmentSelection = process.env.ANIMUS_ENGINE) {
2538
+ const selections = [[engineOption, "the engine option"], [environmentSelection || void 0, "ANIMUS_ENGINE"]];
2539
+ for (const [selection, source] of selections) {
2540
+ if (selection === void 0 || selection === "v2") continue;
2541
+ if (selection === "v1") throw new AnimusConfigError(`${RETIRED_ENGINE_MESSAGE} (from ${source})`);
2542
+ throw new AnimusConfigError(`animus: unknown extraction engine "${selection}" from ${source}: v2 is the only engine.`);
2016
2543
  }
2017
- return a.length - b.length;
2544
+ }
2545
+ //#endregion
2546
+ //#region pipeline/asset-copies.ts
2547
+ /**
2548
+ * Immutable asset copies: one binding from an `asset()` source file to a
2549
+ * content-digested name, shared by every host that serves copies.
2550
+ */
2551
+ /** Copies `sourcePath` into `assetsDir` under its content-digested name,
2552
+ * once: a copy is never overwritten, so a URL naming it always serves the
2553
+ * bytes it named. */
2554
+ function writeAssetCopy(sourcePath, assetsDir) {
2555
+ const bytes = readFileSync$1(sourcePath);
2556
+ const digest = contentHash(bytes);
2557
+ const ext = extname$1(sourcePath);
2558
+ const fileName = `${basename$1(sourcePath, ext)}.${digest.slice(0, 8)}${ext}`;
2559
+ if (!existsSync$1(assetsDir)) mkdirSync(assetsDir, { recursive: true });
2560
+ const copyPath = join$1(assetsDir, fileName);
2561
+ if (!existsSync$1(copyPath)) writeFileSync(copyPath, bytes);
2562
+ return {
2563
+ fileName,
2564
+ digest,
2565
+ bytes
2566
+ };
2567
+ }
2568
+ /** Sync `assets/` to the current build's file set; per-entry failures are
2569
+ * tolerated — a concurrent session may have removed the same file. */
2570
+ function pruneStaleAssets(assetsDir, expected) {
2571
+ if (!existsSync$1(assetsDir)) return;
2572
+ let entries;
2573
+ try {
2574
+ entries = readdirSync$1(assetsDir);
2575
+ } catch {
2576
+ return;
2577
+ }
2578
+ for (const entry of entries) {
2579
+ if (expected.has(entry)) continue;
2580
+ try {
2581
+ unlinkSync(join$1(assetsDir, entry));
2582
+ } catch {}
2583
+ }
2584
+ }
2585
+ //#endregion
2586
+ //#region pipeline/runtime-import.ts
2587
+ /** The runtime every adapter emits by default (spine row 1). */
2588
+ const DEFAULT_RUNTIME_IMPORT = "@animus-ui/system/runtime";
2589
+ /** The entries that supply every terminal: an override naming another must
2590
+ * extract only `.asClass()` terminals. */
2591
+ const FULL_RUNTIME_IMPORTS = /* @__PURE__ */ new Set([DEFAULT_RUNTIME_IMPORT, "@animus-ui/system"]);
2592
+ /** Throws when `runtimeImport` names a module that cannot supply a
2593
+ * terminal the manifest extracted. */
2594
+ function assertRuntimeImportSuppliesTerminals(runtimeImport, manifest) {
2595
+ if (!runtimeImport || FULL_RUNTIME_IMPORTS.has(runtimeImport)) return;
2596
+ const offenders = [];
2597
+ for (const [id, descriptor] of Object.entries(manifest.components)) if (/\bcreateComponent\(|\bcreateComposedFamily\(/.test(descriptor.replacement)) offenders.push(id);
2598
+ if (offenders.length === 0) return;
2599
+ const shown = offenders.slice(0, 5).join(", ");
2600
+ throw new Error(`[animus-extract] runtimeImport '${runtimeImport}' is valid only when every extracted terminal is .asClass(), but ${offenders.length} component(s) need createComponent/createComposedFamily from the default '${DEFAULT_RUNTIME_IMPORT}' runtime: ${shown}` + (offenders.length > 5 ? ", …" : "") + `. Remove the override or convert these terminals to .asClass().`);
2601
+ }
2602
+ //#endregion
2603
+ //#region pipeline/discover-files.ts
2604
+ function pathCharRank(code) {
2605
+ return code === 47 || code === 92 ? -1 : code;
2606
+ }
2607
+ function compareDiscoveryOrder(a, b) {
2608
+ const shared = Math.min(a.length, b.length);
2609
+ for (let i = 0; i < shared; i++) {
2610
+ const left = pathCharRank(a.charCodeAt(i));
2611
+ const right = pathCharRank(b.charCodeAt(i));
2612
+ if (left !== right) return left - right;
2613
+ }
2614
+ return a.length - b.length;
2018
2615
  }
2019
2616
  function discoverFiles(dir, rootDir, exclude, extensionsSet) {
2020
2617
  return walkFiles(dir, rootDir, exclude, extensionsSet).sort(compareDiscoveryOrder);
@@ -2023,7 +2620,7 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
2023
2620
  const results = [];
2024
2621
  let entries;
2025
2622
  try {
2026
- entries = readdirSync(dir, { encoding: "utf8" });
2623
+ entries = readdirSync$1(dir, { encoding: "utf8" });
2027
2624
  } catch {
2028
2625
  return results;
2029
2626
  }
@@ -2033,7 +2630,7 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
2033
2630
  if (isExcluded(fullPath, relative$1(rootDir, fullPath))) continue;
2034
2631
  let stat;
2035
2632
  try {
2036
- stat = statSync(fullPath);
2633
+ stat = statSync$1(fullPath);
2037
2634
  } catch {
2038
2635
  continue;
2039
2636
  }
@@ -2043,6 +2640,161 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
2043
2640
  return results;
2044
2641
  }
2045
2642
  //#endregion
2643
+ //#region pipeline/host-extensions.ts
2644
+ /** Animus's own order, where no host resolver configuration exists. */
2645
+ const BUILT_IN_EXTENSION_POLICY = {
2646
+ appended: EXTENSION_POLICY.appended,
2647
+ nodeNext: EXTENSION_POLICY.nodeNext,
2648
+ alias: [],
2649
+ resolverVersion: "built-in"
2650
+ };
2651
+ /** The TypeScript output a source compiles to, as a host that resolves an
2652
+ * emitted extension to its TypeScript source reads it. */
2653
+ const TYPESCRIPT_OUTPUTS = [
2654
+ [".js", [".ts", ".tsx"]],
2655
+ [".jsx", [".tsx"]],
2656
+ [".mjs", [".mts"]],
2657
+ [".cjs", [".cts"]]
2658
+ ];
2659
+ /** esbuild's rewritten file extensions, which also let `.jsx` name `.ts`. */
2660
+ const ESBUILD_OUTPUTS = [
2661
+ [".js", [".ts", ".tsx"]],
2662
+ [".jsx", [".ts", ".tsx"]],
2663
+ [".mjs", [".mts"]],
2664
+ [".cjs", [".cts"]]
2665
+ ];
2666
+ /** Documented defaults, for a host whose resolved order is not readable. */
2667
+ const HOST_DEFAULT_EXTENSIONS = {
2668
+ vite: [
2669
+ ".mjs",
2670
+ ".js",
2671
+ ".mts",
2672
+ ".ts",
2673
+ ".jsx",
2674
+ ".tsx",
2675
+ ".json"
2676
+ ],
2677
+ webpack: [
2678
+ ".js",
2679
+ ".json",
2680
+ ".wasm"
2681
+ ],
2682
+ /** Rspack 1's documented default; later versions document another. */
2683
+ rspack1: [
2684
+ ".js",
2685
+ ".json",
2686
+ ".wasm"
2687
+ ],
2688
+ turbopack: [
2689
+ ".tsx",
2690
+ ".ts",
2691
+ ".jsx",
2692
+ ".js",
2693
+ ".mjs",
2694
+ ".json"
2695
+ ],
2696
+ esbuild: [
2697
+ ".tsx",
2698
+ ".ts",
2699
+ ".jsx",
2700
+ ".js",
2701
+ ".css",
2702
+ ".json"
2703
+ ]
2704
+ };
2705
+ function label(host, version, how) {
2706
+ return `${version ? `${host}@${version}` : host} ${how}`;
2707
+ }
2708
+ /** Vite's `resolve.extensions`, which `configResolved` holds with its
2709
+ * defaults applied. A TypeScript importer's `./x.js` names `x.ts` too.
2710
+ * Only the default order is captured, as a real build resolves it. A
2711
+ * configured list does not describe Vite's effective resolution: Vite 8's
2712
+ * resolver falls back past it (`['.js']` still loads `./only` as
2713
+ * `only.tsx`), so it is unreadable. */
2714
+ function viteExtensionPolicy(extensions, version) {
2715
+ if (extensions.length !== HOST_DEFAULT_EXTENSIONS.vite.length || extensions.some((ext, i) => ext !== HOST_DEFAULT_EXTENSIONS.vite[i])) return {
2716
+ ...BUILT_IN_EXTENSION_POLICY,
2717
+ resolverVersion: "built-in (hostOptionUnreadable: vite configured extensions)"
2718
+ };
2719
+ return {
2720
+ appended: [...extensions],
2721
+ nodeNext: TYPESCRIPT_OUTPUTS,
2722
+ alias: [],
2723
+ resolverVersion: label("vite", version, "captured")
2724
+ };
2725
+ }
2726
+ /** webpack's or Rspack's resolved order for an ESM import: the root
2727
+ * `resolve` with `byDependency.esm` merged over it as the bundler merges
2728
+ * it, a `...` standing for what it extends. `extensionAlias` is kept whole
2729
+ * and in order. Where neither sets extensions, the bundler's documented
2730
+ * default stands in; for Rspack only version 1's is known. */
2731
+ function webpackExtensionPolicy(host, resolve, version) {
2732
+ const esm = resolve.byDependency?.esm;
2733
+ const unset = resolve.extensions === void 0 && esm?.extensions === void 0;
2734
+ const defaults = !(host === "rspack") ? HOST_DEFAULT_EXTENSIONS.webpack : version?.startsWith("1.") ? HOST_DEFAULT_EXTENSIONS.rspack1 : null;
2735
+ if ([...Object.values(resolve.extensionAlias ?? {}), ...Object.values(esm?.extensionAlias ?? {})].some((value) => !Array.isArray(value))) return {
2736
+ ...BUILT_IN_EXTENSION_POLICY,
2737
+ resolverVersion: "built-in (hostOptionUnreadable: string extensionAlias)"
2738
+ };
2739
+ const extendsBase = (list) => list === void 0 || list.includes("...");
2740
+ if (defaults === null && extendsBase(esm?.extensions) && extendsBase(resolve.extensions)) return {
2741
+ ...BUILT_IN_EXTENSION_POLICY,
2742
+ resolverVersion: `built-in (hostOptionUnreadable: ${host}${version ? `@${version}` : ""} defaults)`
2743
+ };
2744
+ const extend = (list, base) => list === void 0 ? base : list.flatMap((extension) => extension === "..." ? base : [extension]);
2745
+ const root = extend(resolve.extensions, defaults ?? []);
2746
+ const sequence = (value) => Array.isArray(value) ? value : [value];
2747
+ const alias = new Map(Object.entries(resolve.extensionAlias ?? {}).map(([emitted, value]) => [emitted, sequence(value).filter((extension) => extension !== "...")]));
2748
+ for (const [emitted, value] of Object.entries(esm?.extensionAlias ?? {})) {
2749
+ const base = alias.get(emitted) ?? [];
2750
+ alias.set(emitted, sequence(value).flatMap((extension) => extension === "..." ? base : [extension]));
2751
+ }
2752
+ return {
2753
+ appended: [...extend(esm?.extensions, root)],
2754
+ nodeNext: [],
2755
+ alias: [...alias].map(([emitted, extensions]) => [emitted, [...extensions]]),
2756
+ resolverVersion: label(host, version, unset ? "default" : "captured")
2757
+ };
2758
+ }
2759
+ /** Turbopack's `turbopack.resolveExtensions`, or its documented default
2760
+ * when the Next config sets none. It substitutes no source: under Next
2761
+ * 16.3, `./x.js` beside only `x.ts` names no module. */
2762
+ function turbopackExtensionPolicy(resolveExtensions, nextVersion) {
2763
+ return {
2764
+ appended: [...resolveExtensions ?? HOST_DEFAULT_EXTENSIONS.turbopack],
2765
+ nodeNext: [],
2766
+ alias: [],
2767
+ resolverVersion: label("turbopack", nextVersion, resolveExtensions === void 0 ? "default" : "captured")
2768
+ };
2769
+ }
2770
+ /** esbuild's `resolveExtensions`, or its default when the build sets none. */
2771
+ function esbuildExtensionPolicy(resolveExtensions, version) {
2772
+ return {
2773
+ appended: [...resolveExtensions ?? HOST_DEFAULT_EXTENSIONS.esbuild],
2774
+ nodeNext: ESBUILD_OUTPUTS,
2775
+ alias: [],
2776
+ resolverVersion: label("esbuild", version, resolveExtensions === void 0 ? "default" : "captured")
2777
+ };
2778
+ }
2779
+ /** Rollup leaves the extension order to its resolver plugins, which keep
2780
+ * their options private: a TypeScript build sets `@rollup/plugin-node-resolve`'s
2781
+ * `extensions`, which no documented default matches. The built-in policy
2782
+ * answers, with the reason. */
2783
+ function rollupExtensionPolicy(usesNodeResolve) {
2784
+ return {
2785
+ ...BUILT_IN_EXTENSION_POLICY,
2786
+ resolverVersion: usesNodeResolve ? "built-in (hostOptionUnreadable: rollup-node-resolve)" : "built-in (hostOptionUnreadable: rollup resolver plugins)"
2787
+ };
2788
+ }
2789
+ /** The policy as the system loader reads it (`ExtensionPolicy`). */
2790
+ function extensionPolicyJson(policy) {
2791
+ return JSON.stringify({
2792
+ appended: policy.appended,
2793
+ nodeNext: policy.nodeNext,
2794
+ alias: policy.alias
2795
+ });
2796
+ }
2797
+ //#endregion
2046
2798
  //#region pipeline/tsconfig-paths.ts
2047
2799
  /**
2048
2800
  * tsconfig `paths` → alias pairs, for drivers with no live bundler config.
@@ -2129,7 +2881,7 @@ function decodeExtends(value) {
2129
2881
  function readConfig(path) {
2130
2882
  let raw;
2131
2883
  try {
2132
- raw = readFileSync(path, "utf-8");
2884
+ raw = readFileSync$1(path, "utf-8");
2133
2885
  } catch {
2134
2886
  return null;
2135
2887
  }
@@ -2148,16 +2900,27 @@ function readConfig(path) {
2148
2900
  extends: decodeExtends(root.extends)
2149
2901
  };
2150
2902
  }
2151
- function resolveExtendsTarget(specifier, fromDir) {
2903
+ /** Where an `extends` specifier can name a config: a relative one as
2904
+ * written, with `.json`, or as a directory's tsconfig; a package's under
2905
+ * each ancestor's `node_modules`. Resolution takes the first that exists. */
2906
+ function extendsCandidates(specifier, fromDir) {
2152
2907
  if (specifier.startsWith(".") || isAbsolute$1(specifier)) {
2153
2908
  const base = resolve$1(fromDir, specifier);
2154
- for (const candidate of [
2909
+ return [
2155
2910
  base,
2156
2911
  `${base}.json`,
2157
2912
  join$1(base, "tsconfig.json")
2158
- ]) if (existsSync(candidate)) return candidate;
2159
- return null;
2913
+ ];
2914
+ }
2915
+ const target = specifier.endsWith(".json") ? specifier : `${specifier}/tsconfig.json`;
2916
+ const candidates = [];
2917
+ for (let dir = resolve$1(fromDir);; dir = dirname$1(dir)) {
2918
+ candidates.push(join$1(dir, "node_modules", target));
2919
+ if (dirname$1(dir) === dir) return candidates;
2160
2920
  }
2921
+ }
2922
+ function resolveExtendsTarget(specifier, fromDir) {
2923
+ if (specifier.startsWith(".") || isAbsolute$1(specifier)) return extendsCandidates(specifier, fromDir).find((candidate) => existsSync$1(candidate)) ?? null;
2161
2924
  try {
2162
2925
  return __require.resolve(specifier.endsWith(".json") ? specifier : `${specifier}/tsconfig.json`, { paths: [fromDir] });
2163
2926
  } catch {
@@ -2169,9 +2932,8 @@ function resolveExtendsTarget(specifier, fromDir) {
2169
2932
  }
2170
2933
  }
2171
2934
  /** Child-first flattened extends chain starting at `entryPath`. */
2172
- function loadChain(entryPath) {
2935
+ function loadChain(entryPath, visited = /* @__PURE__ */ new Set(), absent = /* @__PURE__ */ new Set()) {
2173
2936
  const chain = [];
2174
- const visited = /* @__PURE__ */ new Set();
2175
2937
  const queue = [entryPath];
2176
2938
  while (queue.length > 0) {
2177
2939
  const path = queue.shift();
@@ -2184,10 +2946,20 @@ function loadChain(entryPath) {
2184
2946
  for (const parent of node.extends) {
2185
2947
  const resolved = resolveExtendsTarget(parent, node.dir);
2186
2948
  if (resolved) queue.push(resolved);
2949
+ else for (const candidate of extendsCandidates(parent, node.dir)) absent.add(candidate);
2187
2950
  }
2188
2951
  }
2189
2952
  return chain;
2190
2953
  }
2954
+ /** Every tsconfig file the project's alias chain reads or looks for, absent
2955
+ * ones included, an unresolved `extends` target's candidates among them:
2956
+ * an edit, creation or deletion of any changes the aliases. */
2957
+ function tsconfigChainPaths(rootDir) {
2958
+ const visited = /* @__PURE__ */ new Set();
2959
+ const absent = /* @__PURE__ */ new Set();
2960
+ loadChain(join$1(rootDir, "tsconfig.json"), visited, absent);
2961
+ return [...visited, ...absent];
2962
+ }
2191
2963
  /**
2192
2964
  * The project's tsconfig path aliases as `buildPathAliasesJson` pairs; empty
2193
2965
  * when no readable tsconfig, or no usable `paths`, exists.
@@ -2252,11 +3024,11 @@ function buildKitDescriptor(manifest) {
2252
3024
  /** Reads the descriptor at a kit's package root; `null` when it has none. */
2253
3025
  function readKitDescriptor(pkgRoot, rootDir) {
2254
3026
  const path = join(pkgRoot, KIT_DESCRIPTOR_FILE);
2255
- if (!existsSync$1(path)) return null;
3027
+ if (!existsSync(path)) return null;
2256
3028
  const packageRoot = posixPath$1(relative(rootDir, pkgRoot));
2257
3029
  let wire;
2258
3030
  try {
2259
- wire = JSON.parse(readFileSync$1(path, "utf-8"));
3031
+ wire = JSON.parse(readFileSync(path, "utf-8"));
2260
3032
  } catch {
2261
3033
  return {
2262
3034
  packageRoot,
@@ -2327,118 +3099,48 @@ function kitDescriptorDiagnostics(records, manifest) {
2327
3099
  return diagnostics;
2328
3100
  }
2329
3101
  //#endregion
2330
- //#region pipeline/source-identity.ts
2331
- /**
2332
- * One derivation of source identity for discovery, watch ingestion, ownership
2333
- * and deletion: event paths are lookup INPUTS only and never fork identity.
2334
- */
2335
- /**
2336
- * True when `target` is `root` or a descendant, decided by `relative()`
2337
- * shape: string prefixing would let `/ui` claim `/ui-old`.
2338
- */
2339
- function isPathWithinRoot(root, target, pathApi = path) {
2340
- const rel = pathApi.relative(root, target);
2341
- return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
2342
- }
2343
- /**
2344
- * True when two paths share a platform root (win32 drive letter or UNC
2345
- * share; always true on posix). Drive letters compare case-insensitively.
2346
- */
2347
- function sharesVolumeRoot(a, b, pathApi = path) {
2348
- const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
2349
- return volumeOf(a) === volumeOf(b);
2350
- }
2351
- function canonicalize(p) {
3102
+ //#region pipeline/workspace-packages.ts
3103
+ /** The parsed `package.json` in `dir`, or null when it cannot be read. */
3104
+ function readManifest(dir) {
2352
3105
  try {
2353
- return realpathSync$1(p);
3106
+ return JSON.parse(readFileSync$1(join$1(dir, "package.json"), "utf-8"));
2354
3107
  } catch {
2355
3108
  return null;
2356
3109
  }
2357
3110
  }
2358
- function createSourceIdentity(rootDir) {
2359
- const normalizedRootDir = path.normalize(rootDir);
2360
- const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
2361
- const roots = [];
2362
- const aliases = /* @__PURE__ */ new Map();
2363
- const owningRecord = (canonicalPath) => {
2364
- let owner = null;
2365
- for (const record of roots) {
2366
- if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
2367
- if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2368
- }
2369
- return owner;
2370
- };
2371
- return {
2372
- rootDir: normalizedRootDir,
2373
- canonicalRootDir,
2374
- registerExternalRoot(root) {
2375
- const asRegistered = path.normalize(root);
2376
- const canonical = canonicalize(asRegistered) ?? asRegistered;
2377
- const existing = roots.find((record) => record.canonical === canonical);
2378
- if (existing) return existing.canonical;
2379
- roots.push({
2380
- asRegistered,
2381
- canonical
2382
- });
2383
- return canonical;
2384
- },
2385
- externalRoots() {
2386
- return roots.map((record) => record.canonical);
2387
- },
2388
- resolveSourceId(inputPath) {
2389
- const lexical = path.normalize(inputPath);
2390
- const canonical = canonicalize(lexical);
2391
- if (canonical === null) return null;
2392
- const owner = owningRecord(canonical);
2393
- const ownedBy = (record) => {
2394
- const pathInRoot = path.relative(record.canonical, canonical);
2395
- return {
2396
- sourceKey: path.relative(normalizedRootDir, path.join(record.asRegistered, pathInRoot)),
2397
- owningRoot: record.canonical,
2398
- pathInRoot
2399
- };
2400
- };
2401
- const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
2402
- let resolved;
2403
- if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
2404
- const key = path.relative(canonicalRootDir, canonical);
2405
- resolved = {
2406
- sourceKey: key,
2407
- owningRoot: null,
2408
- pathInRoot: key
2409
- };
2410
- } else if (owner) resolved = ownedBy(owner);
2411
- else return null;
2412
- aliases.set(lexical, resolved);
2413
- aliases.set(canonical, resolved);
2414
- return resolved;
2415
- },
2416
- resolveDeletedSourceId(inputPath) {
2417
- return aliases.get(path.normalize(inputPath)) ?? null;
2418
- },
2419
- containingExternalRoot(inputPath) {
2420
- const lexical = path.normalize(inputPath);
2421
- let owner = null;
2422
- for (const record of roots) {
2423
- if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
2424
- if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2425
- }
2426
- if (owner) return owner.canonical;
2427
- const canonical = canonicalize(lexical);
2428
- if (canonical === null) return null;
2429
- for (const record of roots) {
2430
- if (!isPathWithinRoot(record.canonical, canonical)) continue;
2431
- if (!owner || record.canonical.length > owner.canonical.length) owner = record;
2432
- }
2433
- return owner?.canonical ?? null;
3111
+ /**
3112
+ * The packages the session root's `package.json` declares as workspaces, by
3113
+ * name, at their real directories, so a linked workspace is one package. The
3114
+ * system loader consults them after a `node_modules` lookup misses. Only
3115
+ * literal entries name a package; a glob entry (`packages/*`) names none
3116
+ * yet. None when the manifest is absent or declares none.
3117
+ */
3118
+ function declaredWorkspacePackages(rootDir) {
3119
+ const rootPkg = readManifest(rootDir);
3120
+ if (!isJsonBlock(rootPkg)) return {};
3121
+ const workspaces = rootPkg.workspaces;
3122
+ const declared = isJsonBlock(workspaces) ? workspaces.packages : workspaces;
3123
+ if (!Array.isArray(declared)) return {};
3124
+ const packages = /* @__PURE__ */ new Map();
3125
+ for (const entry of declared) {
3126
+ if (!isJsonString(entry)) continue;
3127
+ let dir;
3128
+ try {
3129
+ dir = realpathSync$1(resolve$1(rootDir, entry));
3130
+ } catch {
3131
+ continue;
2434
3132
  }
2435
- };
3133
+ const manifest = readManifest(dir);
3134
+ const name = isJsonBlock(manifest) ? manifest.name : void 0;
3135
+ if (isJsonString(name) && name !== "" && !packages.has(name)) packages.set(name, dir);
3136
+ }
3137
+ return Object.fromEntries(packages);
2436
3138
  }
2437
3139
  //#endregion
2438
3140
  //#region pipeline/discover-packages.ts
2439
3141
  function findPackageRoot(absEntryPath) {
2440
3142
  let pkgRoot = dirname$1(absEntryPath);
2441
- while (pkgRoot !== dirname$1(pkgRoot) && !existsSync(join$1(pkgRoot, "package.json"))) pkgRoot = dirname$1(pkgRoot);
3143
+ while (pkgRoot !== dirname$1(pkgRoot) && !existsSync$1(join$1(pkgRoot, "package.json"))) pkgRoot = dirname$1(pkgRoot);
2442
3144
  return pkgRoot;
2443
3145
  }
2444
3146
  const PACKAGE_SRC_EXCLUDES = [
@@ -2460,39 +3162,36 @@ const PACKAGE_OUTPUT_EXCLUDES = [
2460
3162
  ".d.ts",
2461
3163
  ".map"
2462
3164
  ];
2463
- const isFile$1 = (path) => {
2464
- try {
2465
- return statSync(path).isFile();
2466
- } catch {
2467
- return false;
2468
- }
2469
- };
2470
- function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
2471
- return [
2472
- absSpecifier,
2473
- ...Array.from(extensionsSet, (ext) => absSpecifier + ext),
2474
- ...Array.from(extensionsSet, (ext) => join$1(absSpecifier, `index${ext}`))
2475
- ].find(isFile$1) ?? null;
3165
+ /** The file an absolute path specifier names, through the extension
3166
+ * policy, spelled exactly below the directory it shares with `within`
3167
+ * when given; a file differing only in case is a mismatch. */
3168
+ function probeAbsolutePathSpecifier(absSpecifier, extensionsSet, within, policy) {
3169
+ return probeOnDisk(policyCandidates(normalize$1(absSpecifier), {
3170
+ sources: extensionsSet,
3171
+ index: join$1,
3172
+ policy
3173
+ }), within);
2476
3174
  }
2477
3175
  /** The package a bare specifier names: `@scope/name` or `name`. */
2478
3176
  function bareSpecifierPackageName(specifier) {
2479
3177
  const segments = specifier.split("/");
2480
3178
  return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
2481
3179
  }
2482
- function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
3180
+ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet, rootDir, policy) {
2483
3181
  if (isAbsolute$1(specifier)) {
2484
- const resolved = resolveAbsolutePathSpecifier(specifier, extensionsSet);
2485
- if (!resolved) return null;
2486
- return isPathWithinRoot(srcDir, resolved) ? resolved : null;
3182
+ const probed = probeAbsolutePathSpecifier(specifier, extensionsSet, rootDir, policy);
3183
+ if (probed.kind !== "found") return null;
3184
+ return isPathWithinRoot(srcDir, probed.path) ? probed.path : null;
2487
3185
  }
2488
3186
  const packageName = bareSpecifierPackageName(specifier);
2489
3187
  const subpath = specifier.slice(packageName.length + 1);
2490
- return resolveAbsolutePathSpecifier(join$1(srcDir, subpath || "index"), extensionsSet);
3188
+ const probed = probeAbsolutePathSpecifier(join$1(srcDir, subpath || "index"), extensionsSet, srcDir, policy);
3189
+ return probed.kind === "found" ? probed.path : null;
2491
3190
  }
2492
3191
  /** The package's parsed `package.json`, or null when it cannot be read. */
2493
3192
  function readPackageManifest(pkgRoot) {
2494
3193
  try {
2495
- return JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
3194
+ return JSON.parse(readFileSync$1(join$1(pkgRoot, "package.json"), "utf-8"));
2496
3195
  } catch {
2497
3196
  return null;
2498
3197
  }
@@ -2512,53 +3211,52 @@ function exportsSubpaths(manifest) {
2512
3211
  * its source in every host, and the system loader prefers it.
2513
3212
  */
2514
3213
  const KIT_SOURCE_CONDITION = "animus";
2515
- /** The package's kit source condition, or null when no exact `exports`
2516
- * entry declares it: the package is no source kit. */
2517
- function readKitSourceCondition(pkgRoot) {
2518
- const condition = {
2519
- entries: [],
2520
- invalid: [],
2521
- root: null
3214
+ /** The system loader's package locator under the session's declared
3215
+ * `workspaces`: the first `node_modules/<name>` up the tree, then the
3216
+ * workspace of that name. */
3217
+ function nativePackageLocator(engine, workspaces) {
3218
+ const native = engine.locatePackage;
3219
+ return (name, fromDir) => {
3220
+ if (native === void 0) throw new Error("the native engine does not expose locatePackage, which kit discovery needs — rebuild @animus-ui/extract");
3221
+ return native(name, fromDir, workspaces);
2522
3222
  };
2523
- for (const [subpath, value] of exportsSubpaths(readPackageManifest(pkgRoot))) {
2524
- if (subpath.includes("*") || !isJsonBlock(value)) continue;
2525
- const target = value[KIT_SOURCE_CONDITION];
2526
- if (!isJsonString(target)) continue;
2527
- const file = resolve$1(pkgRoot, target);
2528
- if (isPathWithinRoot(pkgRoot, file) && isFile$1(file)) condition.entries.push([subpath, file]);
2529
- else condition.invalid.push([subpath, target]);
2530
- }
2531
- if (condition.entries.length === 0 && condition.invalid.length === 0) return null;
2532
- condition.root = condition.entries.reduce((root, [, file]) => root === null ? dirname$1(file) : commonDirectory(root, dirname$1(file)), null);
2533
- return condition;
2534
- }
2535
- function commonDirectory(a, b) {
2536
- let dir = a;
2537
- while (!isPathWithinRoot(dir, b)) dir = dirname$1(dir);
2538
- return dir;
2539
- }
2540
- /** Whether the manifest at `dir` names the package `name`. */
2541
- function isPackageNamed(dir, name) {
3223
+ }
3224
+ /** The native table's kit source roots under one resolution context. */
3225
+ function nativeKitSourceRoot(engine, fromDir, conditions, workspaces, policyJson) {
3226
+ const native = engine.resolveKitSourceRoot;
3227
+ return (specifier, packageDir, hostEntry) => {
3228
+ if (native === void 0) throw new Error("the native engine does not expose resolveKitSourceRoot, which kit discovery needs — rebuild @animus-ui/extract");
3229
+ const answer = parseInternalWire(native(specifier, fromDir, [...conditions], workspaces, policyJson, hostEntry ?? void 0, packageDir), "resolveKitSourceRoot");
3230
+ if (answer.rule === void 0) return null;
3231
+ return {
3232
+ rule: answer.rule,
3233
+ root: answer.root ?? null,
3234
+ entries: answer.entries ?? [],
3235
+ invalid: answer.invalid ?? [],
3236
+ unresolved: answer.unresolved ?? null
3237
+ };
3238
+ };
3239
+ }
3240
+ /**
3241
+ * Whether `dir`, the nearest manifest above a host's answer, is a package
3242
+ * the host resolved into: its manifest names a package, and it is not the
3243
+ * project or a directory holding it. A host answer from a dependency cache
3244
+ * (`node_modules/.vite/deps`), or from a file outside every package, finds
3245
+ * the application's own manifest, which is no package root. The name need
3246
+ * not be the specifier's: a host alias resolves one package to another,
3247
+ * and the host's package is the one the bundle renders (spec §2.2 A4).
3248
+ */
3249
+ function isHostPackageRoot(dir, rootDir) {
2542
3250
  const manifest = readPackageManifest(dir);
2543
- return isJsonBlock(manifest) && manifest.name === name;
3251
+ return isJsonBlock(manifest) && isJsonString(manifest.name) && manifest.name !== "" && !isPathWithinRoot(realPath(dir), realPath(rootDir));
2544
3252
  }
2545
3253
  function realPath(path) {
2546
3254
  try {
2547
- return realpathSync(path);
3255
+ return realpathSync$1(path);
2548
3256
  } catch {
2549
3257
  return path;
2550
3258
  }
2551
3259
  }
2552
- /** The root of the installed package `name` as Node finds it from `fromDir`,
2553
- * the first `node_modules/<name>` up the directory tree, at its real path,
2554
- * as host resolvers report a linked package. */
2555
- function locatePackageRoot(name, fromDir) {
2556
- for (let dir = fromDir;; dir = dirname$1(dir)) {
2557
- const candidate = join$1(dir, "node_modules", name);
2558
- if (existsSync(join$1(candidate, "package.json"))) return realPath(candidate);
2559
- if (dir === dirname$1(dir)) return null;
2560
- }
2561
- }
2562
3260
  /**
2563
3261
  * A package's `sideEffects` field, decoded the way Vite reads it: a boolean
2564
3262
  * as written, or the list's globs, one with no `/` matching the basename
@@ -2683,7 +3381,7 @@ function packageExportEntries(pkgRoot) {
2683
3381
  function isInstalledPackage(pkgRoot) {
2684
3382
  let realRoot = pkgRoot;
2685
3383
  try {
2686
- realRoot = realpathSync(pkgRoot);
3384
+ realRoot = realpathSync$1(pkgRoot);
2687
3385
  } catch {}
2688
3386
  return realRoot.split(/[\\/]/).includes("node_modules");
2689
3387
  }
@@ -2691,25 +3389,32 @@ function distEntryIsStale(absEntry, srcDir, srcFiles) {
2691
3389
  if (isPathWithinRoot(srcDir, absEntry)) return false;
2692
3390
  let distMtime;
2693
3391
  try {
2694
- distMtime = statSync(absEntry).mtimeMs;
3392
+ distMtime = statSync$1(absEntry).mtimeMs;
2695
3393
  } catch {
2696
3394
  return false;
2697
3395
  }
2698
3396
  let newestSrcMtime = -Infinity;
2699
3397
  for (const srcFile of srcFiles) try {
2700
- newestSrcMtime = Math.max(newestSrcMtime, statSync(srcFile).mtimeMs);
3398
+ newestSrcMtime = Math.max(newestSrcMtime, statSync$1(srcFile).mtimeMs);
2701
3399
  } catch {}
2702
3400
  return distMtime < newestSrcMtime;
2703
3401
  }
2704
3402
  async function collectExternalPackageSources(opts) {
2705
- const { specifiers, resolveSpecifier, rootDir, extensionsSet, hasEntry, onSourceRead, onUnreadable, onPackageResolved } = opts;
3403
+ const { specifiers, unresolvedReason, kitSourceRoot, locatePackage, rootDir, extensionsSet, hasEntry, onSourceRead, onUnreadable, onPackageResolved, requireSourceEntry, extensions, hostOutputDirs = [] } = opts;
3404
+ const answers = /* @__PURE__ */ new Map();
3405
+ const resolveSpecifier = (specifier) => {
3406
+ let answer = answers.get(specifier);
3407
+ if (answer === void 0) {
3408
+ answer = Promise.resolve().then(() => opts.resolveSpecifier(specifier));
3409
+ answers.set(specifier, answer);
3410
+ }
3411
+ return answer;
3412
+ };
2706
3413
  const entries = [];
2707
3414
  const pushed = /* @__PURE__ */ new Set();
2708
3415
  const packageMap = {};
2709
3416
  const sourceEntries = /* @__PURE__ */ new Map();
2710
3417
  const sourceEntrySideEffects = /* @__PURE__ */ new Map();
2711
- /** `replaced` names the entry the redirect stands in for; it is only
2712
- * needed, and so only resolved, for a glob list. */
2713
3418
  /** The entry `subpath` of the package resolves to without the redirect.
2714
3419
  * A resolver with no `import` condition, as Node's `require` has none,
2715
3420
  * finds no ESM-only entry, so the manifest's own target answers then. */
@@ -2720,6 +3425,8 @@ async function collectExternalPackageSources(opts) {
2720
3425
  } catch {}
2721
3426
  return packageExportEntries(pkgRoot).find(([entry]) => entry === subpath)?.[1] ?? null;
2722
3427
  };
3428
+ /** `replaced` names the entry the redirect stands in for; it is only
3429
+ * needed, and so only resolved, for a glob list. */
2723
3430
  const redirect = async (specifier, srcEntry, pkgRoot, replaced) => {
2724
3431
  sourceEntries.set(specifier, srcEntry);
2725
3432
  const field = readPackageSideEffects(pkgRoot);
@@ -2749,7 +3456,7 @@ async function collectExternalPackageSources(opts) {
2749
3456
  if (alreadyIngested(relPath)) return true;
2750
3457
  let source;
2751
3458
  try {
2752
- source = readFileSync(file, "utf-8");
3459
+ source = readFileSync$1(file, "utf-8");
2753
3460
  } catch (err) {
2754
3461
  onUnreadable(relPath, err);
2755
3462
  return false;
@@ -2764,8 +3471,18 @@ async function collectExternalPackageSources(opts) {
2764
3471
  return true;
2765
3472
  };
2766
3473
  const diagnostics = [];
2767
- const reportedPackages = /* @__PURE__ */ new Set();
3474
+ const reported = /* @__PURE__ */ new Set();
3475
+ const report = (packageName, pkgRoot, condition) => {
3476
+ const copy = realPath(pkgRoot);
3477
+ for (const finding of sourceConditionDiagnostics(packageName, condition)) {
3478
+ const key = `${copy}\0${finding.code}\0${finding.message}`;
3479
+ if (reported.has(key)) continue;
3480
+ reported.add(key);
3481
+ diagnostics.push(finding);
3482
+ }
3483
+ };
2768
3484
  const kitDescriptors = [];
3485
+ const packageInstances = {};
2769
3486
  const describedRoots = /* @__PURE__ */ new Set();
2770
3487
  for (const specifier of specifiers) {
2771
3488
  let absEntry;
@@ -2774,23 +3491,78 @@ async function collectExternalPackageSources(opts) {
2774
3491
  } catch {
2775
3492
  absEntry = null;
2776
3493
  }
2777
- if (!absEntry && isAbsolute$1(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
3494
+ let caseMismatch;
3495
+ if (isAbsolute$1(specifier)) {
3496
+ const own = probeAbsolutePathSpecifier(specifier, extensionsSet, rootDir, extensions);
3497
+ const hosted = absEntry === null ? null : normalize$1(absEntry);
3498
+ const seen = hosted === null ? null : seeOnDisk(hosted, rootDir);
3499
+ const probed = own.kind === "case-mismatch" || hosted === null || seen === null ? own : seen.kind === "case-only" ? {
3500
+ kind: "case-mismatch",
3501
+ candidate: hosted,
3502
+ actual: seen.actual
3503
+ } : {
3504
+ kind: "found",
3505
+ path: hosted
3506
+ };
3507
+ absEntry = probed.kind === "found" ? probed.path : null;
3508
+ if (probed.kind === "case-mismatch") caseMismatch = caseMismatchReason(`'${specifier}'`, probed, "spell the include as the file is spelled");
3509
+ }
2778
3510
  const packageName = isAbsolute$1(specifier) ? null : bareSpecifierPackageName(specifier);
2779
- const resolvedRoot = absEntry ? findPackageRoot(absEntry) : null;
2780
- const locatedRoot = packageName && locatePackageRoot(packageName, rootDir);
2781
- const pkgRoot = resolvedRoot && (!locatedRoot || isPackageNamed(resolvedRoot, packageName) || realPath(resolvedRoot) === locatedRoot) ? resolvedRoot : locatedRoot || resolvedRoot;
2782
- const condition = packageName && pkgRoot ? readKitSourceCondition(pkgRoot) : null;
3511
+ let resolvedRoot = absEntry ? findPackageRoot(absEntry) : null;
3512
+ const locatedRoot = packageName && locatePackage(packageName, opts.locateFrom?.(specifier) ?? rootDir);
3513
+ if (resolvedRoot && locatedRoot && absEntry && realPath(resolvedRoot) !== locatedRoot && isPathWithinRoot(locatedRoot, realPath(absEntry))) {
3514
+ while (realPath(resolvedRoot) !== locatedRoot && resolvedRoot !== dirname$1(resolvedRoot)) resolvedRoot = dirname$1(resolvedRoot);
3515
+ if (realPath(resolvedRoot) !== locatedRoot) resolvedRoot = locatedRoot;
3516
+ }
3517
+ const pkgRoot = resolvedRoot && (!locatedRoot || isHostPackageRoot(resolvedRoot, rootDir) || realPath(resolvedRoot) === locatedRoot) ? resolvedRoot : locatedRoot || resolvedRoot;
3518
+ if (absEntry && packageName && locatedRoot && pkgRoot === locatedRoot && !isPathWithinRoot(locatedRoot, realPath(absEntry)) && !realPath(absEntry).split(sep$1).includes("node_modules")) diagnostics.push(packageMismatchDiagnostic(specifier, relative$1(rootDir, absEntry), relative$1(rootDir, locatedRoot)));
3519
+ const selection = packageName && pkgRoot ? kitSourceRoot(specifier, pkgRoot, absEntry) : null;
3520
+ if (selection?.unresolved) {
3521
+ absEntry = null;
3522
+ caseMismatch = selection.unresolved;
3523
+ }
3524
+ const condition = selection && (selection.rule === "animus" || selection.rule === "source-exports" || selection.invalid.length > 0) ? {
3525
+ entries: selection.entries,
3526
+ invalid: selection.invalid,
3527
+ root: selection.rule === "compiled" ? null : selection.root
3528
+ } : null;
3529
+ if (selection?.unresolved && condition && packageName && pkgRoot) report(packageName, pkgRoot, condition);
2783
3530
  const entryKey = packageName && `.${specifier.slice(packageName.length)}`;
2784
3531
  absEntry ??= condition?.entries.find(([entry]) => entry === entryKey)?.[1] ?? null;
2785
3532
  if (!absEntry || !pkgRoot) {
2786
- outcomes.push({
3533
+ const record = {
2787
3534
  specifier,
2788
3535
  outcome: "unresolvable",
2789
3536
  fileCount: 0
3537
+ };
3538
+ const reason = caseMismatch ?? unresolvedReason?.(specifier);
3539
+ if (reason !== void 0) record.reason = reason;
3540
+ outcomes.push(record);
3541
+ continue;
3542
+ }
3543
+ const hostOutput = hostOutputDirs.find((dir) => isPathWithinRoot(dir, pkgRoot) || isPathWithinRoot(realPath(dir), realPath(pkgRoot)));
3544
+ if (hostOutput !== void 0) {
3545
+ outcomes.push({
3546
+ specifier,
3547
+ outcome: "unresolvable",
3548
+ fileCount: 0,
3549
+ reason: `the package lies inside the host's output directory ${hostOutput}, which is never source`
2790
3550
  });
2791
3551
  continue;
2792
3552
  }
3553
+ if (requireSourceEntry?.(specifier)) {
3554
+ if (!(condition && packageName ? condition.entries.some(([entry]) => entry === entryKey) : selection?.rule === "src" && sourceEntryForSpecifier(specifier, join$1(pkgRoot, "src"), extensionsSet, rootDir, extensions) !== null)) {
3555
+ outcomes.push({
3556
+ specifier,
3557
+ outcome: "unresolvable",
3558
+ fileCount: 0,
3559
+ reason: "the host does not resolve it, and the system loader finds only compiled output that no source entry redirects to — link the package into node_modules, or give it an \"animus\" source condition"
3560
+ });
3561
+ continue;
3562
+ }
3563
+ }
2793
3564
  const realRoot = realPath(pkgRoot);
3565
+ if (packageName) packageInstances[packageName] ??= realRoot;
2794
3566
  if (!describedRoots.has(realRoot)) {
2795
3567
  describedRoots.add(realRoot);
2796
3568
  const described = readKitDescriptor(pkgRoot, rootDir);
@@ -2800,10 +3572,24 @@ async function collectExternalPackageSources(opts) {
2800
3572
  const srcDir = join$1(pkgRoot, "src");
2801
3573
  let fileCount = 0;
2802
3574
  let staleDist = false;
2803
- if (packageName && !reportedPackages.has(packageName)) {
2804
- reportedPackages.add(packageName);
2805
- diagnostics.push(...sourceConditionDiagnostics(packageName, condition));
2806
- }
3575
+ if (packageName) report(packageName, pkgRoot, condition);
3576
+ const ingestOutput = (entryFile) => {
3577
+ const outputDir = dirname$1(entryFile);
3578
+ packageDirs.push(outputDir);
3579
+ if (linked) linkedDirs.push(outputDir);
3580
+ outputDirs.push(outputDir);
3581
+ claimDir(outputDir, specifier);
3582
+ onPackageResolved?.(specifier, outputDir);
3583
+ const outputExtensions = new Set(extensionsSet);
3584
+ outputExtensions.add(extname$1(entryFile));
3585
+ dirExtensions[outputDir] = [...outputExtensions];
3586
+ const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
3587
+ const relToOutput = relative$1(outputDir, file);
3588
+ return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
3589
+ });
3590
+ if (!outputFiles.includes(entryFile)) outputFiles.unshift(entryFile);
3591
+ for (const outputFile of outputFiles) if (ingest(outputFile, specifier)) fileCount++;
3592
+ };
2807
3593
  if (condition && packageName) {
2808
3594
  for (const [entry, target] of condition.entries) {
2809
3595
  const entrySpecifier = packageName + entry.slice(1);
@@ -2811,6 +3597,7 @@ async function collectExternalPackageSources(opts) {
2811
3597
  await redirect(entrySpecifier, target, pkgRoot, () => shippedEntry(entrySpecifier, pkgRoot, entry));
2812
3598
  }
2813
3599
  packageMap[specifier] ??= relative$1(rootDir, absEntry);
3600
+ if (!condition.entries.some(([entry]) => entry === entryKey)) ingestOutput(absEntry);
2814
3601
  if (condition.root) {
2815
3602
  packageDirs.push(condition.root);
2816
3603
  if (linked) linkedDirs.push(condition.root);
@@ -2819,13 +3606,13 @@ async function collectExternalPackageSources(opts) {
2819
3606
  onPackageResolved?.(specifier, condition.root);
2820
3607
  for (const file of walkPackageSources(condition.root, extensionsSet)) if (ingest(file, specifier)) fileCount++;
2821
3608
  }
2822
- } else if (existsSync(srcDir)) {
3609
+ } else if (selection === null ? existsSync$1(srcDir) : selection.rule === "src") {
2823
3610
  packageDirs.push(srcDir);
2824
3611
  if (linked) linkedDirs.push(srcDir);
2825
3612
  claimDir(srcDir, specifier);
2826
3613
  dirExtensions[srcDir] = [...extensionsSet];
2827
3614
  onPackageResolved?.(specifier, srcDir);
2828
- const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet);
3615
+ const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet, rootDir, extensions);
2829
3616
  if (srcEntry) {
2830
3617
  packageMap[specifier] = relative$1(rootDir, srcEntry);
2831
3618
  const replaced = absEntry;
@@ -2833,7 +3620,7 @@ async function collectExternalPackageSources(opts) {
2833
3620
  } else packageMap[specifier] = relative$1(rootDir, absEntry);
2834
3621
  if (packageName) {
2835
3622
  if (packageName !== specifier && !(packageName in packageMap)) {
2836
- const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
3623
+ const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet, rootDir, extensions);
2837
3624
  if (rootEntry) {
2838
3625
  packageMap[packageName] = relative$1(rootDir, rootEntry);
2839
3626
  await redirect(packageName, rootEntry, pkgRoot, () => shippedEntry(packageName, pkgRoot, "."));
@@ -2844,22 +3631,8 @@ async function collectExternalPackageSources(opts) {
2844
3631
  staleDist = linked && distEntryIsStale(absEntry, srcDir, pkgFiles);
2845
3632
  for (const pkgFile of pkgFiles) if (ingest(pkgFile, specifier)) fileCount++;
2846
3633
  } else {
2847
- const outputDir = dirname$1(absEntry);
2848
- packageDirs.push(outputDir);
2849
- if (linked) linkedDirs.push(outputDir);
2850
- outputDirs.push(outputDir);
2851
- claimDir(outputDir, specifier);
2852
- onPackageResolved?.(specifier, outputDir);
2853
3634
  packageMap[specifier] = relative$1(rootDir, absEntry);
2854
- const outputExtensions = new Set(extensionsSet);
2855
- outputExtensions.add(extname$1(absEntry));
2856
- dirExtensions[outputDir] = [...outputExtensions];
2857
- const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
2858
- const relToOutput = relative$1(outputDir, file);
2859
- return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
2860
- });
2861
- if (!outputFiles.includes(absEntry)) outputFiles.unshift(absEntry);
2862
- for (const outputFile of outputFiles) if (ingest(outputFile, specifier)) fileCount++;
3635
+ ingestOutput(absEntry);
2863
3636
  }
2864
3637
  if (packageName) for (const [subpath, target] of packageExportEntries(pkgRoot)) {
2865
3638
  const entrySpecifier = packageName + subpath.slice(1);
@@ -2867,6 +3640,16 @@ async function collectExternalPackageSources(opts) {
2867
3640
  if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
2868
3641
  packageMap[entrySpecifier] = targetRelPath;
2869
3642
  }
3643
+ const ownManifest = readPackageManifest(pkgRoot);
3644
+ const hostName = isJsonBlock(ownManifest) ? ownManifest.name : void 0;
3645
+ if (packageName && locatedRoot && realPath(pkgRoot) !== locatedRoot && isJsonString(hostName) && hostName !== "" && hostName !== packageName) for (const [key, entry] of [...sourceEntries]) {
3646
+ if (key !== packageName && !key.startsWith(`${packageName}/`)) continue;
3647
+ const hostKey = hostName + key.slice(packageName.length);
3648
+ if (sourceEntries.has(hostKey)) continue;
3649
+ sourceEntries.set(hostKey, entry);
3650
+ const sideEffects = sourceEntrySideEffects.get(key);
3651
+ if (sideEffects !== void 0) sourceEntrySideEffects.set(hostKey, sideEffects);
3652
+ }
2870
3653
  outcomes.push({
2871
3654
  specifier,
2872
3655
  outcome: staleDist ? "stale-dist" : fileCount > 0 ? "resolved" : "empty",
@@ -2886,7 +3669,8 @@ async function collectExternalPackageSources(opts) {
2886
3669
  fileOwners,
2887
3670
  outcomes,
2888
3671
  diagnostics: [...diagnostics, ...noKitFilesDiagnostics(outcomes)],
2889
- kitDescriptors
3672
+ kitDescriptors,
3673
+ packageInstances
2890
3674
  };
2891
3675
  }
2892
3676
  /** The dependency names a manifest lists in `fields`. */
@@ -2899,14 +3683,22 @@ function dependencyNames(manifest, fields) {
2899
3683
  }
2900
3684
  return [...names];
2901
3685
  }
3686
+ /** Whether the package at `pkgRoot` declares the kit source condition, as
3687
+ * the system loader's table reads its manifest: an `animus` key anywhere in
3688
+ * its `exports`, on any entry, subpath or pattern, valid or not. */
3689
+ function declaresKitSource(engine, pkgRoot) {
3690
+ if (engine.declaresKitSource === void 0) throw new Error("the native engine does not expose declaresKitSource, which kit discovery needs — rebuild @animus-ui/extract");
3691
+ return engine.declaresKitSource(pkgRoot);
3692
+ }
2902
3693
  /** The source kits the application at `rootDir` declares as dependencies.
2903
3694
  * A bundler serves an installed one as compiled code unless told
2904
3695
  * otherwise: Next keeps it external on the server unless it is in
2905
3696
  * `transpilePackages`, and Vite's optimizer prebundles it. */
2906
- function sourceKitDependencies(rootDir) {
3697
+ function sourceKitDependencies(rootDir, engine) {
3698
+ const locatePackage = nativePackageLocator(engine, declaredWorkspacePackages(rootDir));
2907
3699
  return dependencyNames(readPackageManifest(rootDir), ["dependencies", "devDependencies"]).flatMap((name) => {
2908
- const pkgRoot = locatePackageRoot(name, rootDir);
2909
- if (pkgRoot === null || readKitSourceCondition(pkgRoot) === null) return [];
3700
+ const pkgRoot = locatePackage(name, rootDir);
3701
+ if (pkgRoot === null || !declaresKitSource(engine, pkgRoot)) return [];
2910
3702
  return [{
2911
3703
  name,
2912
3704
  installed: isInstalledPackage(pkgRoot),
@@ -2914,6 +3706,34 @@ function sourceKitDependencies(rootDir) {
2914
3706
  }];
2915
3707
  });
2916
3708
  }
3709
+ /**
3710
+ * The installed packages a system's membership reaches: each is a kit,
3711
+ * whether or not it declares the source condition, and so is every kit its
3712
+ * own system extends or includes. Read before any host resolves (a
3713
+ * bundler's config), so the application's modules import as the system
3714
+ * loader resolves them from the root, and a kit's from its own directory.
3715
+ */
3716
+ async function systemKitDependencies(rootDir, systemPath, engine) {
3717
+ const workspaces = declaredWorkspacePackages(rootDir);
3718
+ const resolveSpecifier = engine.resolveSpecifier;
3719
+ const systemPackageRoot = findPackageRoot(systemPath);
3720
+ const kitMembers = /* @__PURE__ */ new Map();
3721
+ const members = await extractSystemFilePackages(systemPath, engine, void 0, (specifier, importer) => resolveSpecifier?.(specifier, findPackageRoot(importer) === systemPackageRoot ? rootDir : dirname$1(importer), [], workspaces, extensionPolicyJson(BUILT_IN_EXTENSION_POLICY))?.path ?? null, void 0, kitMembers);
3722
+ const locatePackage = nativePackageLocator(engine, workspaces);
3723
+ const roots = /* @__PURE__ */ new Map();
3724
+ for (const member of members) {
3725
+ if (!isPackageSpecifier(member)) continue;
3726
+ const name = bareSpecifierPackageName(member);
3727
+ const file = kitMembers.get(member);
3728
+ const pkgRoot = file ? findPackageRoot(file) : locatePackage(name, rootDir);
3729
+ if (pkgRoot !== null && !roots.has(name)) roots.set(name, pkgRoot);
3730
+ }
3731
+ return [...roots].map(([name, pkgRoot]) => ({
3732
+ name,
3733
+ installed: isInstalledPackage(pkgRoot),
3734
+ dependencies: dependencyNames(readPackageManifest(pkgRoot), ["dependencies"])
3735
+ }));
3736
+ }
2917
3737
  /** A specifier that names a package: not relative or absolute, and no URL,
2918
3738
  * scheme (`node:`, `virtual:`), subpath import or virtual id. */
2919
3739
  function isPackageSpecifier(specifier) {
@@ -2929,16 +3749,13 @@ function isPackageSpecifier(specifier) {
2929
3749
  * file the engine parses as it is; an adapted source (`.svelte`, `.mdx`)
2930
3750
  * reaches the engine only as its generated children.
2931
3751
  */
2932
- function importedKitDiagnostics(files, engine, rootDir, systemKits) {
2933
- const { extractFacts } = engine;
3752
+ function importedKitDiagnostics(files, engine, rootDir, systemKits, factsOf) {
2934
3753
  const parsed = files.filter(({ path }) => isEngineTransformExtension(path));
2935
- if (!extractFacts || parsed.length === 0) return [];
3754
+ if (parsed.length === 0) return [];
3755
+ const locatePackage = nativePackageLocator(engine, declaredWorkspacePackages(rootDir));
2936
3756
  let facts;
2937
3757
  try {
2938
- facts = parseInternalWire(extractFacts(JSON.stringify(parsed.map(({ path, source }) => ({
2939
- path,
2940
- source
2941
- })))), "extractFacts");
3758
+ facts = factsOf(parsed);
2942
3759
  } catch {
2943
3760
  return [];
2944
3761
  }
@@ -2952,8 +3769,8 @@ function importedKitDiagnostics(files, engine, rootDir, systemKits) {
2952
3769
  const name = bareSpecifierPackageName(specifier);
2953
3770
  if (seen.has(name)) continue;
2954
3771
  seen.add(name);
2955
- const pkgRoot = locatePackageRoot(name, dirname$1(resolve$1(rootDir, path)));
2956
- if (pkgRoot && readKitSourceCondition(pkgRoot)) diagnostics.push({
3772
+ const pkgRoot = locatePackage(name, dirname$1(resolve$1(rootDir, path)));
3773
+ if (pkgRoot && declaresKitSource(engine, pkgRoot)) diagnostics.push({
2957
3774
  file: path,
2958
3775
  component: name,
2959
3776
  kind: "warn",
@@ -2966,7 +3783,19 @@ function importedKitDiagnostics(files, engine, rootDir, systemKits) {
2966
3783
  return diagnostics;
2967
3784
  }
2968
3785
  /** A kit without the source condition, warned once, or each condition entry
2969
- * whose target is missing or outside the package. */
3786
+ * the loader does not read as source: missing, spelled otherwise on disk,
3787
+ * no package target Node accepts (each read from its runtime target), or
3788
+ * percent-escaped (refused, failing the load). */
3789
+ function packageMismatchDiagnostic(specifier, hostEntry, located) {
3790
+ return {
3791
+ file: specifier,
3792
+ component: "kit",
3793
+ kind: "warn",
3794
+ message: `the host bundler resolves '${specifier}' to ${hostEntry}, which lies in no package, so discovery reads the installed package at ${located} instead and the bundle may render another module than the one analysed — make the bundler resolve '${specifier}' to a package`,
3795
+ code: PACKAGE_MISMATCH,
3796
+ severity: severityFor(PACKAGE_MISMATCH)
3797
+ };
3798
+ }
2970
3799
  function sourceConditionDiagnostics(packageName, condition) {
2971
3800
  if (condition === null) return [{
2972
3801
  file: packageName,
@@ -2976,11 +3805,11 @@ function sourceConditionDiagnostics(packageName, condition) {
2976
3805
  code: KIT_WITHOUT_SOURCE_CONDITION,
2977
3806
  severity: severityFor(KIT_WITHOUT_SOURCE_CONDITION)
2978
3807
  }];
2979
- return condition.invalid.map(([subpath, target]) => ({
3808
+ return condition.invalid.map(([subpath, target, why]) => ({
2980
3809
  file: packageName,
2981
3810
  component: "kit",
2982
3811
  kind: "warn",
2983
- message: `exports["${subpath}"].${KIT_SOURCE_CONDITION} names ${target}, which is missing or outside the package, so that entry is not read from source`,
3812
+ message: why.includes("percent-escape unsupported") ? `exports["${subpath}"].${KIT_SOURCE_CONDITION} names ${target}, which ${why}, so the system loader fails to load that entry` : `exports["${subpath}"].${KIT_SOURCE_CONDITION} names ${target}, which ${why}, so that entry is read from its runtime target instead of source`,
2984
3813
  code: INVALID_KIT_SOURCE_CONDITION,
2985
3814
  severity: severityFor(INVALID_KIT_SOURCE_CONDITION)
2986
3815
  }));
@@ -3037,13 +3866,16 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
3037
3866
  fileOwners,
3038
3867
  outcomes: collected.outcomes,
3039
3868
  diagnostics: collected.diagnostics,
3040
- kitDescriptors: collected.kitDescriptors.filter((record) => !rejectedDirs.some((dir) => isPathWithinRoot(resolve$1(rootDir, record.packageRoot), dir)))
3869
+ kitDescriptors: collected.kitDescriptors.filter((record) => !rejectedDirs.some((dir) => isPathWithinRoot(resolve$1(rootDir, record.packageRoot), dir))),
3870
+ packageInstances: Object.fromEntries(Object.entries(collected.packageInstances).filter(([, root]) => !rejectedDirs.some((dir) => isPathWithinRoot(root, dir))))
3041
3871
  };
3042
3872
  }
3043
- function unresolvableIncludesMessage(outcomes) {
3044
- const unresolvable = outcomes.filter((record) => record.outcome === "unresolvable").map((record) => record.specifier);
3873
+ /** Every unresolvable include, with why where the resolver said, and the
3874
+ * system file that includes them when the caller knows it. */
3875
+ function unresolvableIncludesMessage(outcomes, importer) {
3876
+ const unresolvable = outcomes.filter((record) => record.outcome === "unresolvable").map((record) => record.reason === void 0 ? record.specifier : `${record.specifier} (${record.reason})`);
3045
3877
  if (unresolvable.length === 0) return null;
3046
- return `[animus-extract] unresolvable include specifier(s): ${unresolvable.join(", ")}`;
3878
+ return `[animus-extract] unresolvable include specifier(s)${importer === void 0 ? "" : ` in ${importer}`}: ${unresolvable.join(", ")}`;
3047
3879
  }
3048
3880
  /** A stale dist silently skews merged registry content while discovery
3049
3881
  * compiles the fresh sources; null when no specifier is stale. */
@@ -3060,192 +3892,38 @@ function engineModuleParser(engine) {
3060
3892
  if (!extractFacts) return void 0;
3061
3893
  return (source, path) => {
3062
3894
  try {
3063
- const facts = parseInternalWire(extractFacts(JSON.stringify([{
3064
- path,
3065
- source
3066
- }])), "extractFacts").files[path];
3067
- return facts && !facts.parsePanicked ? {
3068
- imports: facts.imports,
3069
- exports: facts.exports,
3070
- unboundCreateSystemCalls: facts.unboundCreateSystemCalls ?? []
3071
- } : null;
3072
- } catch {
3073
- return null;
3074
- }
3075
- };
3076
- }
3077
- const ANIMUS_SYSTEM_SPECIFIER = /^@animus-ui\/system(?:\/.*)?$/;
3078
- /** The file a specifier names from `importer`: a relative one by the
3079
- * ingestion's probe order, a bare one by the host's package resolution. */
3080
- async function resolveModuleFile(importer, specifier, resolvePackage) {
3081
- if (specifier.startsWith(".")) return relativeSourceCandidates(importer, specifier).find(isFile$1) ?? null;
3082
- try {
3083
- return await resolvePackage?.(specifier) ?? null;
3084
- } catch {
3085
- return null;
3086
- }
3087
- }
3088
- const escapeRegExp$1 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3089
- /**
3090
- * Follows `name`, exported by the module `specifier` names from `importer`,
3091
- * through re-exports to its declaration. It is Animus's factory only when
3092
- * that is `createSystem` of `@animus-ui/system`, and another identity only
3093
- * when a module is shown to declare it; anything discovery cannot follow is
3094
- * unknown.
3095
- */
3096
- async function exportIdentity(importer, specifier, name, following, seen) {
3097
- if (ANIMUS_SYSTEM_SPECIFIER.test(specifier)) return name === "createSystem" ? { kind: "animus" } : {
3098
- kind: "other",
3099
- what: `'${name}' of ${specifier}`
3100
- };
3101
- const file = await resolveModuleFile(importer, specifier, following.resolvePackage);
3102
- if (file === null) return {
3103
- kind: "unknown",
3104
- why: `'${specifier}' could not be resolved`
3105
- };
3106
- const key = `${file}#${name}`;
3107
- if (seen.has(key)) return {
3108
- kind: "unknown",
3109
- why: `'${specifier}' re-exports it in a cycle`
3110
- };
3111
- seen.add(key);
3112
- let source;
3113
- try {
3114
- source = readFileSync(file, "utf-8");
3115
- } catch {
3116
- return {
3117
- kind: "unknown",
3118
- why: `'${specifier}' could not be read`
3119
- };
3120
- }
3121
- const record = following.parseModule(source, file);
3122
- if (record === null) return {
3123
- kind: "unknown",
3124
- why: `'${specifier}' could not be parsed`
3125
- };
3126
- const exported = record.exports.find((entry) => entry.exported === name);
3127
- if (exported?.source) return exportIdentity(file, exported.source, exported.original ?? name, following, seen);
3128
- const local = exported?.local ?? name;
3129
- const imported = record.imports.find((entry) => entry.local === local);
3130
- if (exported && imported) return exportIdentity(file, imported.source, imported.imported, following, seen);
3131
- if (exported || new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function\\*?|class|const|let|var)\\s+${escapeRegExp$1(name)}\\b`).test(source)) return {
3132
- kind: "other",
3133
- what: `the '${name}' that '${specifier}' declares`
3134
- };
3135
- const stars = [];
3136
- for (const match of source.matchAll(/\bexport\s*\*\s*from\s*['"]([^'"]+)['"]/g)) stars.push(await exportIdentity(file, match[1], name, following, seen));
3137
- const definite = stars.filter((identity) => identity.kind !== "unknown");
3138
- if (definite.length === 1) return definite[0];
3139
- if (definite.length > 1) return {
3140
- kind: "unknown",
3141
- why: `more than one \`export *\` of '${specifier}' provides '${name}'`
3142
- };
3143
- return stars.find((identity) => identity.kind === "unknown") ?? {
3144
- kind: "unknown",
3145
- why: `'${specifier}' has no export '${name}' discovery can read`
3146
- };
3147
- }
3148
- /** 1-based line and column of `offset` in `source`. */
3149
- function locationIn(source, offset) {
3150
- const before = source.slice(0, offset);
3151
- const lineStart = before.lastIndexOf("\n") + 1;
3152
- return {
3153
- line: before.split("\n").length,
3154
- column: offset - lineStart + 1
3155
- };
3156
- }
3157
- const NAMESPACE_IMPORT = /\bimport\s+(?:[a-zA-Z_$][a-zA-Z0-9_$]*\s*,\s*)?\*\s*as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s+from\s+['"]([^'"]+)['"]/g;
3158
- /**
3159
- * The callees that are Animus's `createSystem` in a parsed system file. A
3160
- * binding counts once its identity is followed to the factory: a named
3161
- * import, through local and package re-exports; a namespace import's
3162
- * member; or a property destructured from a namespace import, `require` or
3163
- * `import()` of a module. A binding named `createSystem` shown to be
3164
- * something else is no root. One whose identity discovery cannot follow
3165
- * keeps the admission spelling gave it, so a resolution limit never drops a
3166
- * kit. Both are reported. A call of the bare name, which the file neither
3167
- * imports nor declares, is no root: the system loader evaluates the file
3168
- * without auto-imports, where the name is undefined. It is reported once.
3169
- */
3170
- async function systemRoots(systemFilePath, source, record, following) {
3171
- const { imports } = record;
3172
- const roots = /* @__PURE__ */ new Set();
3173
- const identityOf = (specifier, name) => exportIdentity(systemFilePath, specifier, name, following, /* @__PURE__ */ new Set());
3174
- const called = (callee) => callOf(callee).test(source);
3175
- /** Admits a binding named `createSystem` by its identity: an unknown one
3176
- * keeps `spellingAdmits`, and every unproven one is reported. */
3177
- const admit = (local, offset, identity, spellingAdmits) => {
3178
- if (identity.kind === "animus") {
3179
- roots.add(local);
3180
- return;
3181
- }
3182
- if (identity.kind === "unknown" && spellingAdmits) roots.add(local);
3183
- const shown = identity.kind === "other" ? `it is ${identity.what}, so it is not read as a system root` : `discovery cannot follow what it is bound to (${identity.why}), so it is ${spellingAdmits ? "still read" : "not read"} as a system root by its name alone`;
3184
- following.report?.({
3185
- file: systemFilePath,
3186
- component: local,
3187
- kind: "warn",
3188
- message: `'${local}' is not proven to be Animus's createSystem: ${shown} — import createSystem from '@animus-ui/system', directly or through a re-export discovery can follow`,
3189
- code: UNPROVEN_ROOT_BINDING,
3190
- severity: severityFor(UNPROVEN_ROOT_BINDING),
3191
- ...locationIn(source, offset)
3192
- });
3193
- };
3194
- for (const binding of imports) {
3195
- if (binding.imported === "createSystem" || binding.local === "createSystem") {
3196
- const statement = new RegExp(`\\bimport\\b[^;]*?\\bfrom\\s*['"]${escapeRegExp$1(binding.source)}['"]`, "g");
3197
- let offset = 0;
3198
- for (const match of source.matchAll(statement)) {
3199
- const at = match[0].search(new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp$1(binding.local)}\\b`));
3200
- if (at !== -1) {
3201
- offset = match.index + at;
3202
- break;
3203
- }
3204
- }
3205
- admit(binding.local, offset, await identityOf(binding.source, binding.imported), binding.imported === "createSystem");
3206
- continue;
3895
+ const facts = parseInternalWire(extractFacts(JSON.stringify([{
3896
+ path,
3897
+ source
3898
+ }])), "extractFacts").files[path];
3899
+ return facts && !facts.parsePanicked ? {
3900
+ imports: facts.imports,
3901
+ exports: facts.exports,
3902
+ unboundCreateSystemCalls: facts.unboundCreateSystemCalls ?? []
3903
+ } : null;
3904
+ } catch {
3905
+ return null;
3207
3906
  }
3208
- if (binding.source.startsWith(".") && called(binding.local) && (await identityOf(binding.source, binding.imported)).kind === "animus") roots.add(binding.local);
3209
- }
3210
- const namespaces = /* @__PURE__ */ new Map();
3211
- for (const [, namespace, specifier] of source.matchAll(NAMESPACE_IMPORT)) {
3212
- namespaces.set(namespace, specifier);
3213
- const member = `${namespace}.createSystem`;
3214
- if (ANIMUS_SYSTEM_SPECIFIER.test(specifier) || specifier.startsWith(".") && called(member) && (await identityOf(specifier, "createSystem")).kind === "animus") roots.add(member);
3215
- }
3216
- const destructuredFrom = (initializer) => {
3217
- const loaded = /^\(?\s*(?:(?:await\s+)?import|require)\(\s*['"]([^'"]+)['"]\s*\)\s*\)?$/.exec(initializer);
3218
- if (loaded) return identityOf(loaded[1], "createSystem");
3219
- const namespace = namespaces.get(initializer);
3220
- if (namespace !== void 0) return identityOf(namespace, "createSystem");
3221
- return Promise.resolve({
3222
- kind: "unknown",
3223
- why: `it is destructured from '${initializer}', which is not a module namespace`
3224
- });
3225
3907
  };
3226
- for (const { local, offset, initializer } of destructuredCreateSystems(source)) admit(local, offset, await destructuredFrom(initializer), local === "createSystem");
3227
- const unimported = unimportedCreateSystemCall(systemFilePath, record);
3228
- if (unimported) following.report?.(unimported);
3229
- return roots;
3230
- }
3231
- /** A call of `callee` as a whole name, not a member. */
3232
- function callOf(callee) {
3233
- return new RegExp(`(?<![a-zA-Z0-9_$.])${escapeRegExp$1(callee)}\\s*\\(`);
3234
- }
3235
- /** Each `createSystem` property a declaration destructures: its local name,
3236
- * its offset in `source`, and the destructured initializer. */
3237
- function* destructuredCreateSystems(source) {
3238
- for (const match of source.matchAll(/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*([^;\n]+)/g)) {
3239
- const [whole, pattern, initializer] = match;
3240
- for (const property of pattern.split(",")) {
3241
- const binding = /^\s*createSystem\s*(?::\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*(?:=[\s\S]*)?$/.exec(property);
3242
- if (!binding) continue;
3243
- yield {
3244
- local: binding[1] ?? "createSystem",
3245
- offset: match.index + whole.indexOf("createSystem"),
3246
- initializer: initializer.trim()
3247
- };
3248
- }
3908
+ }
3909
+ /** The file a specifier names from `importer`, or why it names none: a
3910
+ * relative one by the ingestion's probe order, spelled exactly, a bare one
3911
+ * by the host's package resolution. */
3912
+ async function resolveModuleFile(importer, specifier, resolvePackage, extensions) {
3913
+ const unresolved = { why: `'${specifier}' could not be resolved` };
3914
+ if (specifier.startsWith(".")) {
3915
+ const probed = probeOnDisk(relativeSourceCandidates(importer, specifier, extensions), dirname$1(importer));
3916
+ if (probed.kind === "found") return { file: probed.path };
3917
+ return probed.kind === "case-mismatch" ? {
3918
+ why: caseMismatchReason(`'${specifier}'`, probed, "spell the import as the file is spelled"),
3919
+ caseMismatch: true
3920
+ } : unresolved;
3921
+ }
3922
+ try {
3923
+ const file = await resolvePackage?.(specifier, importer);
3924
+ return file ? { file: file.split("?")[0] } : unresolved;
3925
+ } catch {
3926
+ return unresolved;
3249
3927
  }
3250
3928
  }
3251
3929
  /** The warning for a `createSystem` call that the parser's scopes leave
@@ -3265,229 +3943,427 @@ function unimportedCreateSystemCall(systemFilePath, record) {
3265
3943
  column: call[1]
3266
3944
  };
3267
3945
  }
3268
- /** A pattern matching a call of any of `callees` as a whole name, or null
3269
- * when there is none to anchor on. */
3270
- function rootCallPattern(callees) {
3271
- if (callees.length === 0) return null;
3272
- return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
3273
- }
3274
- async function extractSystemFilePackages(systemFilePath, parseModule, report, resolvePackage) {
3275
- let source;
3946
+ /** The rounds a membership read resolves the modules it asks for: each
3947
+ * reads the modules the last one's identities stopped at, one re-export
3948
+ * hop further. Past it, what is still asked for is finalized as
3949
+ * unresolved and read once more, so an identity it could not follow keeps
3950
+ * the admission its name gives it, with a finding. */
3951
+ const MEMBERSHIP_ROUNDS = 256;
3952
+ /**
3953
+ * The packages and local modules a system file's roots extend or include,
3954
+ * from the engine's own read of it (spec §2.3 step 5): a bare specifier as
3955
+ * its import spells it, a relative one resolved against the system file.
3956
+ * The engine follows each root binding's identity through the modules this
3957
+ * supplies on request: a relative one by the host's extension order,
3958
+ * spelled exactly, a bare one by the host's package resolution.
3959
+ */
3960
+ async function extractSystemFilePackages(systemFilePath, engine, report, resolvePackage, extensions, kitMembers) {
3961
+ const { systemMembership } = engine;
3962
+ if (systemMembership === void 0) throw new Error("the native engine does not expose systemMembership, which discovery needs — rebuild @animus-ui/extract");
3963
+ const files = {};
3276
3964
  try {
3277
- source = readFileSync(systemFilePath, "utf-8");
3965
+ files[systemFilePath] = readFileSync$1(systemFilePath, "utf-8");
3278
3966
  } catch {
3279
3967
  return [];
3280
3968
  }
3281
- const parsed = parseModule?.(source, systemFilePath) ?? null;
3282
- let rootCall = "createSystem";
3283
- if (parseModule && parsed) rootCall = rootCallPattern([...await systemRoots(systemFilePath, source, parsed, {
3284
- parseModule,
3285
- resolvePackage,
3286
- report
3287
- })]);
3288
- const identifiers = /* @__PURE__ */ new Set();
3289
- const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
3290
- const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
3291
- const collectIdentifiers = (regex) => {
3292
- let match;
3293
- while ((match = regex.exec(source)) !== null) {
3294
- const inner = match[1];
3295
- for (const token of inner.split(",")) {
3296
- const id = token.trim();
3297
- if (id && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(id)) identifiers.add(id);
3298
- }
3969
+ const resolved = {};
3970
+ let answer;
3971
+ for (let round = 0;; round++) {
3972
+ answer = parseInternalWire(systemMembership(JSON.stringify({
3973
+ system: systemFilePath,
3974
+ files,
3975
+ resolved
3976
+ })), "systemMembership");
3977
+ if (answer.needs.length === 0) break;
3978
+ for (const [importer, specifier] of answer.needs) {
3979
+ const key = `${importer}\0${specifier}`;
3980
+ const found = round >= MEMBERSHIP_ROUNDS ? { why: `following '${specifier}' stopped after ${MEMBERSHIP_ROUNDS} re-exports` } : await resolveModuleFile(importer, specifier, resolvePackage, extensions);
3981
+ if ("file" in found) {
3982
+ resolved[key] = { file: found.file };
3983
+ if (!(found.file in files)) try {
3984
+ files[found.file] = readFileSync$1(found.file, "utf-8");
3985
+ } catch {}
3986
+ } else resolved[key] = {
3987
+ why: found.why,
3988
+ caseMismatch: found.caseMismatch
3989
+ };
3299
3990
  }
3300
- };
3301
- if (constructorRegex) collectIdentifiers(constructorRegex);
3302
- collectIdentifiers(chainRegex);
3303
- const IDENT_START_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*/;
3304
- const skipTrivia = (from) => {
3305
- let pos = from;
3306
- for (;;) {
3307
- while (pos < source.length && /\s/.test(source[pos])) pos++;
3308
- if (source.startsWith("//", pos)) {
3309
- const newline = source.indexOf("\n", pos);
3310
- pos = newline === -1 ? source.length : newline + 1;
3311
- continue;
3312
- }
3313
- if (source.startsWith("/*", pos)) {
3314
- const close = source.indexOf("*/", pos + 2);
3315
- pos = close === -1 ? source.length : close + 2;
3316
- continue;
3317
- }
3318
- return pos;
3991
+ }
3992
+ if (!answer.parsed) return [];
3993
+ const reportedUnimported = /* @__PURE__ */ new Set();
3994
+ for (const finding of answer.findings) {
3995
+ if (finding.kind === "unimported") {
3996
+ if (reportedUnimported.has(finding.file)) continue;
3997
+ reportedUnimported.add(finding.file);
3998
+ report?.({
3999
+ file: finding.file,
4000
+ component: "createSystem",
4001
+ kind: "warn",
4002
+ message: `'createSystem' is called with no import or local binding, so it is not read as a system root: the system loader evaluates this file without auto-imports, where the name is undefined — import createSystem from '@animus-ui/system'`,
4003
+ code: UNIMPORTED_CREATE_SYSTEM,
4004
+ severity: severityFor(UNIMPORTED_CREATE_SYSTEM),
4005
+ line: finding.line,
4006
+ column: finding.column
4007
+ });
4008
+ continue;
4009
+ }
4010
+ if (finding.kind === "escapes") {
4011
+ report?.({
4012
+ file: finding.file,
4013
+ component: "createSystem",
4014
+ kind: "warn",
4015
+ message: `a system builder leaves what discovery follows here (${finding.how}), so a kit extended on it from there is not read as a member and its components are not extracted — extend it in the chain that creates it, or through a variable`,
4016
+ code: UNFOLLOWED_SYSTEM_BUILDER,
4017
+ severity: severityFor(UNFOLLOWED_SYSTEM_BUILDER),
4018
+ line: finding.line,
4019
+ column: finding.column
4020
+ });
4021
+ continue;
3319
4022
  }
4023
+ const shown = finding.what !== null ? `it is ${finding.what}, so it is not read as a system root` : `discovery cannot follow what it is bound to (${finding.why}), so it is ${finding.admitted ? "still read" : "not read"} as a system root by its name alone`;
4024
+ report?.({
4025
+ file: finding.file,
4026
+ component: finding.local,
4027
+ kind: "warn",
4028
+ message: `'${finding.local}' is not proven to be Animus's createSystem: ${shown} — import createSystem from '@animus-ui/system', directly or through a re-export discovery can follow`,
4029
+ code: UNPROVEN_ROOT_BINDING,
4030
+ severity: severityFor(UNPROVEN_ROOT_BINDING),
4031
+ line: finding.line,
4032
+ column: finding.column
4033
+ });
4034
+ }
4035
+ const systemPackageRoot = findPackageRoot(systemFilePath);
4036
+ for (const { specifier, importer } of answer.members) {
4037
+ const file = resolved[`${importer}\0${specifier}`]?.file;
4038
+ if (file !== void 0 && !specifier.startsWith(".") && findPackageRoot(importer) !== systemPackageRoot && !kitMembers?.has(specifier)) kitMembers?.set(specifier, file);
4039
+ }
4040
+ return [...new Set(answer.members.map(({ specifier, importer }) => specifier.startsWith(".") ? resolve$1(dirname$1(importer), specifier) : specifier))];
4041
+ }
4042
+ //#endregion
4043
+ //#region pipeline/alias-agreement.ts
4044
+ /** A file's identity on disk: its real path, which follows links and
4045
+ * takes the case the filesystem lists; the path itself where it is not
4046
+ * there. */
4047
+ function physical(file) {
4048
+ try {
4049
+ return realpathSync.native(file);
4050
+ } catch {
4051
+ return resolve(file);
4052
+ }
4053
+ }
4054
+ /** What a host's module id names: a file on disk, its query dropped and a
4055
+ * dev server's `/@fs/` prefix removed; a virtual id as written; or no
4056
+ * file, where the id names a path nothing is at (an alias applied to a
4057
+ * missing module). */
4058
+ function hostTarget(id) {
4059
+ const path = id.split("?")[0];
4060
+ const file = path.startsWith("/@fs/") ? path.slice(4) : path;
4061
+ if (id.startsWith("\0") || !isAbsolute(file)) return { virtual: id };
4062
+ return existsSync(file) ? { file } : null;
4063
+ }
4064
+ /** What a session's comparisons keep between analyses (spec §2.2 A7):
4065
+ * each file's result by its content hash, while the aliases and the file
4066
+ * set are the same. A new alias set, or a file added or removed, compares
4067
+ * every file again. */
4068
+ var AliasAgreementCache = class {
4069
+ scope = "";
4070
+ byFile = /* @__PURE__ */ new Map();
4071
+ };
4072
+ function packageReadRecord(input) {
4073
+ let analysed = null;
4074
+ const rendered = /* @__PURE__ */ new Map();
4075
+ return {
4076
+ rootDir: input.rootDir,
4077
+ roots: input.roots,
4078
+ redirected: input.redirected,
4079
+ holds(file) {
4080
+ analysed ??= new Set(input.analysed.map((path) => physical(join(input.rootDir, path))));
4081
+ return analysed.has(file);
4082
+ },
4083
+ rendersBuilder(file) {
4084
+ let renders = rendered.get(file);
4085
+ if (renders === void 0) {
4086
+ renders = input.rendersBuilder(file);
4087
+ rendered.set(file, renders);
4088
+ }
4089
+ return renders;
4090
+ },
4091
+ reported: /* @__PURE__ */ new Set()
3320
4092
  };
3321
- /** The index after the string literal opening at `from`. */
3322
- const skipString = (from) => {
3323
- const quote = source[from];
3324
- let pos = from + 1;
3325
- while (pos < source.length && source[pos] !== quote) pos += source[pos] === "\\" ? 2 : 1;
3326
- return pos + 1;
4093
+ }
4094
+ /**
4095
+ * An import of a discovered package that the host resolves, from its
4096
+ * importer, to `resolved` (spec §2.2 A4). None where the analysis holds
4097
+ * that file, or the request is not a discovered package's. Otherwise the
4098
+ * importer would render a file extraction never read:
4099
+ * - another instance's: unextracted builder code
4100
+ * (`animus.discovery.unread-package-instance`), or, where a source
4101
+ * redirect serves the instance read in its place, that instance
4102
+ * (`animus.discovery.replaced-package-instance`);
4103
+ * - a file of the instance read that the analysis does not hold, and no
4104
+ * redirect covers, which declares builder chains (a compiled subpath):
4105
+ * `animus.discovery.unread-package-instance`.
4106
+ */
4107
+ function packageInstanceFinding(record, importer, specifier, resolved) {
4108
+ const readRoot = isPackageSpecifier(specifier) ? record.roots[bareSpecifierPackageName(specifier)] : void 0;
4109
+ if (readRoot === void 0) return null;
4110
+ const file = physical(resolved);
4111
+ if (record.holds(file)) return null;
4112
+ const instance = physical(findPackageRoot(file));
4113
+ const redirected = record.redirected.has(specifier);
4114
+ const shown = (path) => relative(record.rootDir, path).split(sep).join(posix.sep);
4115
+ const finding = (code, message) => ({
4116
+ file: importer,
4117
+ component: `import '${specifier}'`,
4118
+ kind: code === "animus.discovery.replaced-package-instance" ? "warn" : "error",
4119
+ message,
4120
+ code,
4121
+ severity: severityFor(code)
4122
+ });
4123
+ if (instance === readRoot) {
4124
+ if (redirected || !record.rendersBuilder(file)) return null;
4125
+ return finding(UNREAD_PACKAGE_INSTANCE, `'${specifier}' in ${importer} resolves through the host bundler to ${shown(file)}, a file of the package extraction reads (${shown(readRoot)}) that it does not analyse, and which declares builder chains, so this importer would render unextracted builder code — import the package by an entry extraction reads, or give this entry an "animus" source condition`);
4126
+ }
4127
+ const where = `'${specifier}' in ${importer} resolves through the host bundler to ${shown(file)}, in another instance of the package (${shown(instance)}) than extraction reads (${shown(readRoot)})`;
4128
+ return redirected ? finding(REPLACED_PACKAGE_INSTANCE, `${where}; the source redirect serves the instance read in its place, so this importer renders that one, not its own — resolve the package to one instance`) : finding(UNREAD_PACKAGE_INSTANCE, `${where}, which is not analysed, so this importer would render unextracted builder code — resolve the package to one instance`);
4129
+ }
4130
+ /**
4131
+ * Where the analysis and the host disagree on a path alias (spec §2.2 A7,
4132
+ * report only: resolution never changes). Each analysed import whose
4133
+ * specifier, as the parser decodes it, an alias the analysis reads names
4134
+ * (tsconfig paths, or the host's own aliases) is resolved as the analysis
4135
+ * resolves it (the alias's expansion through the host's extension order)
4136
+ * and by the host's own resolver. A different physical file, or a file
4137
+ * only one of them finds, is `animus.discovery.alias-mismatch`. A request
4138
+ * the host's resolver fails on is unavailable for that request alone.
4139
+ *
4140
+ * Each import of a discovered package is resolved by the host from its
4141
+ * importer too (spec §2.2 A4): an answer in another instance than the one
4142
+ * read, in a file the analysis does not hold, is
4143
+ * `animus.discovery.unread-package-instance` (the importer would render
4144
+ * unextracted builder code), or `animus.discovery.replaced-package-instance`
4145
+ * where a source redirect serves the instance read in its place.
4146
+ */
4147
+ async function compareAliases(input) {
4148
+ const { cache, packages } = input;
4149
+ const scope = JSON.stringify([
4150
+ input.pathAliasesJson,
4151
+ input.fileEntries.map((entry) => entry.path).sort(),
4152
+ packages?.roots ?? {},
4153
+ [...packages?.redirected ?? []].sort()
4154
+ ]);
4155
+ if (cache.scope !== scope) {
4156
+ cache.scope = scope;
4157
+ cache.byFile.clear();
4158
+ }
4159
+ if (!(input.pathAliasesJson !== null && JSON.parse(input.pathAliasesJson).aliases.length > 0) && Object.keys(packages?.roots ?? {}).length === 0) return {
4160
+ findings: [],
4161
+ unavailable: 0
3327
4162
  };
3328
- /** The index after the bracketed span opening at `from`, balanced across
3329
- * `<>`, `()`, `[]` and `{}`, or -1 when it never closes. An arrow type's
3330
- * `=>` closes nothing. */
3331
- const skipBalanced = (from) => {
3332
- let depth = 0;
3333
- let pos = from;
3334
- while (pos < source.length) {
3335
- const ch = source[pos];
3336
- if (ch === "'" || ch === "\"" || ch === "`") {
3337
- pos = skipString(pos);
3338
- continue;
3339
- }
3340
- if (source.startsWith("//", pos) || source.startsWith("/*", pos)) {
3341
- pos = skipTrivia(pos);
3342
- continue;
3343
- }
3344
- if ("<([{".includes(ch)) depth++;
3345
- else if (")]}".includes(ch) || ch === ">" && source[pos - 1] !== "=") {
3346
- depth--;
3347
- if (depth === 0) return pos + 1;
3348
- }
3349
- pos++;
4163
+ const changed = input.fileEntries.filter((entry) => cache.byFile.get(entry.path)?.hash !== entry.hash);
4164
+ const { files, bases } = changed.length === 0 ? { files: {} } : JSON.parse(input.moduleSpecifiers(JSON.stringify(changed.map(({ path, source }) => ({
4165
+ path,
4166
+ source
4167
+ }))), input.pathAliasesJson ?? void 0));
4168
+ const analysed = new Set(input.fileEntries.map((entry) => entry.path));
4169
+ const packageAnswers = /* @__PURE__ */ new Map();
4170
+ const comparePackage = async (record, importer, specifier) => {
4171
+ const key = `${posix.dirname(importer)}\0${specifier}`;
4172
+ let answer = packageAnswers.get(key);
4173
+ if (answer === void 0) {
4174
+ answer = input.resolve(specifier, join(input.rootDir, importer));
4175
+ packageAnswers.set(key, answer);
3350
4176
  }
3351
- return -1;
3352
- };
3353
- const consumeChainLinks = (from) => {
3354
- let pos = from;
3355
- for (;;) {
3356
- let cursor = skipTrivia(pos);
3357
- if (source[cursor] !== ".") return pos;
3358
- cursor = skipTrivia(cursor + 1);
3359
- const method = IDENT_START_RE.exec(source.slice(cursor))?.[0];
3360
- if (method !== "extend" && method !== "from") return pos;
3361
- cursor = skipTrivia(cursor + method.length);
3362
- if (source[cursor] === "<") {
3363
- const afterTypeArguments = skipBalanced(cursor);
3364
- if (afterTypeArguments === -1) return pos;
3365
- cursor = skipTrivia(afterTypeArguments);
3366
- }
3367
- if (source[cursor] !== "(") return pos;
3368
- cursor = skipTrivia(cursor + 1);
3369
- const base = IDENT_START_RE.exec(source.slice(cursor))?.[0];
3370
- if (!base) return pos;
3371
- cursor = skipTrivia(cursor + base.length);
3372
- while (source[cursor] === ".") {
3373
- const afterDot = skipTrivia(cursor + 1);
3374
- const segment = IDENT_START_RE.exec(source.slice(afterDot))?.[0];
3375
- if (!segment) break;
3376
- cursor = skipTrivia(afterDot + segment.length);
3377
- }
3378
- if (source[cursor] === ",") cursor = skipTrivia(cursor + 1);
3379
- if (source[cursor] !== ")") return pos;
3380
- identifiers.add(base);
3381
- pos = cursor + 1;
4177
+ let id;
4178
+ try {
4179
+ id = await answer;
4180
+ } catch {
4181
+ return "unavailable";
3382
4182
  }
4183
+ const host = id === null ? null : hostTarget(id);
4184
+ if (host === null || "virtual" in host) return null;
4185
+ return packageInstanceFinding(record, importer, specifier, host.file);
3383
4186
  };
3384
- const bindingAt = /* @__PURE__ */ new Map();
3385
- const aliasOf = /* @__PURE__ */ new Map();
3386
- const declarationRe = /\b(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
3387
- let declaration;
3388
- while ((declaration = declarationRe.exec(source)) !== null) {
3389
- const name = declaration[1];
3390
- let cursor = skipTrivia(declaration.index + declaration[0].length);
3391
- if (source[cursor] === ":") {
3392
- cursor++;
3393
- while (cursor < source.length) {
3394
- const ch = source[cursor];
3395
- if (ch === ";" || ch === ",") break;
3396
- if (ch === "\n" && /^\s*(?:export\s+)?(?:const|let|var|function|class|type|interface|import)\b/.test(source.slice(cursor + 1, cursor + 80))) break;
3397
- if (ch === "=" && source[cursor + 1] !== ">") break;
3398
- if ("<([{".includes(ch) || ch === "'" || ch === "\"" || ch === "`") {
3399
- const after = "<([{".includes(ch) ? skipBalanced(cursor) : skipString(cursor);
3400
- if (after === -1) break;
3401
- cursor = after;
3402
- continue;
3403
- }
3404
- cursor = ch === "=" ? cursor + 2 : cursor + 1;
3405
- }
4187
+ const compare = async (importer, specifier, base) => {
4188
+ const candidate = hostCandidate(input.rootDir, base, analysed, input.order, input.readings);
4189
+ const analysis = candidate === null ? null : candidate.analysedAs ?? candidate.candidate.split("?")[0];
4190
+ let id;
4191
+ try {
4192
+ id = await input.resolve(specifier, join(input.rootDir, importer));
4193
+ } catch {
4194
+ return "unavailable";
3406
4195
  }
3407
- if (source[cursor] !== "=" || source[cursor + 1] === "=") continue;
3408
- const initializer = skipTrivia(cursor + 1);
3409
- bindingAt.set(initializer, name);
3410
- const target = IDENT_START_RE.exec(source.slice(initializer))?.[0];
3411
- if (!target) continue;
3412
- const rest = source.slice(initializer + target.length);
3413
- if (/^[ \t]*(?:(?:as|satisfies)\s[^;\n]*)?(?:\/\/[^\n]*)?(?:;|\r?\n(?!\s*[.([?`])|$)/.test(rest)) aliasOf.set(name, target);
3414
- }
3415
- const boundIdentifierBefore = (index) => {
3416
- const declared = bindingAt.get(index);
3417
- if (declared) return declared;
3418
- const match = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*$/.exec(source.slice(0, index));
3419
- return match ? match[1] : null;
4196
+ const host = id === null ? null : hostTarget(id);
4197
+ if (analysis === null || host === null ? analysis === host : "file" in host && physical(join(input.rootDir, analysis)) === physical(host.file)) return null;
4198
+ const shown = host === null ? "no file" : "virtual" in host ? host.virtual : relative(input.rootDir, host.file).split(sep).join(posix.sep);
4199
+ return {
4200
+ file: importer,
4201
+ component: `alias '${specifier}'`,
4202
+ kind: "warn",
4203
+ message: `'${specifier}' in ${importer} resolves to ${analysis ?? "no file"} through the path aliases analysis reads, but the host bundler resolves it to ${shown}, so the analysis may read another module than the one the bundle renders — make the bundler's resolution and the aliases (tsconfig paths) agree`,
4204
+ code: ALIAS_MISMATCH,
4205
+ severity: severityFor(ALIAS_MISMATCH)
4206
+ };
3420
4207
  };
3421
- const chainRootIdentifiers = /* @__PURE__ */ new Set();
3422
- const createSystemAnchor = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(`, "g");
3423
- let anchorMatch;
3424
- while ((anchorMatch = createSystemAnchor?.exec(source) ?? null) !== null) {
3425
- let pos = anchorMatch.index + anchorMatch[0].length;
3426
- let depth = 1;
3427
- while (pos < source.length && depth > 0) {
3428
- const ch = source[pos];
3429
- if (ch === "(") depth++;
3430
- else if (ch === ")") depth--;
3431
- pos++;
3432
- }
3433
- consumeChainLinks(pos);
3434
- const bound = boundIdentifierBefore(anchorMatch.index);
3435
- if (bound) chainRootIdentifiers.add(bound);
3436
- }
3437
- const scannedRoots = /* @__PURE__ */ new Set();
3438
- for (;;) {
3439
- const pendingRoots = [...chainRootIdentifiers].filter((root) => !scannedRoots.has(root));
3440
- if (pendingRoots.length === 0) break;
3441
- for (const root of pendingRoots) {
3442
- scannedRoots.add(root);
3443
- const rootUse = new RegExp(`(?<![a-zA-Z0-9_$])${root.replace(/\$/g, "\\$")}(?![a-zA-Z0-9_$])`, "g");
3444
- let useMatch;
3445
- while ((useMatch = rootUse.exec(source)) !== null) {
3446
- const afterIdentifier = useMatch.index + useMatch[0].length;
3447
- if (consumeChainLinks(afterIdentifier) === afterIdentifier) continue;
3448
- const bound = boundIdentifierBefore(useMatch.index);
3449
- if (bound) chainRootIdentifiers.add(bound);
3450
- }
3451
- }
4208
+ await Promise.all(changed.map(async ({ path, hash }) => {
4209
+ const results = await Promise.all((files[path] ?? []).flatMap((specifier) => {
4210
+ const base = bases?.[path]?.[specifier];
4211
+ if (base !== void 0) return [compare(path, specifier, base)];
4212
+ return packages !== void 0 && isPackageSpecifier(specifier) && packages.roots[bareSpecifierPackageName(specifier)] !== void 0 ? [comparePackage(packages, path, specifier)] : [];
4213
+ }));
4214
+ cache.byFile.set(path, {
4215
+ hash,
4216
+ findings: results.filter((result) => result !== null && result !== "unavailable"),
4217
+ unavailable: results.filter((result) => result === "unavailable").length
4218
+ });
4219
+ }));
4220
+ const kept = input.fileEntries.flatMap((entry) => cache.byFile.get(entry.path) ?? []);
4221
+ return {
4222
+ findings: kept.flatMap((file) => file.findings),
4223
+ unavailable: kept.reduce((total, file) => total + file.unavailable, 0)
4224
+ };
4225
+ }
4226
+ //#endregion
4227
+ //#region pipeline/host-package-resolver.ts
4228
+ /**
4229
+ * Package resolution for discovery: one resolver, whose answers are the
4230
+ * host's where it resolves imports itself (spec §2.2 A4), and the system
4231
+ * loader's otherwise, and for a declared workspace the host cannot resolve.
4232
+ */
4233
+ /**
4234
+ * A declared workspace that nothing links into `node_modules` is no package
4235
+ * to the host, though the system loader resolves it: where the host finds
4236
+ * nothing, discovery asks the loader.
4237
+ */
4238
+ function discoveryResolver(hostResolve, engine, context) {
4239
+ const load = (specifier, fromDir) => {
4240
+ const native = engine.resolveSpecifier;
4241
+ if (native === void 0) throw new Error("the native engine does not expose resolveSpecifier, which package resolution needs — rebuild @animus-ui/extract");
4242
+ return native(specifier, fromDir, [...context.conditions], context.workspaces, extensionPolicyJson(context.extensions));
4243
+ };
4244
+ const loaderResolve = (specifier, fromDir) => load(specifier, fromDir).path ?? null;
4245
+ if (hostResolve === null) {
4246
+ const unresolved = /* @__PURE__ */ new Map();
4247
+ return {
4248
+ hostFirst: false,
4249
+ async resolveSpecifier(specifier) {
4250
+ const entry = load(specifier, context.rootDir);
4251
+ if (entry.path) return entry.path;
4252
+ unresolved.set(specifier, entry.unresolved ?? "it names no file");
4253
+ return null;
4254
+ },
4255
+ unresolvedReason: (specifier) => unresolved.get(specifier),
4256
+ requireSourceEntry: () => false,
4257
+ loaderResolve
4258
+ };
3452
4259
  }
3453
- if (identifiers.size === 0) return [];
3454
- const importMap = /* @__PURE__ */ new Map();
3455
- for (const binding of parsed?.imports ?? []) importMap.set(binding.local, binding.source);
3456
- 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;
3457
- if (!parsed) {
3458
- let importMatch;
3459
- while ((importMatch = importRegex.exec(source)) !== null) {
3460
- const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
3461
- if (comboDefault) importMap.set(comboDefault, specifier);
3462
- if (defaultImport) importMap.set(defaultImport, specifier);
3463
- if (namedImports) for (const binding of namedImports.split(",")) {
3464
- const parts = binding.trim().split(/\s+as\s+/);
3465
- const localName = (parts[1] || parts[0]).trim();
3466
- if (localName) importMap.set(localName, specifier);
4260
+ const loaderOnly = /* @__PURE__ */ new Set();
4261
+ const rootImporter = join$1(context.rootDir, "__animus_discovery__.js");
4262
+ return {
4263
+ hostFirst: true,
4264
+ async resolveSpecifier(specifier) {
4265
+ let hosted;
4266
+ try {
4267
+ hosted = await hostResolve(specifier, rootImporter);
4268
+ } catch {
4269
+ hosted = null;
3467
4270
  }
3468
- }
3469
- }
3470
- const systemFileDir = dirname$1(systemFilePath);
3471
- const packages = /* @__PURE__ */ new Set();
3472
- /** The import a local alias chain ends at, or the identifier itself. */
3473
- const throughAliases = (id) => {
3474
- const seen = /* @__PURE__ */ new Set();
3475
- let current = id;
3476
- while (!importMap.has(current) && !seen.has(current)) {
3477
- seen.add(current);
3478
- const target = aliasOf.get(current);
3479
- if (!target) break;
3480
- current = target;
3481
- }
3482
- return current;
4271
+ if (hosted !== null || !Object.hasOwn(context.workspaces, bareSpecifierPackageName(specifier))) return hosted;
4272
+ const loaded = loaderResolve(specifier, context.rootDir);
4273
+ if (loaded) loaderOnly.add(specifier);
4274
+ return loaded;
4275
+ },
4276
+ unresolvedReason: () => void 0,
4277
+ requireSourceEntry: (specifier) => loaderOnly.has(specifier),
4278
+ loaderResolve
3483
4279
  };
3484
- for (const id of identifiers) {
3485
- const specifier = importMap.get(throughAliases(id));
3486
- if (!specifier) continue;
3487
- if (specifier.startsWith(".")) packages.add(resolve$1(systemFileDir, specifier));
3488
- else packages.add(specifier);
3489
- }
3490
- return Array.from(packages);
4280
+ }
4281
+ //#endregion
4282
+ //#region pipeline/output-targets.ts
4283
+ /**
4284
+ * Where a bundler writes, as one model: the directories it fills and the
4285
+ * files it writes by exact name. Discovery excludes both, and the Rollup
4286
+ * family's render check reads both, so the two never classify output
4287
+ * differently.
4288
+ */
4289
+ function noOutputTargets() {
4290
+ return {
4291
+ dirs: [],
4292
+ files: []
4293
+ };
4294
+ }
4295
+ /** Each output's named directory or file; one naming neither writes to
4296
+ * `fallbackDir` when there is one. */
4297
+ function targetsOf(base, outputs, fallbackDir) {
4298
+ const targets = noOutputTargets();
4299
+ for (const output of outputs) if (output.dir !== void 0) targets.dirs.push(resolve$1(base, output.dir));
4300
+ else if (output.file !== void 0) targets.files.push(resolve$1(base, output.file));
4301
+ else if (fallbackDir !== void 0) targets.dirs.push(resolve$1(base, fallbackDir));
4302
+ return targets;
4303
+ }
4304
+ /** The targets of Rollup's or Rolldown's output options at render. */
4305
+ function rollupOutputTargets(output) {
4306
+ return targetsOf(process.cwd(), [output]);
4307
+ }
4308
+ /** Every Vite environment's outputs, as Vite's own `getResolvedOutDirs`
4309
+ * reads them: each output's `dir`, else the environment's `outDir`. */
4310
+ function viteOutputTargets(config) {
4311
+ return mergeOutputTargets([config.build, ...Object.values(config.environments ?? {}).flatMap((environment) => environment.build === void 0 ? [] : [environment.build])].map((build) => {
4312
+ const output = build.rolldownOptions?.output ?? build.rollupOptions?.output;
4313
+ const outputs = output === void 0 ? [{}] : Array.isArray(output) ? output : [output];
4314
+ return targetsOf(config.root, outputs, build.outDir);
4315
+ }));
4316
+ }
4317
+ /** esbuild's `outdir`, or its exact `outfile`. */
4318
+ function esbuildOutputTargets(options) {
4319
+ return targetsOf(options.absWorkingDir ?? process.cwd(), [{
4320
+ dir: options.outdir,
4321
+ file: options.outfile
4322
+ }]);
4323
+ }
4324
+ /** webpack's `output.path`, up to its first segment holding a template
4325
+ * placeholder (`out/[fullhash]` writes under `out`). */
4326
+ function webpackOutputTargets(outputPath) {
4327
+ if (outputPath === void 0) return noOutputTargets();
4328
+ const segments = outputPath.split(sep$1);
4329
+ const placeholder = segments.findIndex((segment) => segment.includes("["));
4330
+ const fixed = placeholder === -1 ? outputPath : segments.slice(0, placeholder).join(sep$1);
4331
+ return {
4332
+ dirs: fixed === "" ? [] : [fixed],
4333
+ files: []
4334
+ };
4335
+ }
4336
+ function mergeOutputTargets(all) {
4337
+ return {
4338
+ dirs: [...new Set(all.flatMap((targets) => targets.dirs))],
4339
+ files: [...new Set(all.flatMap((targets) => targets.files))]
4340
+ };
4341
+ }
4342
+ /** The Session's structural exclusions: every target inside the root. A
4343
+ * directory that is the root excludes nothing; its files still can. */
4344
+ function outputExclusions(root, targets) {
4345
+ return [...targets.dirs, ...targets.files].flatMap((target) => {
4346
+ const exclusion = outputExclusion(root, target);
4347
+ return exclusion === null ? [] : [exclusion];
4348
+ });
4349
+ }
4350
+ /** The analysed files a build's targets hold, as the render check reports
4351
+ * them: a file target the analysis read is the build's own earlier
4352
+ * output; a file inside a directory target is earlier output or a source
4353
+ * the bundler writes beside, which the check cannot tell apart. */
4354
+ function analysedOutputs(root, targets, analyzed) {
4355
+ const earlier = analyzed.filter((file) => targets.files.includes(file));
4356
+ const inside = targets.dirs.flatMap((dir) => {
4357
+ const exclusion = outputExclusion(root, dir);
4358
+ const held = analyzed.filter((file) => !earlier.includes(file) && isPathWithinRoot(dir, file));
4359
+ return exclusion === null || held.length === 0 ? [] : [{
4360
+ exclusion,
4361
+ held
4362
+ }];
4363
+ });
4364
+ const rel = (file) => relative$1(root, file);
4365
+ const parts = [...earlier.length === 0 ? [] : [`it analysed its own earlier output: ${earlier.map(rel).join(", ")}. Add ${earlier.map((file) => `'${rel(file)}'`).join(", ")} to the plugin's \`exclude\` option`], ...inside.map(({ exclusion, held }) => `it analysed files inside its output directory '${exclusion}': ${held.map(rel).join(", ")}. If they are earlier output, add '${exclusion}' to the plugin's \`exclude\` option; if they are sources, write the output outside the source tree`)];
4366
+ return parts.length === 0 ? null : `[animus] this build ${parts.join("; and ")}.`;
3491
4367
  }
3492
4368
  //#endregion
3493
4369
  //#region pipeline/source-corpus.ts
@@ -3508,6 +4384,7 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
3508
4384
  ownership: {}
3509
4385
  };
3510
4386
  return {
4387
+ facts: (entries) => ingestor.facts(entries),
3511
4388
  async prepare(input) {
3512
4389
  const rawEntries = "fileCache" in input ? assembleRawEntries(input) : input;
3513
4390
  const ingested = await ingestor.ingest(rawEntries);
@@ -3556,7 +4433,7 @@ function requireAnchoredAt(rootDir) {
3556
4433
  function packageRootFromEntry(entry) {
3557
4434
  let current = dirname$1(entry);
3558
4435
  while (true) {
3559
- if (existsSync(join$1(current, "package.json"))) return current;
4436
+ if (existsSync$1(join$1(current, "package.json"))) return current;
3560
4437
  const parent = dirname$1(current);
3561
4438
  if (parent === current) return null;
3562
4439
  current = parent;
@@ -3588,7 +4465,7 @@ function resolveThroughPathAliases(specifier, rootDir, pathAliasesJson) {
3588
4465
  } else if (specifier.startsWith(alias.pattern)) mapped = alias.replacement + specifier.slice(alias.pattern.length);
3589
4466
  if (mapped === null) continue;
3590
4467
  const absolute = isAbsolute$1(mapped) ? mapped : join$1(rootDir, mapped);
3591
- if (existsSync(absolute)) return absolute;
4468
+ if (existsSync$1(absolute)) return absolute;
3592
4469
  }
3593
4470
  return null;
3594
4471
  }
@@ -3609,15 +4486,15 @@ function resolveAssetFile(specifier, rootDir, pathAliasesJson) {
3609
4486
  if (!subpath) return null;
3610
4487
  for (const modulesDir of requireFromRoot.resolve.paths(packageName) ?? []) {
3611
4488
  const packageRoot = join$1(modulesDir, packageName);
3612
- if (!existsSync(join$1(packageRoot, "package.json"))) continue;
4489
+ if (!existsSync$1(join$1(packageRoot, "package.json"))) continue;
3613
4490
  const candidate = join$1(packageRoot, subpath);
3614
- if (existsSync(candidate)) return candidate;
4491
+ if (existsSync$1(candidate)) return candidate;
3615
4492
  }
3616
4493
  try {
3617
4494
  const packageRoot = packageRootFromEntry(requireFromRoot.resolve(packageName));
3618
4495
  if (!packageRoot) return null;
3619
4496
  const candidate = join$1(packageRoot, subpath);
3620
- return existsSync(candidate) ? candidate : null;
4497
+ return existsSync$1(candidate) ? candidate : null;
3621
4498
  } catch {
3622
4499
  return null;
3623
4500
  }
@@ -3642,12 +4519,12 @@ function buildSourceTokenIndex(opts) {
3642
4519
  for (const [dir, specifier] of Object.entries(opts.dirOwners)) {
3643
4520
  let real = dir;
3644
4521
  try {
3645
- real = realpathSync(dir);
4522
+ real = realpathSync$1(dir);
3646
4523
  } catch {}
3647
4524
  addOwnerDir(real, specifier);
3648
4525
  let packageRoot = real;
3649
- while (packageRoot !== dirname$1(packageRoot) && !existsSync(join$1(packageRoot, "package.json"))) packageRoot = dirname$1(packageRoot);
3650
- if (existsSync(join$1(packageRoot, "package.json"))) addOwnerDir(packageRoot, specifier);
4526
+ while (packageRoot !== dirname$1(packageRoot) && !existsSync$1(join$1(packageRoot, "package.json"))) packageRoot = dirname$1(packageRoot);
4527
+ if (existsSync$1(join$1(packageRoot, "package.json"))) addOwnerDir(packageRoot, specifier);
3651
4528
  }
3652
4529
  for (const [modulePath, exports] of Object.entries(manifests)) {
3653
4530
  const owner = realDirOwners.find(({ dir }) => isPathWithinRoot(dir, modulePath));
@@ -4325,15 +5202,28 @@ function buildAnalysisInputs(opts) {
4325
5202
  };
4326
5203
  if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
4327
5204
  if (opts.system.generatedNamesJson) inputs.generatedNamesJson = opts.system.generatedNamesJson;
4328
- const context = opts.analysisContext;
4329
- if (context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length) inputs.analysisContextJson = JSON.stringify({
5205
+ if (opts.system.propertyRecordsJson) inputs.propertyRecordsJson = opts.system.propertyRecordsJson;
5206
+ const context = analysisContextJson(opts.analysisContext);
5207
+ if (context !== void 0) inputs.analysisContextJson = context;
5208
+ return inputs;
5209
+ }
5210
+ /** The engine's wire for what the host knows; present only when the host
5211
+ * knows something, so a host that knows nothing persists unchanged inputs. */
5212
+ function analysisContextJson(context) {
5213
+ if (!(context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length || context?.extensions || context?.openings?.length)) return;
5214
+ return JSON.stringify({
4330
5215
  skippedSources: context.skippedSources ?? [],
4331
5216
  unbundledComputedImports: context.unbundledComputedImports ?? false,
4332
5217
  packageDirs: context.packageDirs ?? [],
4333
5218
  linkedDirs: context.linkedDirs ?? [],
4334
- outputDirs: context.outputDirs ?? []
5219
+ outputDirs: context.outputDirs ?? [],
5220
+ ...context.extensions && { extensions: {
5221
+ appended: context.extensions.appended,
5222
+ nodeNext: context.extensions.nodeNext,
5223
+ alias: context.extensions.alias
5224
+ } },
5225
+ ...context.openings?.length && { openings: context.openings }
4335
5226
  });
4336
- return inputs;
4337
5227
  }
4338
5228
  function hasSourceThemeManifests(system) {
4339
5229
  const json = system.sourceThemeManifestsJson ?? "";
@@ -4367,7 +5257,11 @@ function runProjectAnalysis(engineApi, opts) {
4367
5257
  const inputs = buildAnalysisInputs(opts);
4368
5258
  const serializeMs = Math.round(performance.now() - t);
4369
5259
  t = performance.now();
4370
- const manifestJson = analyzeProject(...buildAnalyzeProjectArgs(inputs));
5260
+ const manifestJson = analyzeProject(...buildAnalyzeProjectArgs(inputs, opts.rootDir === void 0 ? void 0 : gatherProbeEvidence(opts.rootDir, new Set(opts.fileEntries.map((entry) => entry.path)), opts.analysisContext?.extensions, {
5261
+ isAsset: opts.assets?.isAsset,
5262
+ adapted: opts.adapted,
5263
+ expander: opts.loadExpander
5264
+ })));
4371
5265
  const extractMs = Math.round(performance.now() - t);
4372
5266
  t = performance.now();
4373
5267
  const manifest = JSON.parse(manifestJson);
@@ -4603,16 +5497,24 @@ function renameDeclarationRecords(declarationScalesJson, rename) {
4603
5497
  }
4604
5498
  //#endregion
4605
5499
  //#region pipeline/system-config.ts
5500
+ function systemLoadReads(error) {
5501
+ const thrown = error;
5502
+ const carrier = thrown?.dependencies ? thrown : thrown?.cause;
5503
+ return {
5504
+ dependencies: carrier?.dependencies ?? [],
5505
+ missing: carrier?.missing ?? []
5506
+ };
5507
+ }
4606
5508
  /** The loader's evaluation of the system file. A failure on a file that
4607
5509
  * calls `createSystem` with no binding leads with discovery's warning line
4608
5510
  * for it, then keeps the loader's own error. */
4609
- function evaluateSystemModule(engine, systemPath, rootDir, conditions) {
5511
+ function evaluateSystemModule(engine, systemPath, rootDir, conditions, workspaces, extensions) {
4610
5512
  try {
4611
- return engine.loadSystemModule(systemPath, rootDir, void 0, conditions);
5513
+ return engine.loadSystemModule(systemPath, rootDir, void 0, conditions, workspaces, extensions && extensionPolicyJson(extensions));
4612
5514
  } catch (error) {
4613
5515
  let source;
4614
5516
  try {
4615
- source = readFileSync(systemPath, "utf-8");
5517
+ source = readFileSync$1(systemPath, "utf-8");
4616
5518
  } catch {
4617
5519
  throw error;
4618
5520
  }
@@ -4627,7 +5529,7 @@ function evaluateSystemModule(engine, systemPath, rootDir, conditions) {
4627
5529
  * variable name. Error handling stays at the call site.
4628
5530
  */
4629
5531
  function loadSystemConfig(engineApi, opts) {
4630
- const config = evaluateSystemModule(engineApi(), opts.systemPath, opts.rootDir, opts.conditions);
5532
+ const config = evaluateSystemModule(engineApi(), opts.systemPath, opts.rootDir, opts.conditions, opts.workspaces, opts.extensions);
4631
5533
  let scalesJson = config.scalesJson;
4632
5534
  let variableMapJson = config.variableMapJson;
4633
5535
  const registrations = splitInvalidPropertyRegistrations(config.variableCss);
@@ -4677,6 +5579,7 @@ function loadSystemConfig(engineApi, opts) {
4677
5579
  keyframesJson: config.keyframesBlocks || null,
4678
5580
  vocabularyWitnessesJson: config.vocabularyWitnesses || null,
4679
5581
  dependencies: config.dependencies ?? [],
5582
+ missing: config.missing ?? [],
4680
5583
  sourceThemeManifestsJson: config.sourceThemeManifests || null
4681
5584
  };
4682
5585
  if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
@@ -4796,13 +5699,6 @@ const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
4796
5699
  ".mjs",
4797
5700
  ".cjs"
4798
5701
  ]);
4799
- const isFile = (path) => {
4800
- try {
4801
- return statSync$1(path).isFile();
4802
- } catch {
4803
- return false;
4804
- }
4805
- };
4806
5702
  const posixPath = (path) => path.split(sep).join("/");
4807
5703
  /**
4808
5704
  * The package-relative files `npm pack` publishes, read with npm's own rules
@@ -4868,7 +5764,7 @@ function animusTargets(manifest) {
4868
5764
  /** Every file in the package outside `node_modules` and `.git`,
4869
5765
  * package-relative. */
4870
5766
  function packageFiles(pkgRoot, dir = "") {
4871
- return readdirSync$1(join(pkgRoot, dir), { withFileTypes: true }).flatMap((entry) => {
5767
+ return readdirSync(join(pkgRoot, dir), { withFileTypes: true }).flatMap((entry) => {
4872
5768
  const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
4873
5769
  if (entry.isDirectory()) return entry.name === "node_modules" || entry.name === ".git" ? [] : packageFiles(pkgRoot, path);
4874
5770
  return entry.isFile() ? [path] : [];
@@ -4892,7 +5788,7 @@ function targetPattern(target) {
4892
5788
  function kitPublicationFailures(pkgRoot, engine, artifactDir) {
4893
5789
  let manifest;
4894
5790
  try {
4895
- manifest = JSON.parse(readFileSync$1(join(pkgRoot, "package.json"), "utf-8"));
5791
+ manifest = JSON.parse(readFileSync(join(pkgRoot, "package.json"), "utf-8"));
4896
5792
  } catch (error) {
4897
5793
  return [`package.json: cannot be read: ${String(error)}`];
4898
5794
  }
@@ -4931,8 +5827,14 @@ function kitPublicationFailures(pkgRoot, engine, artifactDir) {
4931
5827
  const matched = files.filter((file) => pattern.test(file));
4932
5828
  if (matched.length === 0) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, matches no file in the package`);
4933
5829
  for (const file of matched) admit(subpath, file);
4934
- } else if (!isFile(resolve(pkgRoot, target))) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, does not exist in the package`);
4935
- else admit(subpath, relPath);
5830
+ } else {
5831
+ const seen = seeOnDisk(resolve(pkgRoot, target), pkgRoot);
5832
+ if (seen.kind === "file") admit(subpath, relPath);
5833
+ else failures.push(seen.kind === "case-only" ? `package.json: ${caseMismatchReason(`the "animus" target of exports["${subpath}"]`, {
5834
+ candidate: target,
5835
+ actual: rel(seen.actual)
5836
+ }, "spell the target as the file is spelled")}` : `package.json: the "animus" target of exports["${subpath}"], ${target}, does not exist in the package`);
5837
+ }
4936
5838
  }
4937
5839
  const visited = /* @__PURE__ */ new Set();
4938
5840
  while (queue.length > 0) {
@@ -4941,12 +5843,17 @@ function kitPublicationFailures(pkgRoot, engine, artifactDir) {
4941
5843
  visited.add(file);
4942
5844
  const specifiers = parseInternalWire(moduleSpecifiers(JSON.stringify([{
4943
5845
  path: file,
4944
- source: readFileSync$1(file, "utf-8")
5846
+ source: readFileSync(file, "utf-8")
4945
5847
  }])), "moduleSpecifiers").files[file] ?? [];
4946
5848
  for (const specifier of specifiers) {
4947
5849
  if (specifier.startsWith(".")) {
4948
- const target = relativeSourceCandidates(file, specifier).find(isFile);
4949
- if (target === void 0) failures.push(`${rel(file)}: import '${specifier}' resolves to no file in the package`);
5850
+ const probed = probeOnDisk(relativeSourceCandidates(file, specifier), dirname(file));
5851
+ const target = probed.kind === "found" ? probed.path : void 0;
5852
+ if (probed.kind === "case-mismatch") failures.push(`${rel(file)}: ${caseMismatchReason(`import '${specifier}'`, {
5853
+ candidate: rel(probed.candidate),
5854
+ actual: rel(probed.actual)
5855
+ }, "spell the import as the file is spelled")}`);
5856
+ else if (target === void 0) failures.push(`${rel(file)}: import '${specifier}' resolves to no file in the package`);
4950
5857
  else if (outside(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, outside the package`);
4951
5858
  else if (!published(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, which is not in the published files`);
4952
5859
  else if (CODE_EXTENSIONS.has(extname(target))) queue.push(target);
@@ -5030,7 +5937,7 @@ function toWatchKeys(input) {
5030
5937
  * ancestor. A symlink retargeted after load fails open: no match. */
5031
5938
  function realpathThroughNearestExistingAncestor(path) {
5032
5939
  try {
5033
- return realpathSync$1(path);
5940
+ return realpathSync(path);
5034
5941
  } catch {}
5035
5942
  const suffix = [];
5036
5943
  let current = path;
@@ -5040,9 +5947,644 @@ function realpathThroughNearestExistingAncestor(path) {
5040
5947
  suffix.unshift(basename(current));
5041
5948
  current = parent;
5042
5949
  try {
5043
- return join(realpathSync$1(current), ...suffix);
5950
+ return join(realpathSync(current), ...suffix);
5044
5951
  } catch {}
5045
5952
  }
5046
5953
  }
5047
5954
  //#endregion
5048
- export { discoverFiles as $, fileSideEffects as A, surfaceManifestDiagnostics as At, unresolvableIncludesMessage as B, substituteSheetAssets as Bt, resolveAssetFile as C, DiagnosticFailure as Ct, engineModuleParser as D, isDeletedSource as Dt, collectExternalPackageSources as E, VOCABULARY_LEGACY_VERB as Et, keepsKitSourceEffects as F, findAssetSpecifiers as Ft, KIT_DESCRIPTOR_FILE as G, assembleStylesheet as Gt, createSourceIdentity as H, contentHash as Ht, kitSourceModuleSideEffects as I, findSheetAssetSpecifiers as It, readKitDescriptor as J, __require as Jt, buildKitDescriptor as K, stripLeadingLayerDeclaration as Kt, resolveAbsolutePathSpecifier as L, generatedModuleCode as Lt, firstOwners as M, unreadableSourceDiagnostic as Mt, importedKitDiagnostics as N, unresolvedParentName as Nt, excludeCollectedPackages as O, isUnresolvedParentDrop as Ot, isExcludedPackageRelativePath as P, vocabularyWitnessDiagnostics as Pt, compareDiscoveryOrder as Q, sourceKitDependencies as R, reportSurvivingAssetPlaceholders as Rt, enforceExternalTokenContracts as S, DIAGNOSTIC_LEVELS as St, createSourceCorpus as T, VOCABULARY_COLLISION as Tt, isPathWithinRoot as U, buildAnalyzeProjectArgs as Ut, walkPackageSources as V, parseInternalWire as Vt, sharesVolumeRoot as W, ANIMUS_LAYERS as Wt, isJsonString as X, isJsonBlock as Y, readTsconfigAliasPairs as Z, runProjectAnalysis as _, adaptSvelteSource as _t, kitPublicationFailures as a, STRUCTURAL_EXCLUDE as at, resolveLightningTargets as b, isEngineTransformExtension as bt, buildDynamicPropConfig as c, resolveMode as ct, applyPrefix as d, assertNoRetiredEngineSelection as dt, AnimusConfigError as et, serializeStaticCss as f, createV2EngineApi as ft, clearEngineCache as g, projectExternalFileOwners as gt, buildAnalysisInputs as h, parseFilesJson as ht, snapshotFilePlans as i, REPLACEABLE_DEFAULT_EXCLUDE as it, findPackageRoot as j, systemLoadDiagnostics as jt, extractSystemFilePackages as k, noKitFilesDiagnostics as kt, runStructuralSelfCheck as l, resolveVerbosity as lt, stableStringify as m, isAdvisorySourceDiagnostic as mt, diffFilePlans as n, DEFAULT_EXCLUDE as nt, assertNoErrorDiagnostics as o, assertKnownOptionKeys as ot, camelToKebab as p, ingestSourceEntries as pt, kitDescriptorDiagnostics as q, validateLayerOrder as qt, hashReplacementPlans as r, DRIVER_NAMESPACES as rt, buildSystemPropsModule as s, createExcludeMatcher as st, toWatchKeys as t, CORE_OPTION_KEYS as tt, loadSystemConfig as u, RETIRED_ENGINE_MESSAGE as ut, applyUnitFallback as v, DEFAULT_EXTENSIONS as vt, resolveThroughPathAliases as w, INVALID_PROPERTY_REGISTRATION as wt, buildPathAliasesJson as x, preprocessMdx as xt, postProcessCss as y, ENGINE_TRANSFORM_EXTENSIONS as yt, staleDistIncludesMessage as z, substituteAssetPlaceholders as zt };
5955
+ //#region pipeline/built-in-asset-types.ts
5956
+ /** The Vite version the built-in types come from. */
5957
+ const BUILT_IN_ASSET_SOURCE = "vite@8.2.2";
5958
+ /** Asset extensions, as regular-expression alternatives, a session with no
5959
+ * readable host classification reads as assets (spec §2.2 A8). */
5960
+ const BUILT_IN_ASSET_TYPES = [
5961
+ "apng",
5962
+ "bmp",
5963
+ "png",
5964
+ "jpe?g",
5965
+ "jfif",
5966
+ "pjpeg",
5967
+ "pjp",
5968
+ "gif",
5969
+ "svg",
5970
+ "ico",
5971
+ "webp",
5972
+ "avif",
5973
+ "cur",
5974
+ "jxl",
5975
+ "mp4",
5976
+ "webm",
5977
+ "ogg",
5978
+ "mp3",
5979
+ "wav",
5980
+ "flac",
5981
+ "aac",
5982
+ "opus",
5983
+ "mov",
5984
+ "m4a",
5985
+ "vtt",
5986
+ "woff2?",
5987
+ "eot",
5988
+ "ttf",
5989
+ "otf",
5990
+ "webmanifest",
5991
+ "pdf",
5992
+ "txt"
5993
+ ];
5994
+ /** Stylesheet extensions, as regular-expression alternatives, a session
5995
+ * with no readable host classification reads as stylesheets. */
5996
+ const BUILT_IN_STYLESHEET_TYPES = [
5997
+ "css",
5998
+ "less",
5999
+ "sass",
6000
+ "scss",
6001
+ "styl",
6002
+ "stylus",
6003
+ "pcss",
6004
+ "postcss",
6005
+ "sss"
6006
+ ];
6007
+ //#endregion
6008
+ //#region pipeline/host-assets.ts
6009
+ const BUILT_IN_ASSET_RE = new RegExp(`\\.(?:${BUILT_IN_ASSET_TYPES.join("|")})$`, "i");
6010
+ /** Vite's stylesheet test (`isCSSRequest`), on the final extension only: a
6011
+ * `x.css.ts` is a module. Exact case, as Vite reads it. */
6012
+ const STYLESHEET_RE = new RegExp(`\\.(?:${BUILT_IN_STYLESHEET_TYPES.join("|")})$`);
6013
+ /** JSON, as Vite's json plugin, webpack's `json` type, esbuild's `json`
6014
+ * loader and Turbopack read it: the final extension, exact case. */
6015
+ const JSON_RE = /\.json$/;
6016
+ /** The queries Vite answers with a string whatever the file holds: `?url`
6017
+ * and `?raw`. Any other query keeps the file's own reading. */
6018
+ const VITE_OPAQUE_QUERY_RE = /[?&](?:url|raw)\b/;
6019
+ /** The built-in classification, where no host's can be read or is not
6020
+ * modelled (webpack's and Rspack's rules, as a conditional rule may never
6021
+ * apply): Vite's known asset types, `svg` included, its CSS languages
6022
+ * (`BUILT_IN_ASSET_SOURCE`) and JSON, read with Vite's queries. */
6023
+ function builtInAssetClassification(reason, detail) {
6024
+ return {
6025
+ isAsset: (file, query) => VITE_OPAQUE_QUERY_RE.test(query) || BUILT_IN_ASSET_RE.test(file) || STYLESHEET_RE.test(file) || JSON_RE.test(file),
6026
+ resolverVersion: `built-in (${reason}${detail ? `: ${detail}` : ""}; ${BUILT_IN_ASSET_SOURCE})`
6027
+ };
6028
+ }
6029
+ /** Vite's own classification: `?url` and `?raw`, its known asset types and
6030
+ * the user's `assetsInclude`, as `ResolvedConfig.assetsInclude` answers,
6031
+ * its CSS languages (`isCSSRequest`), and its json plugin's JSON. */
6032
+ function viteAssetClassification(assetsInclude, version) {
6033
+ return {
6034
+ isAsset: (file, query) => VITE_OPAQUE_QUERY_RE.test(query) || assetsInclude(file) || STYLESHEET_RE.test(file) || JSON_RE.test(file),
6035
+ resolverVersion: `${version ? `vite@${version}` : "vite"} captured`
6036
+ };
6037
+ }
6038
+ /** esbuild's built-in loaders, which a configured `loader` map extends. */
6039
+ const ESBUILD_DEFAULT_LOADERS = {
6040
+ "": "js",
6041
+ ".js": "js",
6042
+ ".mjs": "js",
6043
+ ".cjs": "js",
6044
+ ".jsx": "jsx",
6045
+ ".ts": "ts",
6046
+ ".cts": "ts",
6047
+ ".mts": "ts",
6048
+ ".tsx": "tsx",
6049
+ ".css": "css",
6050
+ ".module.css": "local-css",
6051
+ ".json": "json",
6052
+ ".txt": "text"
6053
+ };
6054
+ /** esbuild loaders whose module is an opaque value: a URL, the file's bytes
6055
+ * or text, a stylesheet, or JSON. `copy` keeps the file as code. */
6056
+ const ESBUILD_OPAQUE_LOADERS = /* @__PURE__ */ new Set([
6057
+ "file",
6058
+ "dataurl",
6059
+ "base64",
6060
+ "binary",
6061
+ "text",
6062
+ "css",
6063
+ "local-css",
6064
+ "global-css",
6065
+ "json"
6066
+ ]);
6067
+ /** esbuild's classification, from its `loader` map: the loader esbuild
6068
+ * picks for a file is the longest extension of its name the map holds,
6069
+ * exact case. */
6070
+ function esbuildAssetClassification(loader) {
6071
+ const loaders = new Map(Object.entries({
6072
+ ...ESBUILD_DEFAULT_LOADERS,
6073
+ ...loader
6074
+ }));
6075
+ return {
6076
+ isAsset: (file) => {
6077
+ let name = file.slice(Math.max(file.lastIndexOf("/"), file.lastIndexOf("\\")) + 1);
6078
+ let picked = name.includes(".") ? void 0 : loaders.get("");
6079
+ for (let dot = name.indexOf("."); dot !== -1; dot = name.indexOf(".")) {
6080
+ picked = loaders.get(name.slice(dot));
6081
+ if (picked !== void 0) break;
6082
+ name = name.slice(dot + 1);
6083
+ }
6084
+ return picked !== void 0 && ESBUILD_OPAQUE_LOADERS.has(picked);
6085
+ },
6086
+ resolverVersion: "esbuild captured"
6087
+ };
6088
+ }
6089
+ /** Turbopack's classification under Next: Next's static-image table
6090
+ * (`nextImageLoaderRegex`), unless `images.disableStaticImages`, and its
6091
+ * stylesheets (`regexLikeCss`), as the installed `next` declares them,
6092
+ * and JSON. Built-in where Next cannot be read. */
6093
+ function nextImageAssetClassification(rootDir, disableStaticImages) {
6094
+ try {
6095
+ const require = createRequire(`${rootDir}/package.json`);
6096
+ const { version } = require("next/package.json");
6097
+ const { nextImageLoaderRegex } = require("next/dist/build/webpack-config");
6098
+ const { regexLikeCss } = require("next/dist/build/webpack/config/blocks/css");
6099
+ if (!(nextImageLoaderRegex instanceof RegExp) || !(regexLikeCss instanceof RegExp)) return builtInAssetClassification("hostOptionUnreadable", `next@${version} image and stylesheet types`);
6100
+ return {
6101
+ isAsset: (file) => JSON_RE.test(file) || regexLikeCss.test(file) || !disableStaticImages && nextImageLoaderRegex.test(file),
6102
+ resolverVersion: `from next@${version}`
6103
+ };
6104
+ } catch {
6105
+ return builtInAssetClassification("hostOptionUnreadable", "next");
6106
+ }
6107
+ }
6108
+ //#endregion
6109
+ //#region pipeline/vite-expansions.ts
6110
+ /** The glob syntax read here: literal characters, `*` and a whole-segment
6111
+ * `**`, and a `{a,b}` group of literal alternatives. Anything else (`?`,
6112
+ * classes, extglobs, nested groups) is not reproduced. */
6113
+ const UNREAD_SYNTAX = /[?[\]()!+@\\]/;
6114
+ function segmentOf(text) {
6115
+ if (text === "**") return { kind: "globstar" };
6116
+ if (UNREAD_SYNTAX.test(text) || text.includes("**")) return void 0;
6117
+ let source = "";
6118
+ for (const part of text.split(/(\{[^{}]*\}|\*)/)) if (part === "*") source += "[^/]*";
6119
+ else if (part.startsWith("{") && part.endsWith("}")) {
6120
+ const options = part.slice(1, -1).split(",");
6121
+ if (options.some((option) => /[{}*]/.test(option))) return void 0;
6122
+ source += `(?:${options.map(escape).join("|")})`;
6123
+ } else if (/[{}]/.test(part)) return void 0;
6124
+ else source += escape(part);
6125
+ return {
6126
+ kind: "match",
6127
+ test: new RegExp(`^${source}$`),
6128
+ dotted: text.startsWith(".")
6129
+ };
6130
+ }
6131
+ function escape(text) {
6132
+ return text.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
6133
+ }
6134
+ /**
6135
+ * A pattern as Vite resolves it (`toAbsoluteGlob`): relative to the
6136
+ * importer's directory, or with `/` to the Vite root. A pattern in another
6137
+ * form (an alias, a bare name) or with syntax not read here is undefined.
6138
+ */
6139
+ function resolveGlob(pattern, importerDir, viteRoot) {
6140
+ const negated = pattern.startsWith("!");
6141
+ const body = negated ? pattern.slice(1) : pattern;
6142
+ const start = body.startsWith("/") ? viteRoot : body.startsWith("./") || body.startsWith("../") ? importerDir : void 0;
6143
+ if (start === void 0) return void 0;
6144
+ const parts = body.split("/").filter((part) => part !== "" && part !== ".");
6145
+ let base = start;
6146
+ let index = 0;
6147
+ if (parts.some((part) => part !== ".." && UNREAD_SYNTAX.test(part))) return;
6148
+ while (index < parts.length - 1 && !/[*{}]/.test(parts[index])) {
6149
+ base = parts[index] === ".." ? dirname(base) : resolve(base, parts[index]);
6150
+ index += 1;
6151
+ }
6152
+ const segments = [];
6153
+ for (const part of parts.slice(index)) {
6154
+ if (part === "..") return void 0;
6155
+ const segment = segmentOf(part);
6156
+ if (segment === void 0) return void 0;
6157
+ segments.push(segment);
6158
+ }
6159
+ return {
6160
+ negated,
6161
+ base,
6162
+ segments
6163
+ };
6164
+ }
6165
+ /** Whether `names`, a path below a glob's base, matches its segments. A
6166
+ * wildcard never matches a dot-led name unless `dot`. */
6167
+ function matches(segments, names, dot) {
6168
+ const [segment, ...rest] = segments;
6169
+ if (segment === void 0) return names.length === 0;
6170
+ if (segment.kind === "globstar") {
6171
+ if (matches(rest, names, dot)) return true;
6172
+ const [name, ...more] = names;
6173
+ if (name === void 0 || !dot && name.startsWith(".")) return false;
6174
+ return matches(segments, more, dot);
6175
+ }
6176
+ const [name, ...more] = names;
6177
+ if (name === void 0) return false;
6178
+ if (!dot && name.startsWith(".") && !segment.dotted) return false;
6179
+ return segment.test.test(name) && matches(rest, more, dot);
6180
+ }
6181
+ /** Every file below `dir`, following symbolic links as Vite does, each
6182
+ * directory once by its real path. A read the walk cannot finish throws:
6183
+ * the expansion would not be every file. */
6184
+ function walk(dir, prefix = [], visited = /* @__PURE__ */ new Set()) {
6185
+ const real = realpathSync(dir);
6186
+ if (visited.has(real)) return [];
6187
+ const inside = new Set(visited).add(real);
6188
+ const files = [];
6189
+ for (const name of readdirSync(dir)) {
6190
+ const path = `${dir}${sep}${name}`;
6191
+ const names = [...prefix, name];
6192
+ const stat = statSync(path);
6193
+ if (stat.isDirectory()) files.push(...walk(path, names, inside));
6194
+ else if (stat.isFile()) files.push({
6195
+ names,
6196
+ path,
6197
+ real: realpathSync(path)
6198
+ });
6199
+ }
6200
+ return files;
6201
+ }
6202
+ /** The files a set of Vite globs selects: in any affirmed glob and no
6203
+ * negated one, `node_modules` and dot-led names left out unless
6204
+ * `exhaustive`, the importer itself never. */
6205
+ function expandGlobs(globs, importer, exhaustive) {
6206
+ const affirmed = globs.filter((glob) => !glob.negated);
6207
+ const negated = globs.filter((glob) => glob.negated);
6208
+ const inside = (glob, file) => {
6209
+ const rel = relative(glob.base, file);
6210
+ if (isAbsolute(rel) || rel.split(sep)[0] === "..") return false;
6211
+ return matches(glob.segments, rel.split(sep), exhaustive);
6212
+ };
6213
+ const selected = /* @__PURE__ */ new Set();
6214
+ for (const glob of affirmed) for (const { names, path, real } of walk(glob.base)) {
6215
+ if (!exhaustive && names.includes("node_modules")) continue;
6216
+ if (path === importer || !inside(glob, path)) continue;
6217
+ if (negated.some((other) => inside(other, path))) continue;
6218
+ selected.add(real);
6219
+ }
6220
+ return [...selected].sort();
6221
+ }
6222
+ /** The queries a glob's `query` option is read as: Vite's opaque ones,
6223
+ * with or without the `?` it adds. */
6224
+ const GLOB_QUERIES = /* @__PURE__ */ new Map([
6225
+ [void 0, ""],
6226
+ ["raw", "?raw"],
6227
+ ["?raw", "?raw"],
6228
+ ["url", "?url"],
6229
+ ["?url", "?url"]
6230
+ ]);
6231
+ /** The boolean an option is written as; absent, `false`. */
6232
+ const GLOB_FLAGS = /* @__PURE__ */ new Map([
6233
+ [void 0, false],
6234
+ [true, true],
6235
+ [false, false]
6236
+ ]);
6237
+ /** A glob's options as Vite reads them (`parseGlobOptions`): the query each
6238
+ * file is imported with and whether the glob is exhaustive. `import`
6239
+ * selects an export, not files. Undefined for an option set not
6240
+ * reproduced here: another query, `base`, `caseSensitive`, or the
6241
+ * deprecated `as`, which a Vite 8 build does not apply as a query. */
6242
+ function globOptions(options) {
6243
+ if (options === null) return {
6244
+ query: "",
6245
+ exhaustive: false
6246
+ };
6247
+ for (const key of Object.keys(options)) if (![
6248
+ "eager",
6249
+ "import",
6250
+ "exhaustive",
6251
+ "query"
6252
+ ].includes(key)) return;
6253
+ const query = GLOB_QUERIES.get(options.query);
6254
+ const exhaustive = GLOB_FLAGS.get(options.exhaustive);
6255
+ if (query === void 0 || exhaustive === void 0) return void 0;
6256
+ if (!GLOB_FLAGS.has(options.eager)) return void 0;
6257
+ return {
6258
+ query,
6259
+ exhaustive
6260
+ };
6261
+ }
6262
+ /**
6263
+ * The glob a variable `import()` of a template literal becomes under
6264
+ * Vite's dynamic-import-vars (`dynamicImportToGlob`): each substitution one
6265
+ * `*` within a segment. It must start `./` or `../` and name a file
6266
+ * extension in its last static text; Vite leaves any other one, and a
6267
+ * concatenation, a runtime import, which it does not expand.
6268
+ */
6269
+ function importVarsGlob(quasis) {
6270
+ if (quasis.length < 2) return void 0;
6271
+ const head = quasis[0];
6272
+ if (!head.startsWith("./") && !head.startsWith("../")) return void 0;
6273
+ if (quasis.slice(1, -1).some((quasi) => quasi === "")) return void 0;
6274
+ if (quasis.some((quasi) => /[*?{}[\]]/.test(quasi))) return void 0;
6275
+ const last = quasis[quasis.length - 1];
6276
+ if (!/\.[a-z0-9]+$/i.test(last) || last.includes("?")) return void 0;
6277
+ return quasis.join("*");
6278
+ }
6279
+ /**
6280
+ * How a bundled Vite environment (Vite 8, rolldown's native glob and
6281
+ * dynamic-import-vars plugins) expands computed loads (spec §6.3):
6282
+ * `import.meta.glob` and a variable `import()`. `importVars` is false where
6283
+ * the project's `build.dynamicImportVarsOptions` differ from Vite's
6284
+ * default, which transforms every importer outside `node_modules`.
6285
+ * Anything this does not reproduce exactly lists no expansion.
6286
+ */
6287
+ function viteLoadExpander(viteRoot, importVars) {
6288
+ return { expand(load, rootDir) {
6289
+ const importer = resolve(rootDir, load.importer);
6290
+ const importerDir = dirname(importer);
6291
+ let patterns;
6292
+ let query = "";
6293
+ let exhaustive = false;
6294
+ if (load.kind === "glob" && load.args && "patterns" in load.args) {
6295
+ const read = globOptions(load.args.options);
6296
+ if (read === void 0) return void 0;
6297
+ ({query, exhaustive} = read);
6298
+ patterns = load.args.patterns;
6299
+ } else if (load.kind === "import" && load.args && "quasis" in load.args) {
6300
+ if (!importVars || importer.split(sep).includes("node_modules") || !load.args.template || load.args.options !== null || load.args.comments.some((comment) => comment.includes("@vite-ignore"))) return;
6301
+ const glob = importVarsGlob(load.args.quasis);
6302
+ if (glob === void 0) return void 0;
6303
+ patterns = [glob];
6304
+ } else return;
6305
+ const globs = [];
6306
+ for (const pattern of patterns) {
6307
+ const glob = resolveGlob(pattern, importerDir, viteRoot);
6308
+ if (glob === void 0) return void 0;
6309
+ globs.push(glob);
6310
+ }
6311
+ if (!globs.some((glob) => !glob.negated)) return void 0;
6312
+ let files;
6313
+ let realRoot;
6314
+ try {
6315
+ files = expandGlobs(globs, importer, exhaustive);
6316
+ realRoot = realpathSync(rootDir);
6317
+ } catch {
6318
+ return;
6319
+ }
6320
+ return files.map((file) => ({
6321
+ path: relative(realRoot, file).split(sep).join(posix.sep),
6322
+ query
6323
+ }));
6324
+ } };
6325
+ }
6326
+ /** Whether `build.dynamicImportVarsOptions` are Vite's default, which
6327
+ * transforms every importer outside `node_modules`. */
6328
+ function defaultImportVars(options) {
6329
+ const exclude = options?.exclude;
6330
+ return options?.include === void 0 && Array.isArray(exclude) && exclude.length === 1 && exclude[0] instanceof RegExp && exclude[0].source === "node_modules" && exclude[0].flags === "";
6331
+ }
6332
+ /** The aliases Vite adds to every configuration (`clientAlias`), which
6333
+ * name only its own `@vite/` modules. */
6334
+ const VITE_CLIENT_ALIASES = /* @__PURE__ */ new Set(["^\\/?@vite\\/env", "^\\/?@vite\\/client"]);
6335
+ /** Whether an alias could rewrite a relative or root specifier, which the
6336
+ * expander reads without the resolver: a string from `.` or `/`, or a
6337
+ * RegExp other than Vite's own client aliases. */
6338
+ function aliasesRelative(aliases) {
6339
+ return (aliases ?? []).some(({ find }) => find instanceof RegExp ? !(find.flags === "" && VITE_CLIENT_ALIASES.has(find.source)) : find.startsWith(".") || find.startsWith("/"));
6340
+ }
6341
+ /**
6342
+ * The expander for a resolved Vite configuration: only where every
6343
+ * environment is bundled (Vite 8, rolldown's native plugins; an earlier
6344
+ * Vite marks none), and no alias could rewrite a relative or root
6345
+ * specifier. A variable `import()` is expanded only where every environment
6346
+ * keeps Vite's default dynamic-import-vars options.
6347
+ */
6348
+ function viteHostExpander(config) {
6349
+ const environments = Object.values(config.environments ?? {});
6350
+ if (environments.length === 0 || !environments.every((environment) => environment.isBundled === true) || aliasesRelative(config.resolve?.alias)) return;
6351
+ return viteLoadExpander(config.root, environments.every((environment) => defaultImportVars(environment.build?.dynamicImportVarsOptions)));
6352
+ }
6353
+ //#endregion
6354
+ //#region pipeline/webpack-expansions.ts
6355
+ /** Webpack's `quoteMeta`. */
6356
+ function quoteMeta(text) {
6357
+ return text.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
6358
+ }
6359
+ /** A literal as a RegExp, where webpack tests it statelessly: a global or
6360
+ * sticky one carries state between tests, which is not reproduced. */
6361
+ function regex(literal) {
6362
+ if (literal === null) return void 0;
6363
+ if (/[gy]/.test(literal.flags)) return null;
6364
+ try {
6365
+ return new RegExp(literal.source, literal.flags);
6366
+ } catch {
6367
+ return null;
6368
+ }
6369
+ }
6370
+ /** Webpack's `splitContextFromPrefix`. */
6371
+ function splitContextFromPrefix(prefix) {
6372
+ const at = prefix.lastIndexOf("/");
6373
+ return at >= 0 ? {
6374
+ context: prefix.slice(0, at),
6375
+ prefix: `.${prefix.slice(at)}`
6376
+ } : {
6377
+ context: ".",
6378
+ prefix
6379
+ };
6380
+ }
6381
+ /** A context directory webpack resolves relative to the importer; another
6382
+ * form goes through the resolver (aliases, modules), not reproduced. */
6383
+ function contextDir(importerDir, context) {
6384
+ return context === "." || context.startsWith("./") || context.startsWith("../") ? resolve(importerDir, context) : void 0;
6385
+ }
6386
+ /** The comments that change which files a webpack context holds. */
6387
+ const SELECTING_COMMENT = /webpack(?:Include|Exclude|Ignore)\b/;
6388
+ /**
6389
+ * The context webpack builds for a template literal or a concatenation
6390
+ * (`ContextDependencyHelpers.create`), with the default wrapped-context
6391
+ * options (`/.*\/`, recursive): a template's inner text joins the
6392
+ * expression pattern; a concatenation keeps its prefix and postfix only.
6393
+ */
6394
+ function specifierContext(load, importerDir) {
6395
+ const { args } = load;
6396
+ if (args === null || !("quasis" in args)) return void 0;
6397
+ if (args.options !== null) return void 0;
6398
+ if (args.comments.some((comment) => SELECTING_COMMENT.test(comment))) return;
6399
+ const { quasis, template } = args;
6400
+ if (quasis.length < 2 || !template && quasis.length > 2) return void 0;
6401
+ const { context, prefix } = splitContextFromPrefix(quasis[0]);
6402
+ const postfixRaw = quasis[quasis.length - 1];
6403
+ if (postfixRaw.includes("#")) return void 0;
6404
+ const at = postfixRaw.indexOf("?");
6405
+ const postfix = at === -1 ? postfixRaw : postfixRaw.slice(0, at);
6406
+ const query = at === -1 ? "" : postfixRaw.slice(at);
6407
+ const inner = template ? quasis.slice(1, -1).map((quasi) => `${quoteMeta(quasi)}.*`).join("") : "";
6408
+ const dir = contextDir(importerDir, context);
6409
+ if (dir === void 0) return void 0;
6410
+ return {
6411
+ dir,
6412
+ recursive: true,
6413
+ regExp: new RegExp(`^${quoteMeta(prefix)}.*${inner}${quoteMeta(postfix)}$`),
6414
+ include: void 0,
6415
+ exclude: void 0,
6416
+ query,
6417
+ category: load.kind === "require" ? "commonjs" : "esm"
6418
+ };
6419
+ }
6420
+ /** `require.context(dir, recursive, regExp, mode)` and
6421
+ * `import.meta.webpackContext(dir, { … })`, with webpack's defaults. */
6422
+ function directContext(load, importerDir) {
6423
+ const { args } = load;
6424
+ if (args === null || !("dir" in args)) return void 0;
6425
+ const regExp = regex(args.regExp);
6426
+ const include = regex(args.include);
6427
+ const exclude = regex(args.exclude);
6428
+ if (regExp === null || include === null || exclude === null) return;
6429
+ const dir = contextDir(importerDir, args.dir);
6430
+ if (dir === void 0) return void 0;
6431
+ return {
6432
+ dir,
6433
+ recursive: args.recursive ?? true,
6434
+ regExp: regExp ?? /^\.\/.*$/,
6435
+ include,
6436
+ exclude,
6437
+ query: "",
6438
+ category: load.kind === "context" ? "commonjs" : "esm"
6439
+ };
6440
+ }
6441
+ /** A group of module directory names, rather than one absolute module
6442
+ * directory. */
6443
+ function isModuleGroup(modules) {
6444
+ return Array.isArray(modules);
6445
+ }
6446
+ /** The requests webpack tests a context file's regExp against
6447
+ * (`RequireContextPlugin`'s `alternativeRequests`). */
6448
+ function alternatives(context, request, reading) {
6449
+ let items = [request];
6450
+ if (!reading.fullySpecified) {
6451
+ const extended = [];
6452
+ for (const item of items) {
6453
+ for (const extension of reading.extensions) if (item.endsWith(extension)) extended.push(item.slice(0, -extension.length));
6454
+ if (!reading.enforceExtension) extended.push(item);
6455
+ }
6456
+ items = [];
6457
+ for (const item of extended) {
6458
+ for (const mainFile of reading.mainFiles) if (item.endsWith(`/${mainFile}`)) {
6459
+ items.push(item.slice(0, -mainFile.length));
6460
+ items.push(item.slice(0, -mainFile.length - 1));
6461
+ }
6462
+ items.push(item);
6463
+ }
6464
+ }
6465
+ const requests = [];
6466
+ for (const item of items) {
6467
+ let hidden = false;
6468
+ for (const modules of reading.modules) if (isModuleGroup(modules)) {
6469
+ for (const name of modules) if (item.startsWith(`./${name}/`)) {
6470
+ requests.push(item.slice(name.length + 3));
6471
+ hidden = true;
6472
+ }
6473
+ } else {
6474
+ const dir = modules.replace(/\\/g, "/");
6475
+ const full = context.replace(/\\/g, "/") + item.slice(1);
6476
+ if (full.startsWith(dir)) requests.push(full.slice(dir.length + 1));
6477
+ }
6478
+ if (!hidden) requests.push(item);
6479
+ }
6480
+ return requests;
6481
+ }
6482
+ /** The files a context holds (`resolveDependencies`): below its directory,
6483
+ * dot-led names skipped, `exclude` and `include` tested on each absolute
6484
+ * path, a file kept when any of its alternative requests matches. */
6485
+ function contextFiles(context, reading) {
6486
+ const files = [];
6487
+ const visit = (dir, visited) => {
6488
+ const real = realpathSync(dir);
6489
+ const names = readdirSync(dir).map((name) => name.normalize("NFC"));
6490
+ if (visited.has(real)) return;
6491
+ const inside = new Set(visited).add(real);
6492
+ for (const name of names) {
6493
+ if (name.startsWith(".")) continue;
6494
+ const path = `${dir}${sep}${name}`;
6495
+ if (context.exclude?.test(path)) continue;
6496
+ const stat = statSync(path);
6497
+ if (stat.isDirectory()) {
6498
+ if (context.recursive) visit(path, inside);
6499
+ } else if (stat.isFile() && (!context.include || context.include.test(path))) {
6500
+ const request = `.${path.slice(context.dir.length).replace(/\\/g, "/")}`;
6501
+ if (alternatives(context.dir, request, reading).some((alternative) => context.regExp.test(alternative))) files.push(realpathSync(path));
6502
+ }
6503
+ }
6504
+ };
6505
+ visit(context.dir, /* @__PURE__ */ new Set());
6506
+ return files.sort();
6507
+ }
6508
+ /**
6509
+ * How a webpack 5 build expands computed loads (spec §6.3): a template or
6510
+ * concatenated `import()` or `require()`, `require.context` and
6511
+ * `import.meta.webpackContext`, each a context module whose files
6512
+ * `ContextModuleFactory` lists. `resolveReading` is the resolver's options
6513
+ * for a dependency category; `wrappedDefaults` is false where the project
6514
+ * changes `wrappedContextRegExp` or `wrappedContextRecursive`. Anything not
6515
+ * reproduced exactly lists no expansion.
6516
+ */
6517
+ function webpackLoadExpander(resolveReading, wrappedDefaults) {
6518
+ return { expand(load, rootDir) {
6519
+ const importerDir = dirname(resolve(rootDir, load.importer));
6520
+ let context;
6521
+ if (load.kind === "import" || load.kind === "require") context = wrappedDefaults ? specifierContext(load, importerDir) : void 0;
6522
+ else if (load.kind === "context" || load.kind === "webpackContext") context = directContext(load, importerDir);
6523
+ if (context === void 0) return void 0;
6524
+ const reading = resolveReading(context.category);
6525
+ if (reading === void 0 || reading.aliasNames.length > 0) return;
6526
+ let files;
6527
+ let realRoot;
6528
+ try {
6529
+ files = contextFiles(context, reading);
6530
+ realRoot = realpathSync(rootDir);
6531
+ } catch {
6532
+ return;
6533
+ }
6534
+ return files.map((file) => ({
6535
+ path: relative(realRoot, file).split(sep).join(posix.sep),
6536
+ query: context.query
6537
+ }));
6538
+ } };
6539
+ }
6540
+ /** A rule entry, rather than `'...'` or a falsy placeholder. */
6541
+ function isRule(rule) {
6542
+ return rule instanceof Object;
6543
+ }
6544
+ /** Every rule, nested `rules` and `oneOf` included. */
6545
+ function everyRule(rules) {
6546
+ return rules.filter(isRule).flatMap((rule) => [
6547
+ rule,
6548
+ ...everyRule(rule.rules ?? []),
6549
+ ...everyRule(rule.oneOf ?? [])
6550
+ ]);
6551
+ }
6552
+ /**
6553
+ * The expander for a webpack 5 compiler. Each expansion reads the
6554
+ * compiler's options again and lists nothing where a rule carries its own
6555
+ * resolve options or context parser options, `module.defaultRules` is not
6556
+ * webpack's own, or the options cannot be read.
6557
+ */
6558
+ function webpackHostExpander(reading) {
6559
+ return { expand(load, rootDir) {
6560
+ let options;
6561
+ try {
6562
+ options = reading.options();
6563
+ } catch {
6564
+ return;
6565
+ }
6566
+ if (everyRule(options.rules).some((rule) => rule.resolve !== void 0 || Object.keys(rule.parser ?? {}).some((key) => key.includes("Context"))) || !options.defaultRulesAreWebpacks) return void 0;
6567
+ return webpackLoadExpander(reading.resolveReading, options.wrappedDefaults).expand(load, rootDir);
6568
+ } };
6569
+ }
6570
+ //#endregion
6571
+ //#region pipeline/served-files.ts
6572
+ function servedFiles(manifest) {
6573
+ const dropped = /* @__PURE__ */ new Map();
6574
+ for (const input of manifest.missing_inputs ?? []) {
6575
+ if (input.outcome === "failed-parent") continue;
6576
+ if (!dropped.has(input.file)) dropped.set(input.file, input);
6577
+ }
6578
+ return {
6579
+ listed: new Set(Object.entries(manifest.files ?? {}).filter(([, ids]) => ids.length > 0).map(([file]) => file)),
6580
+ dropped
6581
+ };
6582
+ }
6583
+ /** Why a file dropped over its extension parent is not served: its builder
6584
+ * code would render with empty classes. */
6585
+ function droppedExtensionMessage(drop) {
6586
+ const from = drop.specifier === null ? "" : ` from '${drop.specifier}'`;
6587
+ return `[animus-extract] cannot serve ${drop.file}: '${drop.component}' extends '${drop.parent}'${from}, which the analysis could not resolve (${drop.outcome}); serving it untransformed would render it without its styles. Make the parent an analysed Animus component, or remove the extension.`;
6588
+ }
6589
+ //#endregion
6590
+ export { excludeCollectedPackages as $, createSourceIdentity as $t, postProcessCss as A, substituteAssetPlaceholders as An, webpackExtensionPolicy as At, noOutputTargets as B, __require as Bn, CORE_OPTION_KEYS as Bt, serializeStaticCss as C, systemLoadDiagnostics as Cn, tsconfigChainPaths as Ct, clearEngineCache as D, findSheetAssetSpecifiers as Dn, rollupExtensionPolicy as Dt, buildAnalysisInputs as E, findAssetSpecifiers as En, extensionPolicyJson as Et, resolveThroughPathAliases as F, buildAnalyzeProjectArgs as Fn, pruneStaleAssets as Ft, discoveryResolver as G, assertKnownOptionKeys as Gt, rollupOutputTargets as H, DRIVER_NAMESPACES as Ht, createSourceCorpus as I, ANIMUS_LAYERS as In, writeAssetCopy as It, packageInstanceFinding as J, outputSourceGuard as Jt, AliasAgreementCache as K, createExcludeMatcher as Kt, analysedOutputs as L, assembleStylesheet as Ln, RETIRED_ENGINE_MESSAGE as Lt, buildPathAliasesJson as M, caseMismatchReason as Mn, discoverFiles as Mt, enforceExternalTokenContracts as N, contentHash as Nn, DEFAULT_RUNTIME_IMPORT as Nt, runProjectAnalysis as O, generatedModuleCode as On, turbopackExtensionPolicy as Ot, resolveAssetFile as P, parseInternalWire as Pn, assertRuntimeImportSuppliesTerminals as Pt, engineModuleParser as Q, unmatchedExcludeLines as Qt, esbuildOutputTargets as R, stripLeadingLayerDeclaration as Rn, assertSupportedEngine as Rt, applyPrefix as S, surfaceManifestDiagnostics as Sn, readTsconfigAliasPairs as St, stableStringify as T, vocabularyWitnessDiagnostics as Tn, esbuildExtensionPolicy as Tt, viteOutputTargets as U, REPLACEABLE_DEFAULT_EXCLUDE as Ut, outputExclusions as V, DEFAULT_EXCLUDE as Vt, webpackOutputTargets as W, STRUCTURAL_EXCLUDE as Wt, bareSpecifierPackageName as X, resolveMode as Xt, packageReadRecord as Y, outputSourceMessage as Yt, collectExternalPackageSources as Z, resolveVerbosity as Zt, buildSystemPropsModule as _, VOCABULARY_COLLISION as _n, declaredWorkspacePackages as _t, viteHostExpander as a, parseFilesJson as an, isExcludedPackageRelativePath as at, loadSystemConfig as b, noKitFilesDiagnostics as bn, kitDescriptorDiagnostics as bt, esbuildAssetClassification as c, adaptedOriginals as cn, kitSourceModuleSideEffects as ct, toWatchKeys as d, isEngineTransformExtension as dn, probeAbsolutePathSpecifier as dt, isPathWithinRoot as en, extractSystemFilePackages as et, diffFilePlans as f, preprocessMdx as fn, sourceKitDependencies as ft, assertNoErrorDiagnostics as g, SYSTEM_INSTANCE_MISMATCH as gn, walkPackageSources as gt, kitPublicationFailures as h, INVALID_PROPERTY_REGISTRATION as hn, unresolvableIncludesMessage as ht, webpackLoadExpander as i, isAdvisorySourceDiagnostic as in, importedKitDiagnostics as it, resolveLightningTargets as j, substituteSheetAssets as jn, compareDiscoveryOrder as jt, applyUnitFallback as k, reportSurvivingAssetPlaceholders as kn, viteExtensionPolicy as kt, nextImageAssetClassification as l, DEFAULT_EXTENSIONS as ln, nativeKitSourceRoot as lt, snapshotFilePlans as m, DiagnosticFailure as mn, systemKitDependencies as mt, servedFiles as n, createV2EngineApi as nn, findPackageRoot as nt, viteLoadExpander as o, projectExternalFileOwners as on, isPackageSpecifier as ot, hashReplacementPlans as p, DIAGNOSTIC_LEVELS as pn, staleDistIncludesMessage as pt, compareAliases as q, outputExclusion as qt, webpackHostExpander as r, ingestSourceEntries as rn, firstOwners as rt, builtInAssetClassification as s, adaptSvelteSource as sn, keepsKitSourceEffects as st, droppedExtensionMessage as t, sharesVolumeRoot as tn, fileSideEffects as tt, viteAssetClassification as u, ENGINE_TRANSFORM_EXTENSIONS as un, nativePackageLocator as ut, buildDynamicPropConfig as v, VOCABULARY_LEGACY_VERB as vn, KIT_DESCRIPTOR_FILE as vt, camelToKebab as w, unreadableSourceDiagnostic as wn, BUILT_IN_EXTENSION_POLICY as wt, systemLoadReads as x, severityFor as xn, readKitDescriptor as xt, runStructuralSelfCheck as y, isDeletedSource as yn, buildKitDescriptor as yt, mergeOutputTargets as z, validateLayerOrder as zn, AnimusConfigError as zt };