arkgate 4.8.22 → 4.8.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/README.md +7 -5
- package/bin/ark-check-runtime.mjs +27 -78
- package/bin/ark-dashboard.mjs +56 -53
- package/bin/ark-layer-match.mjs +1117 -38
- package/bin/ark-mcp-runtime.mjs +412 -138
- package/bin/ark-mcp.mjs +68 -4
- package/bin/ark-shared.mjs +39 -0
- package/bin/ark.mjs +65 -97
- package/bin/lib/adapter-contract-types.mjs +5 -0
- package/bin/lib/adapter-contract.mjs +15 -0
- package/bin/lib/agent-projection-command.mjs +5 -1
- package/bin/lib/analysis-engine.mjs +15 -11
- package/bin/lib/architecture-scan.mjs +50 -6
- package/bin/lib/ark-order-doctor.mjs +1 -1
- package/bin/lib/ark-order-facts.mjs +410 -15
- package/bin/lib/ark-order-invariants.mjs +51 -9
- package/bin/lib/ark-order-sensors.mjs +28 -101
- package/bin/lib/ark-order-types.mjs +2 -0
- package/bin/lib/ark-run-doctor.mjs +14 -0
- package/bin/lib/ark-run-facts.mjs +434 -11
- package/bin/lib/ark-run-sensors.mjs +5 -3
- package/bin/lib/arkgate-pins.mjs +91 -0
- package/bin/lib/arkrules-sensors.mjs +50 -249
- package/bin/lib/check-args.mjs +23 -1
- package/bin/lib/ci-and-commands.mjs +16 -6
- package/bin/lib/class-source-scan.mjs +479 -0
- package/bin/lib/codex-home.mjs +4 -2
- package/bin/lib/config-contract-slices.mjs +701 -0
- package/bin/lib/config-contract.mjs +5 -2
- package/bin/lib/config-warnings.mjs +32 -0
- package/bin/lib/dashboard-cli.mjs +62 -0
- package/bin/lib/design-delta.mjs +1 -1
- package/bin/lib/diagnostic-catalog.mjs +45 -6
- package/bin/lib/doctor-advisories.mjs +84 -1
- package/bin/lib/doctor-next-actions.mjs +17 -2
- package/bin/lib/doctor-plan.mjs +7 -9
- package/bin/lib/effective-contract-load.mjs +121 -59
- package/bin/lib/enforcement-honesty.mjs +22 -0
- package/bin/lib/enforcement-profiles.mjs +40 -4
- package/bin/lib/first-run-help.mjs +3 -2
- package/bin/lib/flat-parent-importers.mjs +137 -0
- package/bin/lib/flat-parent-pilot.mjs +192 -0
- package/bin/lib/gate-files.mjs +29 -2
- package/bin/lib/git-change-scope.mjs +109 -0
- package/bin/lib/github-enforcement.mjs +1 -1
- package/bin/lib/hook-templates.mjs +18 -16
- package/bin/lib/host-support-matrix.mjs +3 -1
- package/bin/lib/html-report-advisories.mjs +2 -0
- package/bin/lib/html-report-depth.mjs +3 -1
- package/bin/lib/import-resolve.mjs +13 -1
- package/bin/lib/install-migrate.mjs +50 -73
- package/bin/lib/invariant-coverage.mjs +59 -11
- package/bin/lib/mcp-adoption.mjs +9 -1
- package/bin/lib/mcp-hook-payload.mjs +401 -61
- package/bin/lib/package-manager.mjs +59 -1
- package/bin/lib/passthrough-commands.mjs +83 -0
- package/bin/lib/persistence-write-hint.mjs +81 -0
- package/bin/lib/physical-cohesion.mjs +16 -18
- package/bin/lib/pilot-loop.mjs +21 -8
- package/bin/lib/pin-dependent-gates.mjs +117 -0
- package/bin/lib/policy-delta-io.mjs +168 -10
- package/bin/lib/prepare-change.mjs +120 -6
- package/bin/lib/prepare-write.mjs +1 -0
- package/bin/lib/presets.mjs +3 -5
- package/bin/lib/remediation.mjs +23 -4
- package/bin/lib/resolved-candidate-facts.mjs +315 -55
- package/bin/lib/rules-inventory.mjs +22 -3
- package/bin/lib/rules-under-contract.mjs +179 -5
- package/bin/lib/scan-files.mjs +32 -0
- package/bin/lib/snippet-analysis.mjs +140 -6
- package/bin/lib/source-policy.mjs +34 -0
- package/bin/lib/stable-hash.mjs +90 -0
- package/bin/lib/start-install-recovery.mjs +71 -1
- package/bin/lib/start-preview.mjs +30 -0
- package/bin/lib/status-command.mjs +33 -4
- package/bin/lib/status-manifest.mjs +32 -4
- package/bin/lib/team-parliament-io.mjs +79 -75
- package/bin/lib/team-parliament.mjs +6 -1
- package/bin/lib/write-path-capabilities.mjs +4 -2
- package/dist/chunk-A5EIY3HI.cjs +5 -0
- package/dist/chunk-MBXCFLO4.js +16 -0
- package/dist/chunk-RNQHRDCQ.cjs +16 -0
- package/dist/chunk-SVSQVGFJ.js +5 -0
- package/dist/{configTypes-CTELo87-.d.ts → configTypes-BJbaeoj4.d.cts} +53 -4
- package/dist/configTypes-BJbaeoj4.d.ts +246 -0
- package/dist/eslint/index.cjs +17 -4
- package/dist/eslint/index.d.cts +359 -0
- package/dist/eslint/index.d.ts +178 -158
- package/dist/eslint/index.js +10 -4
- package/dist/index.cjs +38 -37
- package/dist/index.d.cts +5475 -0
- package/dist/index.d.ts +3408 -296
- package/dist/index.js +38 -37
- package/dist/inspectorListen-6DQVJ4DC.js +1 -0
- package/dist/inspectorListen-KGMIQ2TF.cjs +1 -0
- package/dist/nestjs/index.cjs +1 -18
- package/dist/nestjs/index.d.cts +22 -0
- package/dist/nestjs/index.d.ts +1 -3
- package/dist/nestjs/index.js +1 -18
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.d.cts +155 -0
- package/dist/order/index.d.ts +15 -3
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +13 -30
- package/dist/{diagnosticCatalog-WD2TPSwZ.d.ts → runtime/index.d.cts} +814 -290
- package/dist/runtime/index.d.ts +2678 -108
- package/dist/runtime/index.js +13 -30
- package/dist/{types-yDNEQ603.d.ts → types-DRinG-vT.d.cts} +576 -140
- package/dist/types-DRinG-vT.d.ts +1792 -0
- package/docs/README.md +3 -1
- package/docs/agent-guide.md +92 -15
- package/docs/ai-gates.md +160 -26
- package/docs/arkorder.md +32 -15
- package/docs/brownfield-adoption.md +5 -1
- package/docs/configuration.md +273 -12
- package/docs/develop.md +2 -1
- package/docs/diagnostics.md +122 -13
- package/docs/package-surface.md +31 -8
- package/docs/product-voice.md +1 -1
- package/package.json +59 -18
- package/schemas/ark.analysis-result.schema.json +15 -0
- package/schemas/ark.config.schema.json +163 -4
- package/schemas/ark.resolved-candidate-facts.schema.json +1 -1
- package/schemas/ark.status-manifest.schema.json +22 -0
- package/server.json +3 -9
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +4 -1
- package/templates/agent-skills/ark-order/SKILL.md +4 -2
- package/templates/agent-skills/ark-runtime/SKILL.md +6 -1
- package/templates/arkrules/PersistenceAdapters.json +1 -1
- package/templates/arkrules/PresentationAdapters.json +1 -1
- package/templates/skills/ark-adopt.md +4 -1
- package/templates/skills/ark-order.md +4 -2
- package/templates/skills/ark-runtime.md +6 -1
- package/dist/types-fQ9-KrBj.d.ts +0 -183
|
@@ -36,16 +36,21 @@ import {
|
|
|
36
36
|
import { provePortProofInject } from './port-proof.mjs';
|
|
37
37
|
import { extractClassShapesFromSource } from './arkrules-sensors.mjs';
|
|
38
38
|
import {
|
|
39
|
+
arkRunEffectiveIntentPrefixes,
|
|
40
|
+
createArkRunKernelRootSpecifierMatcher,
|
|
39
41
|
extractArkRunDeclarationsFromSource,
|
|
42
|
+
reexportsArkRunKernelRoot,
|
|
40
43
|
extractArkRunKernelCallsFromSource,
|
|
41
44
|
extractArkRunManagedNewsFromSource,
|
|
42
45
|
} from './ark-run-facts.mjs';
|
|
43
46
|
import {
|
|
47
|
+
extractArkOrderBudgetLeaksFromSource,
|
|
44
48
|
extractArkOrderGenericUpdatesFromSource,
|
|
45
49
|
extractArkOrderIngestWritesXiFromSource,
|
|
46
50
|
extractArkOrderPlaneCallsFromSource,
|
|
47
51
|
extractArkOrderReleaseKeyCountsFromSource,
|
|
48
52
|
extractArkOrderXiFieldWritesFromSource,
|
|
53
|
+
extractArkOrderXiTtlKeysFromSource,
|
|
49
54
|
} from './ark-order-facts.mjs';
|
|
50
55
|
import {
|
|
51
56
|
collectGovernedFiles,
|
|
@@ -89,6 +94,17 @@ function canonicalProjectPath(value) {
|
|
|
89
94
|
return normalized;
|
|
90
95
|
}
|
|
91
96
|
|
|
97
|
+
/**
|
|
98
|
+
* `path.resolve` that reuses an equal string instead of allocating a copy, so
|
|
99
|
+
* one file's absolute/real path is one string across the resolver's maps.
|
|
100
|
+
* `sameAs` lets a real path that equals the lexical path share that instance.
|
|
101
|
+
*/
|
|
102
|
+
function resolvedPath(value, sameAs) {
|
|
103
|
+
const resolved = path.resolve(value);
|
|
104
|
+
if (sameAs !== undefined && resolved === sameAs) return sameAs;
|
|
105
|
+
return resolved === value ? value : resolved;
|
|
106
|
+
}
|
|
107
|
+
|
|
92
108
|
function isInsideRoot(root, target) {
|
|
93
109
|
const relative = path.relative(root, target);
|
|
94
110
|
return (
|
|
@@ -129,13 +145,13 @@ function readPackageName(root, observeInput) {
|
|
|
129
145
|
function rememberCanonicalAlias(aliases, real, relative, absolute) {
|
|
130
146
|
const current = aliases.get(real);
|
|
131
147
|
if (!current || relative < current.relative) {
|
|
132
|
-
aliases.set(real, { relative, absolute:
|
|
148
|
+
aliases.set(real, { relative, absolute: resolvedPath(absolute) });
|
|
133
149
|
}
|
|
134
150
|
}
|
|
135
151
|
|
|
136
152
|
function rememberDirectoryAlias(aliases, real, relative, absolute) {
|
|
137
153
|
const current = aliases.get(real) ?? new Map();
|
|
138
|
-
if (!current.has(relative)) current.set(relative, { relative, absolute:
|
|
154
|
+
if (!current.has(relative)) current.set(relative, { relative, absolute: resolvedPath(absolute) });
|
|
139
155
|
aliases.set(real, current);
|
|
140
156
|
}
|
|
141
157
|
|
|
@@ -244,10 +260,20 @@ function discoverScopedFiles(root, config, scopeRelatives, observeInput) {
|
|
|
244
260
|
);
|
|
245
261
|
}
|
|
246
262
|
|
|
247
|
-
function collectCandidateFiles(
|
|
263
|
+
function collectCandidateFiles(
|
|
264
|
+
root,
|
|
265
|
+
config,
|
|
266
|
+
changes,
|
|
267
|
+
observeInput,
|
|
268
|
+
scopeRelatives,
|
|
269
|
+
{ readContents = true } = {}
|
|
270
|
+
) {
|
|
248
271
|
const files = new Map();
|
|
249
272
|
const directoryAliases = new Map();
|
|
250
273
|
const expandedAliasDirectories = new Set();
|
|
274
|
+
// The walk already resolved each file's real path; reuse it instead of a
|
|
275
|
+
// second realpath per file.
|
|
276
|
+
const walkedRealpaths = new Map();
|
|
251
277
|
const discovered = Array.isArray(scopeRelatives)
|
|
252
278
|
? discoverScopedFiles(root, config, scopeRelatives, observeInput)
|
|
253
279
|
: (config.include ?? [])
|
|
@@ -259,13 +285,16 @@ function collectCandidateFiles(root, config, changes, observeInput, scopeRelativ
|
|
|
259
285
|
if (relative === '.') relative = '';
|
|
260
286
|
rememberDirectoryAlias(directoryAliases, real, relative, absolute);
|
|
261
287
|
},
|
|
288
|
+
onFile(absolute, real) {
|
|
289
|
+
walkedRealpaths.set(absolute, real);
|
|
290
|
+
},
|
|
262
291
|
})
|
|
263
292
|
)
|
|
264
293
|
.map((absolute) => {
|
|
265
294
|
observeResolvedInput(observeInput, absolute, 'realpath');
|
|
266
295
|
return {
|
|
267
296
|
absolute,
|
|
268
|
-
real: fs.realpathSync(absolute),
|
|
297
|
+
real: walkedRealpaths.get(absolute) ?? fs.realpathSync(absolute),
|
|
269
298
|
relative: canonicalProjectPath(normalize(path.relative(root, absolute))),
|
|
270
299
|
};
|
|
271
300
|
})
|
|
@@ -306,12 +335,14 @@ function collectCandidateFiles(root, config, changes, observeInput, scopeRelativ
|
|
|
306
335
|
}
|
|
307
336
|
}
|
|
308
337
|
for (const { absolute, real, relative } of canonicalByRealpath.values()) {
|
|
309
|
-
|
|
338
|
+
// Path-only callers (overlay canonicalization, tsconfig closure) never read
|
|
339
|
+
// source text: do not load every file's contents for them.
|
|
340
|
+
if (readContents) observeResolvedInput(observeInput, absolute, 'source');
|
|
310
341
|
files.set(relative, {
|
|
311
342
|
path: relative,
|
|
312
|
-
absolute:
|
|
313
|
-
real:
|
|
314
|
-
content: fs.readFileSync(absolute, 'utf8'),
|
|
343
|
+
absolute: resolvedPath(absolute),
|
|
344
|
+
real: resolvedPath(real, absolute),
|
|
345
|
+
...(readContents ? { content: fs.readFileSync(absolute, 'utf8') } : {}),
|
|
315
346
|
});
|
|
316
347
|
}
|
|
317
348
|
|
|
@@ -364,7 +395,9 @@ function collectCandidateFiles(root, config, changes, observeInput, scopeRelativ
|
|
|
364
395
|
/** Canonicalize a Tooling overlay without exposing filesystem identity to Kernel. */
|
|
365
396
|
export function canonicalizeCandidateChanges({ root, config, changes = [] }) {
|
|
366
397
|
const canonicalRoot = fs.realpathSync(root);
|
|
367
|
-
return collectCandidateFiles(canonicalRoot, config, changes
|
|
398
|
+
return collectCandidateFiles(canonicalRoot, config, changes, undefined, undefined, {
|
|
399
|
+
readContents: false,
|
|
400
|
+
}).changes.map((change) =>
|
|
368
401
|
change.delete === true
|
|
369
402
|
? { path: change.path, delete: true }
|
|
370
403
|
: { path: change.path, content: change.content }
|
|
@@ -390,13 +423,17 @@ function createOverlayModuleHost(ts, root, files, changes, observeInput) {
|
|
|
390
423
|
const rememberVirtualDirectories = (absolute) => {
|
|
391
424
|
let directory = path.dirname(absolute);
|
|
392
425
|
while (isInsideRoot(root, directory)) {
|
|
393
|
-
|
|
394
|
-
|
|
426
|
+
const resolved = path.resolve(directory);
|
|
427
|
+
// Every walk records the whole chain up to root, so a known directory
|
|
428
|
+
// already has all of its ancestors: stop instead of re-walking to root.
|
|
429
|
+
if (virtualDirectories.has(resolved) && resolved !== root) break;
|
|
430
|
+
virtualDirectories.add(resolved);
|
|
431
|
+
if (resolved === root) break;
|
|
395
432
|
directory = path.dirname(directory);
|
|
396
433
|
}
|
|
397
434
|
};
|
|
398
435
|
const remember = (absolute, file) => {
|
|
399
|
-
const key =
|
|
436
|
+
const key = resolvedPath(absolute);
|
|
400
437
|
byAbsolute.set(key, file);
|
|
401
438
|
pathByAbsolute.set(key, file.path);
|
|
402
439
|
rememberVirtualDirectories(key);
|
|
@@ -442,7 +479,9 @@ function createOverlayModuleHost(ts, root, files, changes, observeInput) {
|
|
|
442
479
|
observeResolvedInput(observeInput, absolute, 'module-read');
|
|
443
480
|
if (deleted.has(absolute)) return undefined;
|
|
444
481
|
const candidate = byAbsolute.get(absolute);
|
|
445
|
-
|
|
482
|
+
// Overlay candidates carry their proposed text. On-disk candidates are read
|
|
483
|
+
// lazily (their text is not held for the whole resolution).
|
|
484
|
+
if (typeof candidate?.content === 'string') return candidate.content;
|
|
446
485
|
if (sys?.readFile) return sys.readFile(fileName);
|
|
447
486
|
try {
|
|
448
487
|
return fs.readFileSync(fileName, 'utf8');
|
|
@@ -495,6 +534,13 @@ function createOverlayModuleHost(ts, root, files, changes, observeInput) {
|
|
|
495
534
|
};
|
|
496
535
|
|
|
497
536
|
return {
|
|
537
|
+
/** Drop the per-file lookup tables once module resolution is finished. */
|
|
538
|
+
dispose() {
|
|
539
|
+
byAbsolute.clear();
|
|
540
|
+
pathByAbsolute.clear();
|
|
541
|
+
deleted.clear();
|
|
542
|
+
virtualDirectories.clear();
|
|
543
|
+
},
|
|
498
544
|
fileExists,
|
|
499
545
|
readFile,
|
|
500
546
|
directoryExists,
|
|
@@ -597,7 +643,7 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
597
643
|
const configByFile = new Map();
|
|
598
644
|
const nearestConfigByDirectory = new Map();
|
|
599
645
|
if (explicitPath) {
|
|
600
|
-
for (const file of candidateFiles) configByFile.set(
|
|
646
|
+
for (const file of candidateFiles) configByFile.set(resolvedPath(file.absolute), explicitPath);
|
|
601
647
|
} else if (!explicitPath) {
|
|
602
648
|
for (const file of candidateFiles) {
|
|
603
649
|
const nearest = nearestTsconfig(
|
|
@@ -606,7 +652,7 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
606
652
|
nearestConfigByDirectory,
|
|
607
653
|
observeInput
|
|
608
654
|
);
|
|
609
|
-
if (nearest) configByFile.set(
|
|
655
|
+
if (nearest) configByFile.set(resolvedPath(file.absolute), nearest);
|
|
610
656
|
}
|
|
611
657
|
}
|
|
612
658
|
const configPaths = [...new Set(configByFile.values())].sort((left, right) => {
|
|
@@ -678,7 +724,7 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
678
724
|
};
|
|
679
725
|
for (const configPath of configPaths) loadConfigOptions(configPath);
|
|
680
726
|
|
|
681
|
-
const
|
|
727
|
+
const configPathFor = (fileName) => {
|
|
682
728
|
const resolved = path.resolve(fileName);
|
|
683
729
|
let configPath = configByFile.get(resolved);
|
|
684
730
|
if (!configPath) {
|
|
@@ -686,6 +732,10 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
686
732
|
?? nearestTsconfig(root, resolved, nearestConfigByDirectory, observeInput);
|
|
687
733
|
if (configPath) configByFile.set(resolved, configPath);
|
|
688
734
|
}
|
|
735
|
+
return configPath;
|
|
736
|
+
};
|
|
737
|
+
const optionsFor = (fileName) => {
|
|
738
|
+
const configPath = configPathFor(fileName);
|
|
689
739
|
return configPath ? loadConfigOptions(configPath) ?? {} : {};
|
|
690
740
|
};
|
|
691
741
|
const configs = configPaths.map((configPath) => ({
|
|
@@ -713,6 +763,8 @@ function compilerContext(ts, root, tsconfig, candidateFiles, observeInput) {
|
|
|
713
763
|
.sort();
|
|
714
764
|
return {
|
|
715
765
|
optionsFor,
|
|
766
|
+
/** Options are a function of the governing tsconfig path ('' = none). */
|
|
767
|
+
optionsKeyFor: (fileName) => configPathFor(fileName) ?? '',
|
|
716
768
|
reasons,
|
|
717
769
|
configInputPaths,
|
|
718
770
|
tsconfigHash: deterministicHash(stableSerialize(configClosure)),
|
|
@@ -734,7 +786,9 @@ export function resolvedCompilerInputPaths({
|
|
|
734
786
|
if (!ts?.readConfigFile || !ts?.parseJsonConfigFileContent) return [];
|
|
735
787
|
observeResolvedInput(observeInput, root, 'realpath');
|
|
736
788
|
const canonicalRoot = fs.realpathSync(root);
|
|
737
|
-
const candidate = collectCandidateFiles(canonicalRoot, config, changes, observeInput
|
|
789
|
+
const candidate = collectCandidateFiles(canonicalRoot, config, changes, observeInput, undefined, {
|
|
790
|
+
readContents: false,
|
|
791
|
+
});
|
|
738
792
|
return compilerContext(ts, canonicalRoot, tsconfig, candidate.files, observeInput)
|
|
739
793
|
.configInputPaths;
|
|
740
794
|
}
|
|
@@ -1081,6 +1135,30 @@ function unavailableFacts(config, ts, reason) {
|
|
|
1081
1135
|
});
|
|
1082
1136
|
}
|
|
1083
1137
|
|
|
1138
|
+
const SOURCE_READ_FAILURE = Symbol('ark.sourceReadFailure');
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Share per-file parse results between resolutions of one config (a base tree
|
|
1142
|
+
* and its overlay candidate). Pass the same object to both calls; it is only
|
|
1143
|
+
* used while `config` is the identical object.
|
|
1144
|
+
*/
|
|
1145
|
+
export function createResolverIngestCache(config) {
|
|
1146
|
+
return { config, records: new Map() };
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/** Overlay text when present; otherwise read the on-disk source now (not held afterwards). */
|
|
1150
|
+
function readCandidateSource(candidate, observeInput) {
|
|
1151
|
+
if (typeof candidate.content === 'string') return candidate.content;
|
|
1152
|
+
observeResolvedInput(observeInput, candidate.absolute, 'source');
|
|
1153
|
+
try {
|
|
1154
|
+
return fs.readFileSync(candidate.absolute, 'utf8');
|
|
1155
|
+
} catch (error) {
|
|
1156
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
1157
|
+
failure[SOURCE_READ_FAILURE] = true;
|
|
1158
|
+
throw failure;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1084
1162
|
function loadOneCandidateFile(root, config, relative, observeInput) {
|
|
1085
1163
|
const discovered = discoverScopedFiles(root, config, [relative], observeInput);
|
|
1086
1164
|
const found = discovered[0];
|
|
@@ -1088,8 +1166,8 @@ function loadOneCandidateFile(root, config, relative, observeInput) {
|
|
|
1088
1166
|
observeResolvedInput(observeInput, found.absolute, 'source');
|
|
1089
1167
|
return {
|
|
1090
1168
|
path: found.relative,
|
|
1091
|
-
absolute:
|
|
1092
|
-
real:
|
|
1169
|
+
absolute: resolvedPath(found.absolute),
|
|
1170
|
+
real: resolvedPath(found.real, found.absolute),
|
|
1093
1171
|
content: fs.readFileSync(found.absolute, 'utf8'),
|
|
1094
1172
|
};
|
|
1095
1173
|
}
|
|
@@ -1103,6 +1181,7 @@ export function resolveCandidateFacts({
|
|
|
1103
1181
|
changes = [],
|
|
1104
1182
|
observeInput,
|
|
1105
1183
|
scopeFiles,
|
|
1184
|
+
ingestCache,
|
|
1106
1185
|
}) {
|
|
1107
1186
|
if (!ts?.createSourceFile || !ts?.resolveModuleName) {
|
|
1108
1187
|
return unavailableFacts(config, ts, 'No API-compatible TypeScript resolver is available.');
|
|
@@ -1116,12 +1195,15 @@ export function resolveCandidateFacts({
|
|
|
1116
1195
|
try {
|
|
1117
1196
|
observeResolvedInput(observeInput, root, 'realpath');
|
|
1118
1197
|
canonicalRoot = fs.realpathSync(root);
|
|
1198
|
+
// Source text is read one file at a time in `ingest` and released after it,
|
|
1199
|
+
// instead of holding every file's text for the whole resolution.
|
|
1119
1200
|
const candidate = collectCandidateFiles(
|
|
1120
1201
|
canonicalRoot,
|
|
1121
1202
|
config,
|
|
1122
1203
|
changes,
|
|
1123
1204
|
observeInput,
|
|
1124
|
-
scoped ? scopeFiles : undefined
|
|
1205
|
+
scoped ? scopeFiles : undefined,
|
|
1206
|
+
{ readContents: false }
|
|
1125
1207
|
);
|
|
1126
1208
|
candidateFiles = candidate.files;
|
|
1127
1209
|
canonicalChanges = candidate.changes;
|
|
@@ -1141,6 +1223,28 @@ export function resolveCandidateFacts({
|
|
|
1141
1223
|
canonicalChanges,
|
|
1142
1224
|
observeInput
|
|
1143
1225
|
);
|
|
1226
|
+
// TypeScript resolves a specifier from its containing directory (its own
|
|
1227
|
+
// module cache is keyed the same way), and this run's host never changes.
|
|
1228
|
+
// Resolve each (tsconfig, directory, specifier) once instead of once per
|
|
1229
|
+
// importing file: a shared `react` or `@/lib/x` no longer re-walks every
|
|
1230
|
+
// ancestor node_modules for each file. Only the small verdict is kept.
|
|
1231
|
+
const resolutionMemo = new Map();
|
|
1232
|
+
const resolveOnce = (dependency, containingFile) => {
|
|
1233
|
+
if (!dependency.specifier) return resolveDependency(ts, dependency, containingFile, {}, host);
|
|
1234
|
+
const key = `${compiler.optionsKeyFor(containingFile)}\0${path.dirname(containingFile)}\0${dependency.specifier}`;
|
|
1235
|
+
let resolved = resolutionMemo.get(key);
|
|
1236
|
+
if (!resolved) {
|
|
1237
|
+
resolved = resolveDependency(
|
|
1238
|
+
ts,
|
|
1239
|
+
dependency,
|
|
1240
|
+
containingFile,
|
|
1241
|
+
compiler.optionsFor(containingFile),
|
|
1242
|
+
host
|
|
1243
|
+
);
|
|
1244
|
+
resolutionMemo.set(key, resolved);
|
|
1245
|
+
}
|
|
1246
|
+
return resolved;
|
|
1247
|
+
};
|
|
1144
1248
|
const forbiddenGlobals = [
|
|
1145
1249
|
...new Set([
|
|
1146
1250
|
...AMBIENT_CAPABILITY_ENTRIES,
|
|
@@ -1168,6 +1272,8 @@ export function resolveCandidateFacts({
|
|
|
1168
1272
|
const arkOrderXiFieldWrites = [];
|
|
1169
1273
|
const arkOrderIngestWritesXi = [];
|
|
1170
1274
|
const arkOrderReleaseKeyCounts = [];
|
|
1275
|
+
const arkOrderXiTtlKeys = [];
|
|
1276
|
+
const arkOrderBudgetLeaks = [];
|
|
1171
1277
|
const arkRunActive = config.arkRun != null && typeof config.arkRun === 'object';
|
|
1172
1278
|
const arkOrderActive = config.arkOrder != null && typeof config.arkOrder === 'object';
|
|
1173
1279
|
const arkRulesActive =
|
|
@@ -1178,13 +1284,21 @@ export function resolveCandidateFacts({
|
|
|
1178
1284
|
const compositionRootPatterns = arkRunActive
|
|
1179
1285
|
? [...(config.arkRun?.compositionRoots ?? [])]
|
|
1180
1286
|
: [];
|
|
1287
|
+
// Identifiers imported from a kernel root module are traced kernel receivers.
|
|
1288
|
+
const kernelRootPatterns = arkRunActive
|
|
1289
|
+
? [...(config.arkRun?.kernelRoots ?? config.arkRun?.compositionRoots ?? [])]
|
|
1290
|
+
: [];
|
|
1181
1291
|
const planeRootPatterns = arkOrderActive ? [...(config.arkOrder?.planeRoots ?? [])] : [];
|
|
1182
1292
|
|
|
1183
1293
|
const seedPathSet = new Set(candidateFiles.map((file) => file.path));
|
|
1184
|
-
const
|
|
1294
|
+
const ingestFresh = (candidate, fullExtract, knownContent) => {
|
|
1295
|
+
const content = knownContent ?? readCandidateSource(candidate, observeInput);
|
|
1296
|
+
// Keep the text only when a later pass re-reads it (ArkRun kernel calls, kernel-root
|
|
1297
|
+
// barrels and managed-new facts).
|
|
1298
|
+
if (arkRunActive && candidate.content === undefined) candidate.content = content;
|
|
1185
1299
|
const sourceFile = ts.createSourceFile(
|
|
1186
1300
|
candidate.absolute,
|
|
1187
|
-
|
|
1301
|
+
content,
|
|
1188
1302
|
ts.ScriptTarget.Latest,
|
|
1189
1303
|
true,
|
|
1190
1304
|
ts.getScriptKindFromFileName?.(candidate.absolute)
|
|
@@ -1199,10 +1313,10 @@ export function resolveCandidateFacts({
|
|
|
1199
1313
|
namedBindings: namedModuleBindings(ts, node),
|
|
1200
1314
|
}));
|
|
1201
1315
|
let portProofEligible = false;
|
|
1202
|
-
if (/\bimport\s*\{[^}]+\}\s*from\s*['"]\.\.?\//.test(
|
|
1316
|
+
if (/\bimport\s*\{[^}]+\}\s*from\s*['"]\.\.?\//.test(content)) {
|
|
1203
1317
|
try {
|
|
1204
1318
|
portProofEligible = Boolean(
|
|
1205
|
-
provePortProofInject(ts,
|
|
1319
|
+
provePortProofInject(ts, content, { filePath: candidate.absolute, sourceFile })
|
|
1206
1320
|
.eligible
|
|
1207
1321
|
);
|
|
1208
1322
|
} catch {
|
|
@@ -1219,7 +1333,7 @@ export function resolveCandidateFacts({
|
|
|
1219
1333
|
});
|
|
1220
1334
|
files.push({
|
|
1221
1335
|
path: candidate.path,
|
|
1222
|
-
contentHash: deterministicHash(
|
|
1336
|
+
contentHash: deterministicHash(content),
|
|
1223
1337
|
parseStatus: parseDiagnosticCount === 0 ? 'parsed' : 'invalid',
|
|
1224
1338
|
parseDiagnosticCount,
|
|
1225
1339
|
exportsOnlyTypes,
|
|
@@ -1260,7 +1374,7 @@ export function resolveCandidateFacts({
|
|
|
1260
1374
|
publishCalls.push(...policy.publishCalls);
|
|
1261
1375
|
intentReferences.push(...policy.intentReferences);
|
|
1262
1376
|
safetyUses.push(
|
|
1263
|
-
...collectSafetyUses(ts, sourceFile, candidate.path,
|
|
1377
|
+
...collectSafetyUses(ts, sourceFile, candidate.path, content, semanticDependencies)
|
|
1264
1378
|
);
|
|
1265
1379
|
// Class-shape extraction is text-conservative (false negatives over false positives).
|
|
1266
1380
|
// Only TS/TSX candidates; sensors consume the same shape via facts.classShapes.
|
|
@@ -1268,22 +1382,17 @@ export function resolveCandidateFacts({
|
|
|
1268
1382
|
if (/\.(tsx?|mts|cts)$/i.test(candidate.path)) {
|
|
1269
1383
|
if (arkRulesActive || arkRunActive) {
|
|
1270
1384
|
try {
|
|
1271
|
-
classShapes.push(...extractClassShapesFromSource(candidate.path,
|
|
1385
|
+
classShapes.push(...extractClassShapesFromSource(candidate.path, content));
|
|
1272
1386
|
} catch {
|
|
1273
1387
|
// Never fail the resolver for shape extraction; sensors stay silent on this file.
|
|
1274
1388
|
}
|
|
1275
1389
|
}
|
|
1276
1390
|
if (arkRunActive) {
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
...extractArkRunKernelCallsFromSource(candidate.path, candidate.content)
|
|
1280
|
-
);
|
|
1281
|
-
} catch {
|
|
1282
|
-
// Never fail the resolver for ArkRun call extraction.
|
|
1283
|
-
}
|
|
1391
|
+
// Kernel calls are extracted after every file is parsed, so imports of a
|
|
1392
|
+
// kernel root resolve through tsconfig paths and barrel re-exports.
|
|
1284
1393
|
try {
|
|
1285
1394
|
arkRunDeclarations.push(
|
|
1286
|
-
...extractArkRunDeclarationsFromSource(candidate.path,
|
|
1395
|
+
...extractArkRunDeclarationsFromSource(candidate.path, content)
|
|
1287
1396
|
);
|
|
1288
1397
|
} catch {
|
|
1289
1398
|
// Never fail the resolver for ArkRun declaration extraction.
|
|
@@ -1292,44 +1401,125 @@ export function resolveCandidateFacts({
|
|
|
1292
1401
|
if (arkOrderActive) {
|
|
1293
1402
|
try {
|
|
1294
1403
|
arkOrderPlaneCalls.push(
|
|
1295
|
-
...extractArkOrderPlaneCallsFromSource(candidate.path,
|
|
1404
|
+
...extractArkOrderPlaneCallsFromSource(candidate.path, content)
|
|
1296
1405
|
);
|
|
1297
1406
|
} catch {
|
|
1298
1407
|
// Never fail the resolver for ArkOrder factory extraction.
|
|
1299
1408
|
}
|
|
1300
1409
|
try {
|
|
1301
1410
|
arkOrderGenericUpdates.push(
|
|
1302
|
-
...extractArkOrderGenericUpdatesFromSource(candidate.path,
|
|
1411
|
+
...extractArkOrderGenericUpdatesFromSource(candidate.path, content, {
|
|
1412
|
+
planeRoots: planeRootPatterns,
|
|
1413
|
+
})
|
|
1303
1414
|
);
|
|
1304
1415
|
} catch {
|
|
1305
1416
|
// Never fail the resolver for ArkOrder generic-update extraction.
|
|
1306
1417
|
}
|
|
1307
1418
|
try {
|
|
1308
1419
|
arkOrderXiFieldWrites.push(
|
|
1309
|
-
...extractArkOrderXiFieldWritesFromSource(candidate.path,
|
|
1420
|
+
...extractArkOrderXiFieldWritesFromSource(candidate.path, content, xiKeys)
|
|
1310
1421
|
);
|
|
1311
1422
|
} catch {
|
|
1312
1423
|
// Never fail the resolver for ArkOrder xi-field-write extraction.
|
|
1313
1424
|
}
|
|
1314
1425
|
try {
|
|
1315
1426
|
arkOrderIngestWritesXi.push(
|
|
1316
|
-
...extractArkOrderIngestWritesXiFromSource(candidate.path,
|
|
1427
|
+
...extractArkOrderIngestWritesXiFromSource(candidate.path, content)
|
|
1317
1428
|
);
|
|
1318
1429
|
} catch {
|
|
1319
1430
|
// Never fail the resolver for ArkOrder ingest-writes-ξ extraction.
|
|
1320
1431
|
}
|
|
1321
1432
|
try {
|
|
1322
1433
|
arkOrderReleaseKeyCounts.push(
|
|
1323
|
-
...extractArkOrderReleaseKeyCountsFromSource(candidate.path,
|
|
1434
|
+
...extractArkOrderReleaseKeyCountsFromSource(candidate.path, content)
|
|
1324
1435
|
);
|
|
1325
1436
|
} catch {
|
|
1326
1437
|
// Never fail the resolver for ArkOrder release key-count extraction.
|
|
1327
1438
|
}
|
|
1439
|
+
try {
|
|
1440
|
+
arkOrderXiTtlKeys.push(
|
|
1441
|
+
...extractArkOrderXiTtlKeysFromSource(candidate.path, content, {
|
|
1442
|
+
planeRoots: planeRootPatterns,
|
|
1443
|
+
})
|
|
1444
|
+
);
|
|
1445
|
+
} catch {
|
|
1446
|
+
// Never fail the resolver for ArkOrder ξ-TTL extraction.
|
|
1447
|
+
}
|
|
1448
|
+
try {
|
|
1449
|
+
arkOrderBudgetLeaks.push(
|
|
1450
|
+
...extractArkOrderBudgetLeaksFromSource(candidate.path, content)
|
|
1451
|
+
);
|
|
1452
|
+
} catch {
|
|
1453
|
+
// Never fail the resolver for ArkOrder information-budget extraction.
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
// Per-file facts depend only on (config, path, text). A base/candidate pair
|
|
1460
|
+
// (atomic preflight) shares one cache: every file the overlay does not touch
|
|
1461
|
+
// is parsed once, not twice. Overlay files are never cached or reused.
|
|
1462
|
+
const accumulators = {
|
|
1463
|
+
completenessReasons,
|
|
1464
|
+
files,
|
|
1465
|
+
capabilityUses,
|
|
1466
|
+
ambientUses,
|
|
1467
|
+
publishCalls,
|
|
1468
|
+
intentReferences,
|
|
1469
|
+
safetyUses,
|
|
1470
|
+
classShapes,
|
|
1471
|
+
arkRunDeclarations,
|
|
1472
|
+
arkOrderPlaneCalls,
|
|
1473
|
+
arkOrderGenericUpdates,
|
|
1474
|
+
arkOrderXiFieldWrites,
|
|
1475
|
+
arkOrderIngestWritesXi,
|
|
1476
|
+
arkOrderReleaseKeyCounts,
|
|
1477
|
+
arkOrderXiTtlKeys,
|
|
1478
|
+
arkOrderBudgetLeaks,
|
|
1479
|
+
};
|
|
1480
|
+
const accumulatorNames = Object.keys(accumulators);
|
|
1481
|
+
const reusable = ingestCache?.config === config ? ingestCache.records : undefined;
|
|
1482
|
+
const ingest = (candidate, fullExtract) => {
|
|
1483
|
+
if (!reusable || !fullExtract || typeof candidate.content === 'string') {
|
|
1484
|
+
ingestFresh(candidate, fullExtract);
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
const content = readCandidateSource(candidate, observeInput);
|
|
1488
|
+
const contentHash = deterministicHash(content);
|
|
1489
|
+
const cached = reusable.get(candidate.path);
|
|
1490
|
+
if (cached && cached.absolute === candidate.absolute && cached.contentHash === contentHash) {
|
|
1491
|
+
if (arkRunActive) candidate.content = content;
|
|
1492
|
+
parsed.set(candidate.path, { ...cached.parsed, candidate });
|
|
1493
|
+
for (const name of accumulatorNames) {
|
|
1494
|
+
const slice = cached.slices[name];
|
|
1495
|
+
if (slice) accumulators[name].push(...slice);
|
|
1328
1496
|
}
|
|
1497
|
+
return;
|
|
1329
1498
|
}
|
|
1499
|
+
const before = accumulatorNames.map((name) => accumulators[name].length);
|
|
1500
|
+
ingestFresh(candidate, fullExtract, content);
|
|
1501
|
+
const slices = {};
|
|
1502
|
+
accumulatorNames.forEach((name, index) => {
|
|
1503
|
+
if (accumulators[name].length > before[index]) {
|
|
1504
|
+
slices[name] = accumulators[name].slice(before[index]);
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
const { candidate: _candidate, ...parsedFacts } = parsed.get(candidate.path);
|
|
1508
|
+
reusable.set(candidate.path, {
|
|
1509
|
+
absolute: candidate.absolute,
|
|
1510
|
+
contentHash,
|
|
1511
|
+
parsed: parsedFacts,
|
|
1512
|
+
slices,
|
|
1513
|
+
});
|
|
1330
1514
|
};
|
|
1331
1515
|
|
|
1332
|
-
|
|
1516
|
+
try {
|
|
1517
|
+
for (const candidate of candidateFiles) ingest(candidate, true);
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
// Same outcome as the former eager read inside the discovery try-block.
|
|
1520
|
+
if (error?.[SOURCE_READ_FAILURE] !== true) throw error;
|
|
1521
|
+
return unavailableFacts(config, ts, error.message);
|
|
1522
|
+
}
|
|
1333
1523
|
|
|
1334
1524
|
if (scoped) {
|
|
1335
1525
|
const loaded = new Set(candidateFiles.map((file) => file.path));
|
|
@@ -1340,13 +1530,7 @@ export function resolveCandidateFacts({
|
|
|
1340
1530
|
const parsedFile = parsed.get(source.path);
|
|
1341
1531
|
if (!parsedFile) continue;
|
|
1342
1532
|
for (const dependency of parsedFile.dependencies) {
|
|
1343
|
-
const resolved =
|
|
1344
|
-
ts,
|
|
1345
|
-
dependency,
|
|
1346
|
-
parsedFile.candidate.absolute,
|
|
1347
|
-
compiler.optionsFor(parsedFile.candidate.absolute),
|
|
1348
|
-
host
|
|
1349
|
-
);
|
|
1533
|
+
const resolved = resolveOnce(dependency, parsedFile.candidate.absolute);
|
|
1350
1534
|
if (resolved.resolution !== 'resolved-project' || !resolved.target) continue;
|
|
1351
1535
|
if (loaded.has(resolved.target)) continue;
|
|
1352
1536
|
const extra = loadOneCandidateFile(
|
|
@@ -1369,6 +1553,27 @@ export function resolveCandidateFacts({
|
|
|
1369
1553
|
const extractCandidates = scoped
|
|
1370
1554
|
? candidateFiles.filter((file) => seedPathSet.has(file.path))
|
|
1371
1555
|
: candidateFiles;
|
|
1556
|
+
if (arkRunActive) {
|
|
1557
|
+
const rootMatcherFor = arkRunKernelRootMatchers({
|
|
1558
|
+
parsed,
|
|
1559
|
+
patterns: kernelRootPatterns,
|
|
1560
|
+
resolveTarget: (file, dependency) => resolveOnce(dependency, file.candidate.absolute),
|
|
1561
|
+
});
|
|
1562
|
+
const intentPrefixes = arkRunEffectiveIntentPrefixes(config);
|
|
1563
|
+
for (const candidate of extractCandidates) {
|
|
1564
|
+
if (!/\.(tsx?|mts|cts)$/i.test(candidate.path)) continue;
|
|
1565
|
+
try {
|
|
1566
|
+
arkRunKernelCalls.push(
|
|
1567
|
+
...extractArkRunKernelCallsFromSource(candidate.path, candidate.content, {
|
|
1568
|
+
isKernelRootSpecifier: rootMatcherFor(candidate.path),
|
|
1569
|
+
intentPrefixes,
|
|
1570
|
+
})
|
|
1571
|
+
);
|
|
1572
|
+
} catch {
|
|
1573
|
+
// Never fail the resolver for ArkRun call extraction.
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1372
1577
|
const admittedTypeNames = new Set(classShapes.map((shape) => shape.className));
|
|
1373
1578
|
if (arkRunActive) {
|
|
1374
1579
|
for (const candidate of extractCandidates) {
|
|
@@ -1428,13 +1633,7 @@ export function resolveCandidateFacts({
|
|
|
1428
1633
|
const dependencies = [];
|
|
1429
1634
|
for (const source of parsed.values()) {
|
|
1430
1635
|
for (const dependency of source.dependencies) {
|
|
1431
|
-
const resolved =
|
|
1432
|
-
ts,
|
|
1433
|
-
dependency,
|
|
1434
|
-
source.candidate.absolute,
|
|
1435
|
-
compiler.optionsFor(source.candidate.absolute),
|
|
1436
|
-
host
|
|
1437
|
-
);
|
|
1636
|
+
const resolved = resolveOnce(dependency, source.candidate.absolute);
|
|
1438
1637
|
if (resolved.resolverFailed) {
|
|
1439
1638
|
completenessReasons.push({
|
|
1440
1639
|
code: 'MODULE_RESOLUTION_FAILURE',
|
|
@@ -1486,6 +1685,11 @@ export function resolveCandidateFacts({
|
|
|
1486
1685
|
}
|
|
1487
1686
|
|
|
1488
1687
|
const projectPackageName = readPackageName(canonicalRoot, observeInput);
|
|
1688
|
+
// Canonicalization copies every fact. Release the per-file parse records and
|
|
1689
|
+
// the module host (closures keep them reachable) before that copy is built.
|
|
1690
|
+
parsed.clear();
|
|
1691
|
+
resolutionMemo.clear();
|
|
1692
|
+
host.dispose();
|
|
1489
1693
|
return createTrustedResolvedCandidateFacts({
|
|
1490
1694
|
schemaVersion: '1.2',
|
|
1491
1695
|
completeness: completenessReasons.length === 0 ? 'complete' : 'partial',
|
|
@@ -1514,5 +1718,61 @@ export function resolveCandidateFacts({
|
|
|
1514
1718
|
arkOrderXiFieldWrites,
|
|
1515
1719
|
arkOrderIngestWritesXi,
|
|
1516
1720
|
arkOrderReleaseKeyCounts,
|
|
1721
|
+
arkOrderXiTtlKeys,
|
|
1722
|
+
arkOrderBudgetLeaks,
|
|
1517
1723
|
});
|
|
1518
1724
|
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* Per-file predicate: does an import specifier reach an ArkRun kernel root?
|
|
1728
|
+
* Uses real module resolution (tsconfig `paths`, package links) plus the
|
|
1729
|
+
* relative-glob fallback, and treats barrels that re-export a root as roots
|
|
1730
|
+
* (fixpoint), so `import { ark } from '@/main'` or a re-exporting index is traced.
|
|
1731
|
+
*/
|
|
1732
|
+
function arkRunKernelRootMatchers({ parsed, patterns, resolveTarget }) {
|
|
1733
|
+
const regexes = [];
|
|
1734
|
+
for (const pattern of patterns) {
|
|
1735
|
+
try {
|
|
1736
|
+
regexes.push(globToRegExp(pattern));
|
|
1737
|
+
} catch {
|
|
1738
|
+
// Invalid glob: the relative fallback still applies.
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const roots = new Set([...parsed.keys()].filter((file) => regexes.some((re) => re.test(file))));
|
|
1742
|
+
const targets = new Map();
|
|
1743
|
+
if (patterns.length > 0) {
|
|
1744
|
+
for (const [file, info] of parsed) {
|
|
1745
|
+
const bySpecifier = new Map();
|
|
1746
|
+
for (const dependency of info.dependencies) {
|
|
1747
|
+
if (!dependency.specifier || (dependency.kind !== 'import' && dependency.kind !== 'export')) {
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
if (bySpecifier.has(dependency.specifier)) continue;
|
|
1751
|
+
const resolved = resolveTarget(info, dependency);
|
|
1752
|
+
if (resolved.resolution === 'resolved-project' && resolved.target) {
|
|
1753
|
+
bySpecifier.set(dependency.specifier, resolved.target);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
targets.set(file, bySpecifier);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
const matcherFor = (file) => {
|
|
1760
|
+
const relative = createArkRunKernelRootSpecifierMatcher(file, patterns);
|
|
1761
|
+
const bySpecifier = targets.get(file);
|
|
1762
|
+
return (specifier) => {
|
|
1763
|
+
const target = bySpecifier?.get(specifier);
|
|
1764
|
+
return target !== undefined ? roots.has(target) : relative(specifier);
|
|
1765
|
+
};
|
|
1766
|
+
};
|
|
1767
|
+
for (let changed = patterns.length > 0; changed; ) {
|
|
1768
|
+
changed = false;
|
|
1769
|
+
for (const [file, info] of parsed) {
|
|
1770
|
+
if (roots.has(file)) continue;
|
|
1771
|
+
if (reexportsArkRunKernelRoot(info.candidate.content, matcherFor(file))) {
|
|
1772
|
+
roots.add(file);
|
|
1773
|
+
changed = true;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
return matcherFor;
|
|
1778
|
+
}
|