@entrorove/skill 0.1.70 → 0.1.73

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 (42) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/IDE-EXECUTION.md +4 -0
  3. package/SKILL.md +3 -2
  4. package/local-budget-reads.mjs +7 -2
  5. package/local-budget.mjs +3 -2
  6. package/local-probe.mjs +16 -7
  7. package/local-scope-build-data.mjs +73 -0
  8. package/local-scope-build-facts.mjs +33 -0
  9. package/local-scope-build-io.mjs +57 -0
  10. package/local-scope-build-read.mjs +22 -0
  11. package/local-scope-build-readset.mjs +49 -0
  12. package/local-scope-files.mjs +20 -7
  13. package/local-scope-repository.mjs +47 -37
  14. package/local-scope-signature.mjs +6 -4
  15. package/local-scope-state.mjs +7 -5
  16. package/local-scope-uni-build.mjs +69 -119
  17. package/local-scope-uni-conditional.mjs +8 -6
  18. package/local-scope-uni-directives.mjs +5 -5
  19. package/local-scope-uni-expressions.mjs +10 -5
  20. package/local-scope-workspaces.mjs +3 -3
  21. package/manifest.json +1 -1
  22. package/package.json +12 -2
  23. package/release-source-groups.mjs +46 -0
  24. package/release-state.json +40 -30
  25. package/release.mjs +6 -40
  26. package/releases.json +18 -0
  27. package/remote-client.mjs +1 -1
  28. package/remote-transport.mjs +14 -0
  29. package/scope-amendment-client.mjs +31 -19
  30. package/scope-amendment-reference.mjs +22 -4
  31. package/scope-amendment-state.mjs +1 -1
  32. package/scope-context-client.mjs +17 -8
  33. package/scope-context-journal.mjs +5 -4
  34. package/scope-context-observer.mjs +3 -2
  35. package/scope-context-remote.mjs +5 -5
  36. package/scope-context-state.mjs +4 -1
  37. package/scope-context-wire.mjs +9 -1
  38. package/scope-evidence-local.mjs +52 -0
  39. package/scope-evidence-remote.mjs +78 -0
  40. package/scope-evidence-wire.mjs +120 -0
  41. package/scope-runner-client.mjs +65 -35
  42. package/scope-runner-journal.mjs +32 -0
@@ -5,7 +5,7 @@ import { promisify } from 'node:util';
5
5
  import { builtinModules } from 'node:module';
6
6
  import ts from 'typescript';
7
7
  import { parse as parseVue, parseCache as vueParseCache } from '@vue/compiler-sfc';
8
- import { lineCount, observedScopeRisk, packageBoundary, scopeFail, scopeFiles, scopeProbeTarget, scopeRead, scopeTarget, stableScope } from './local-scope-files.mjs';
8
+ import { lineCount, observedScopeRisk, packageBoundary, scopeFail, scopeFiles, scopeProbeTarget, scopeRead, scopeBuildSourceRead, scopeTarget, stableScope } from './local-scope-files.mjs';
9
9
  import { sha256 } from './local-integrity.mjs';
10
10
  import { workspaceImport, workspaceIndex } from './local-scope-workspaces.mjs';
11
11
  import { assertBudgetRunnerAllowed } from './local-budget-reads.mjs';
@@ -19,6 +19,8 @@ import { createScopeRequireContexts } from './local-scope-create-require.mjs';
19
19
  import { resolveCreatedRequire } from './local-scope-require-resolution.mjs';
20
20
  import { uniConditionalVariants } from './local-scope-uni-conditional.mjs';
21
21
  import { createUniBuildInspection } from './local-scope-uni-build.mjs';
22
+ import { createScopeBuildIO } from './local-scope-build-io.mjs';
23
+ import { observeBuildFacts } from './local-scope-build-facts.mjs';
22
24
 
23
25
  const execute = promisify(execFile);
24
26
  const inspectionFreshness = new WeakMap();
@@ -244,6 +246,9 @@ function declarationModules(content, from) {
244
246
  && node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword)).map(node => node.name.text));
245
247
  }
246
248
  async function resolveImport(root, from, specifier, workspace, input, aliases, configPath, declarations) {
249
+ if (specifier.startsWith('/') || /^[A-Za-z]:/.test(specifier) || specifier.includes('\\') || /[\u0000-\u001f\u007f]/.test(specifier)) {
250
+ scopeFail('ENTROROVE_PATH_UNSAFE', 'Source module addresses require safe repository-relative or declared package selectors');
251
+ }
247
252
  if (/^(?:https?:|file:|data:)/i.test(specifier)) scopeFail('ENTROROVE_IMPORT_UNSUPPORTED', 'remote source imports cannot be inspected as local facts');
248
253
  if (declarations.has(specifier)) return null;
249
254
  const label = /^[A-Za-z0-9@._/~+-]{1,256}$/.test(specifier) ? specifier : '[unsupported specifier]';
@@ -258,9 +263,9 @@ async function resolveImport(root, from, specifier, workspace, input, aliases, c
258
263
  const localPackage = !specifier.startsWith('.') ? workspaceImport(workspace, specifier) : null;
259
264
  if (!specifier.startsWith('.') && !localPackage) {
260
265
  if (builtinModules.includes(specifier) || (specifier.startsWith('node:') && builtinModules.includes(specifier.slice(5)))) return null;
261
- const boundary = await packageBoundary(root, configPath ?? from, input);
266
+ const boundary = await packageBoundary(root, configPath ?? from, input, scopeBuildSourceRead);
262
267
  if (boundary.manifestPath) {
263
- const manifest = JSON.parse((await scopeRead(root, boundary.manifestPath, input)).content.toString('utf8'));
268
+ const manifest = JSON.parse((await scopeBuildSourceRead(root, boundary.manifestPath, input)).content.toString('utf8'));
264
269
  const name = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
265
270
  if (['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].some(field => Object.hasOwn(manifest[field] ?? {}, name))) return null;
266
271
  }
@@ -284,25 +289,29 @@ async function externalConfigReference(root, relative, resolved) {
284
289
  async function configMetadata(root, relative, input) {
285
290
  const target = await scopeProbeTarget(root, relative, dependencyReference(relative));
286
291
  if (!target.exists || !target.stat.isFile()) throw Object.assign(new Error(`Missing configuration file: ${relative}`), { code: 'ENOENT' });
287
- const read = await scopeRead(root, relative, input), resolved = await fs.realpath(target.target);
292
+ const read = await scopeBuildSourceRead(root, relative, input), resolved = await fs.realpath(target.target);
288
293
  const physical = path.relative(root, resolved).split(path.sep).join('/');
289
294
  const outside = physical === '..' || physical.startsWith('../') || path.isAbsolute(physical);
290
295
  const realPath = outside ? relative : physical;
291
296
  const referenceRoot = outside ? await externalConfigReference(root, relative, resolved) : undefined;
292
- return { path: relative, sha256: read.sha256, contentBase64: read.content.toString('base64'), realPath, referenceRoot };
293
- }
294
- async function conditionalBuildDirectory(root, relative, maximumEntries) {
295
- const target = relative === '.' ? root : (await scopeTarget(root, relative, false, dependencyReference(relative))).target;
296
- const directory = await fs.opendir(target); const environmentFiles = []; let entries = 0;
297
- for await (const entry of directory) {
298
- if (++entries > maximumEntries) scopeFail('ENTROROVE_SCOPE_LIMIT', 'Conditional build directory exceeds its bounded entry inspection');
299
- if (entry.name === '.env' || entry.name.startsWith('.env.')) environmentFiles.push(entry.name);
300
- }
301
- return { environmentFiles, entries };
297
+ return { path: relative, sha256: read.sha256, mode: read.mode, contentBase64: read.content.toString('base64'), realPath, referenceRoot };
302
298
  }
303
299
  function conditionalBuildInspection(root, input) {
304
- return createUniBuildInspection(relative => configMetadata(root, relative, input),
305
- (relative, maximumEntries) => conditionalBuildDirectory(root, relative, maximumEntries));
300
+ const io = createScopeBuildIO(root, relative => configMetadata(root, relative, input));
301
+ return createUniBuildInspection(io.read, io.inspectDirectory, { probe: io.probe, repositoryRoot: root });
302
+ }
303
+ function sourceProjection(content, relative, root) {
304
+ // Only these pure parsers run here. Filesystem, privacy and budget checks occur
305
+ // outside this boundary and cannot become a semantic uncertainty.
306
+ try {
307
+ const conditional = /\.vue$/i.test(relative) ? uniConditionalVariants(content.toString('utf8'), relative) : undefined;
308
+ return { conditional, imports: importSpecifiers(content, relative, root, conditional) };
309
+ } catch (error) {
310
+ if (error.code !== 'ENTROROVE_IMPORT_UNSUPPORTED') throw error;
311
+ return { conditional: undefined, imports: { specifiers: [], requires: [], dynamic: true,
312
+ unresolvedImports: [{ line: error.line ?? 1, column: error.column ?? 1, reason: 'source-projection-unsupported',
313
+ message: 'Source syntax has no supported static projection; the original hashed source remains read-only' }] } };
314
+ }
306
315
  }
307
316
  async function resolveLocalImport(root, relative, required = true) {
308
317
  if (relative.startsWith('../') || relative === '..') scopeFail('ENTROROVE_PATH_UNSAFE', 'local import escapes the repository');
@@ -318,6 +327,8 @@ async function resolveLocalImport(root, relative, required = true) {
318
327
  }
319
328
  export async function importGraph(root, originals, input = { repositoryRoot: root }, limits = {}) {
320
329
  const pending = originals.filter(file => file.exists !== false).map(file => file.path); const visited = new Map(); const edges = []; let dynamic = false;
330
+ const explicitSources = new Set(pending);
331
+ const graphRead = (actualRoot, relative, binding) => (explicitSources.has(relative) ? scopeRead : scopeBuildSourceRead)(actualRoot, relative, binding);
321
332
  let sourceBytes = 0;
322
333
  const dynamicSources = [], unresolvedImports = [];
323
334
  const pendingCreates = new Set(originals.filter(file => file.exists === false && input.scopeCreatePaths?.includes(file.path)).map(file => file.path));
@@ -334,7 +345,7 @@ export async function importGraph(root, originals, input = { repositoryRoot: roo
334
345
  }
335
346
  sourceBytes += target.stat.size;
336
347
  }
337
- const read = await scopeRead(root, relative, input); const boundary = await packageBoundary(root, relative, input);
348
+ const read = await graphRead(root, relative, input); const boundary = await packageBoundary(root, relative, input, scopeBuildSourceRead);
338
349
  visited.set(relative, { path: relative, sha256: read.sha256, module: boundary.path, boundary });
339
350
  if (!sourceExtension.test(relative)) continue;
340
351
  const contexts = (input.importContexts ?? []).filter(context => relative === context.sourcePrefix || relative.startsWith(`${context.sourcePrefix}/`))
@@ -342,20 +353,23 @@ export async function importGraph(root, originals, input = { repositoryRoot: roo
342
353
  const configPath = contexts[0]?.configPath;
343
354
  const aliases = await scopeTsconfigImports(target => configMetadata(root, target, input), relative, configPath);
344
355
  for (const observed of aliases.observations) metadata.set(observed.path, observed);
345
- const conditional = /\.vue$/i.test(relative) ? uniConditionalVariants(read.content.toString('utf8'), relative) : undefined;
346
- if (conditional?.directives.length) for (const observed of await inspectBuild(relative, configPath)) metadata.set(observed.path, observed);
356
+ const projection = sourceProjection(read.content, relative, root), conditional = projection.conditional;
357
+ const build = await inspectBuild(relative, configPath, { hasConditionals: Boolean(conditional?.directives.length) });
358
+ const opaque = await observeBuildFacts(root, build, input, { visited, metadata, dynamicSources, unresolvedImports });
359
+ dynamic ||= opaque;
347
360
  const declarations = declarationModules(read.content, relative);
348
- const imports = importSpecifiers(read.content, relative, root, conditional); dynamic ||= imports.dynamic;
361
+ const imports = projection.imports; dynamic ||= imports.dynamic;
349
362
  unresolvedImports.push(...imports.unresolvedImports.map(site => ({ path: relative, ...site })));
350
- if (imports.dynamic) dynamicSources.push({ path: relative, sha256: read.sha256 });
363
+ if (imports.dynamic && !dynamicSources.some(item => item.path === relative)) dynamicSources.push({ path: relative, sha256: read.sha256 });
351
364
  for (const specifier of imports.specifiers.sort()) {
352
365
  let target;
353
366
  try { target = await resolveImport(root, relative, specifier, workspace, input, aliases, configPath, declarations); }
354
367
  catch (error) {
355
368
  const missing = error.code === 'ENTROROVE_IMPORT_UNRESOLVED' && specifier.startsWith('.')
356
369
  ? error.missingCandidates?.filter(candidate => pendingCreates.has(candidate)) : null;
357
- if (!missing?.length) throw error;
358
- unresolvedImports.push({ path: relative, specifier, reason: 'approved-create-not-present', candidatePaths: missing });
370
+ if (error.code !== 'ENTROROVE_IMPORT_UNRESOLVED') throw error;
371
+ unresolvedImports.push({ path: relative, specifier, reason: missing?.length ? 'approved-create-not-present' : 'source-import-unresolved',
372
+ candidatePaths: missing?.length ? missing : error.missingCandidates ?? [] });
359
373
  dynamic = true;
360
374
  if (!dynamicSources.some(source => source.path === relative)) dynamicSources.push({ path: relative, sha256: read.sha256 });
361
375
  continue;
@@ -372,15 +386,16 @@ export async function importGraph(root, originals, input = { repositoryRoot: roo
372
386
  }
373
387
  }
374
388
  const nodes = [...visited.values()].sort((a, b) => a.path.localeCompare(b.path));
375
- await stableScope(root, nodes, input);
389
+ await stableScope(root, nodes, input, graphRead);
376
390
  await inspectBuild.verify();
377
391
  const workspaceMetadata = [...metadata.values()].sort((a, b) => a.path.localeCompare(b.path));
378
- await stableScope(root, workspaceMetadata, input);
392
+ await stableScope(root, workspaceMetadata, input, scopeBuildSourceRead);
379
393
  const graph = { nodes, edges: edges.sort((a, b) => `${a.from}:${a.specifier}`.localeCompare(`${b.from}:${b.specifier}`)),
380
394
  dynamic, complete: !dynamic, dynamicSources: dynamicSources.sort((a, b) => a.path.localeCompare(b.path)), workspaceMetadata,
381
395
  ...(unresolvedImports.length ? { unresolvedImports } : {}) };
382
396
  const actualFiles = [...nodes, ...workspaceMetadata].map(file => ({ path: file.path, sha256: file.sha256 }));
383
- inspectionFreshness.set(graph, async () => { await inspectBuild.verify(); await stableScope(root, actualFiles, input); });
397
+ inspectionFreshness.set(graph, async () => { await inspectBuild.verify(); await stableScope(root, actualFiles, input,
398
+ (actualRoot, relative, binding) => metadata.has(relative) ? scopeBuildSourceRead(actualRoot, relative, binding) : graphRead(actualRoot, relative, binding)); });
384
399
  return graph;
385
400
  }
386
401
  export async function scopeCandidateImports(root, file, input = { repositoryRoot: root }) {
@@ -389,10 +404,6 @@ export async function scopeCandidateImports(root, file, input = { repositoryRoot
389
404
  .sort((a, b) => b.sourcePrefix.length - a.sourcePrefix.length)[0];
390
405
  const aliases = await scopeTsconfigImports(relative => configMetadata(root, relative, input), file.path, context?.configPath);
391
406
  const conditional = /\.vue$/i.test(file.path) ? uniConditionalVariants(file.content, file.path) : undefined;
392
- let inspectBuild;
393
- if (conditional?.directives.length) {
394
- inspectBuild = conditionalBuildInspection(root, input); await inspectBuild(file.path, context?.configPath);
395
- }
396
407
  const parsed = importSpecifiers(Buffer.from(file.content), file.path, root, conditional);
397
408
  const declarations = declarationModules(Buffer.from(file.content), file.path), edges = [];
398
409
  for (const specifier of parsed.specifiers) {
@@ -401,10 +412,9 @@ export async function scopeCandidateImports(root, file, input = { repositoryRoot
401
412
  }
402
413
  for (const request of parsed.requires) {
403
414
  const resolved = await resolveCreatedRequire(file.path, request.anchor, request.specifier, target => configMetadata(root, target, input));
404
- await stableScope(root, resolved.metadata, input);
415
+ await stableScope(root, resolved.metadata, input, scopeBuildSourceRead);
405
416
  edges.push({ from: file.path, to: resolved.external ? null : resolved.target, external: resolved.external, specifier: request.specifier });
406
417
  }
407
- await inspectBuild?.verify();
408
418
  return { edges: edges.sort((a, b) => `${a.from}:${a.specifier}`.localeCompare(`${b.from}:${b.specifier}`)), dynamic: parsed.dynamic,
409
419
  ...(parsed.unresolvedImports.length ? { unresolvedImports: parsed.unresolvedImports.map(site => ({ path: file.path, ...site })) } : {}) };
410
420
  }
@@ -417,17 +427,17 @@ export async function collectScopeFacts(root, requested, baselineRef, knownFiles
417
427
  files.push({ path: relative, exists: false, sha256: sha256('entrorove.missing-file/1.0'), bytes: 0, lines: 0, module: previous.module });
418
428
  continue;
419
429
  }
420
- const read = await scopeRead(root, relative, input); const boundary = await packageBoundary(root, relative, input);
430
+ const read = await scopeRead(root, relative, input); const boundary = await packageBoundary(root, relative, input, scopeBuildSourceRead);
421
431
  files.push({ path: relative, exists: true, sha256: read.sha256, bytes: read.bytes, lines: lineCount(read.content), module: boundary.path });
422
432
  }
423
433
  const graph = await importGraph(root, files, input);
424
434
  await contextEvidence(root, graph, input);
425
435
  const verifyGraph = inspectionFreshness.get(graph), observedMetadata = graph.workspaceMetadata.map(file => ({ path: file.path, sha256: file.sha256 }));
426
- inspectionFreshness.set(graph, async () => { await verifyGraph(); await stableScope(root, observedMetadata, input); });
436
+ inspectionFreshness.set(graph, async () => { await verifyGraph(); await stableScope(root, observedMetadata, input, scopeBuildSourceRead); });
427
437
  const observedGit = await observeGitFacts(root, files, baselineRef, input); const history = observedGit.facts;
428
438
  const modules = [...new Map(graph.nodes.map(node => [node.boundary.path, node.boundary])).values()].sort((a, b) => a.path.localeCompare(b.path));
429
439
  await stableScope(root, files, input);
430
- await stableScope(root, modules.filter(module => module.manifestPath).map(module => ({ path: module.manifestPath, sha256: module.manifestDigest })), input);
440
+ await stableScope(root, modules.filter(module => module.manifestPath).map(module => ({ path: module.manifestPath, sha256: module.manifestDigest })), input, scopeBuildSourceRead);
431
441
  if (history.available && (await scopeGitMetadata(root, ['rev-parse', '--verify', 'HEAD'], input, [128]))?.trim() !== history.head) scopeFail('ENTROROVE_SCOPE_CHANGED', 'Git HEAD changed during scope inspection');
432
442
  const sourceLines = files.reduce((sum, file) => sum + file.lines, 0); const bytes = files.reduce((sum, file) => sum + file.bytes, 0);
433
443
  // An actual current diff (including empty untracked files) is authoritative; clean Git uses history, unknown Git uses source lines.
@@ -452,12 +462,12 @@ async function contextEvidence(root, graph, input) {
452
462
  if (!target.stat.isFile() || target.stat.size > 1_048_576 || (bytes += target.stat.size) > 8_388_608) {
453
463
  scopeFail('ENTROROVE_SCOPE_LIMIT', 'Context evidence exceeds its bounded actual file inspection');
454
464
  }
455
- const read = await scopeRead(root, file.path, input);
465
+ const read = await scopeBuildSourceRead(root, file.path, input);
456
466
  if (read.sha256 !== file.sha256 || metadata.has(file.path) && metadata.get(file.path).sha256 !== read.sha256) {
457
467
  scopeFail('ENTROROVE_SCOPE_CHANGED', 'Actual context build evidence changed since its exact review');
458
468
  }
459
469
  if (!metadata.has(file.path)) metadata.set(file.path, { path: file.path, sha256: read.sha256 });
460
470
  }
461
471
  graph.workspaceMetadata = [...metadata.values()].sort((left, right) => left.path.localeCompare(right.path));
462
- await stableScope(root, graph.workspaceMetadata, input);
472
+ await stableScope(root, graph.workspaceMetadata, input, scopeBuildSourceRead);
463
473
  }
@@ -2,7 +2,7 @@ import { createPrivateKey, createPublicKey, randomUUID, sign } from 'node:crypto
2
2
  import { promises as fs } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { canonical, sha256 } from './local-integrity.mjs';
5
- import { scopeFail, scopeRead, scopeControlRead, scopeRoot, stableScope } from './local-scope-files.mjs';
5
+ import { scopeFail, scopeRead, scopeBuildSourceRead, scopeControlRead, scopeRoot, stableScope } from './local-scope-files.mjs';
6
6
  import { collectScopeFacts, verifyScopeInspectionFreshness } from './local-scope-repository.mjs';
7
7
  import { readScopeSnapshot, verifyFreshScopeSnapshot } from './local-scope-snapshot.mjs';
8
8
  import { scopeGitMetadata } from './local-scope-git-reads.mjs';
@@ -51,10 +51,12 @@ async function signingChallenge(input, beforeSign, maximumExpiresAt) {
51
51
  async function verifySigningFreshness(input, root, actual) {
52
52
  await verifyScopeInspectionFreshness(actual);
53
53
  await verifyFreshScopeSnapshot(input, root, actual);
54
- await stableScope(root, actual.importGraph.nodes, input);
55
- await stableScope(root, actual.importGraph.workspaceMetadata, input);
54
+ const explicit = new Set(actual.files.map(file => file.path));
55
+ await stableScope(root, actual.importGraph.nodes, input,
56
+ (actualRoot, relative, binding) => (explicit.has(relative) ? scopeRead : scopeBuildSourceRead)(actualRoot, relative, binding));
57
+ await stableScope(root, actual.importGraph.workspaceMetadata, input, scopeBuildSourceRead);
56
58
  await stableScope(root, actual.modules.filter(module => module.manifestPath)
57
- .map(module => ({ path: module.manifestPath, sha256: module.manifestDigest })), input);
59
+ .map(module => ({ path: module.manifestPath, sha256: module.manifestDigest })), input, scopeBuildSourceRead);
58
60
  if (actual.git.available && (await scopeGitMetadata(root, ['rev-parse', '--verify', 'HEAD'], input, [128]))?.trim() !== actual.git.head) {
59
61
  scopeFail('ENTROROVE_SCOPE_CHANGED', 'Git HEAD changed before the original context signature');
60
62
  }
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { canonical, sha256, verifySnapshotManifest } from './local-integrity.mjs';
7
7
  import { withStateLock } from './local-state-lock.mjs';
8
- import { scopeFail, scopeRead, scopeControlRead, scopeTarget, stableScope } from './local-scope-files.mjs';
8
+ import { scopeFail, scopeRead, scopeBuildSourceRead, scopeControlRead, scopeTarget, stableScope } from './local-scope-files.mjs';
9
9
 
10
10
  const ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
11
11
  const order = ['lock', 'probe', 'swarm'];
@@ -63,13 +63,13 @@ async function originalSnapshot(input, root, base) {
63
63
  }
64
64
  return { manifest, directory, digest: manifest.snapshotDigest };
65
65
  }
66
- async function capture(root, files, directory, input) {
66
+ async function capture(root, files, directory, input, readSource = scopeRead) {
67
67
  for (const file of files) {
68
68
  if (file.exists === false) {
69
69
  if ((await scopeTarget(root, file.path, true)).exists) scopeFail('ENTROROVE_SCOPE_CHANGED', 'a deleted source reappeared before preservation');
70
70
  continue;
71
71
  }
72
- const read = await scopeRead(root, file.path, input);
72
+ const read = await readSource(root, file.path, input);
73
73
  if (read.sha256 !== file.sha256) scopeFail('ENTROROVE_SCOPE_CHANGED', 'scope source changed before snapshot preservation');
74
74
  const destination = path.join(directory, file.path);
75
75
  await fs.mkdir(path.dirname(destination), { recursive: true, mode: 0o700 });
@@ -107,16 +107,18 @@ function preservedFiles(facts) {
107
107
  }
108
108
  async function saveRevision(location, root, state, facts, mode, initial, input) {
109
109
  const staged = await fs.mkdtemp(path.join(location.directory, '.revision-'));
110
+ const explicit = new Set(facts.files.map(file => file.path));
111
+ const readSource = (actualRoot, relative, binding) => (explicit.has(relative) ? scopeRead : scopeBuildSourceRead)(actualRoot, relative, binding);
110
112
  const final = path.join(location.directory, `revision-${state.revision + 1}-${randomUUID()}`); let committed = false;
111
113
  try {
112
114
  const currentDirectory = path.join(staged, 'current'); await fs.mkdir(currentDirectory, { mode: 0o700 });
113
- await capture(root, preservedFiles(facts), currentDirectory, input);
115
+ await capture(root, preservedFiles(facts), currentDirectory, input, readSource);
114
116
  if (initial) {
115
117
  const original = path.join(staged, 'original'); await fs.mkdir(original, { mode: 0o700 });
116
118
  await preserveSnapshotCopies14(input, initial.directory, initial.manifest, original);
117
119
  await fs.writeFile(path.join(original, 'manifest.json'), `${canonical(initial.manifest)}\n`, { flag: 'wx', mode: 0o600 });
118
120
  }
119
- await stableScope(root, preservedFiles(facts), input); await fs.rename(staged, final);
121
+ await stableScope(root, preservedFiles(facts), input, readSource); await fs.rename(staged, final);
120
122
  const record = historyRecord(state, facts, path.join(final, 'current'), mode);
121
123
  const next = { ...state, revision: record.revision, mode, history: [...state.history, record] };
122
124
  if (initial) next.originalSnapshot = { ...next.originalSnapshot, directory: path.join(final, 'original') };
@@ -1,135 +1,85 @@
1
1
  import path from 'node:path';
2
- import ts from 'typescript';
3
2
  import { uniConditionalError } from './local-scope-uni-expressions.mjs';
3
+ import { createBuildDataReadset } from './local-scope-build-readset.mjs';
4
+ import { buildConfigurationNames, buildDataPath, buildModuleData, buildScriptReferences } from './local-scope-build-data.mjs';
4
5
 
5
- const controls = /\bUNI_(?:CUSTOM_DEFINE|SCRIPT_(?:DEFINE|ENV)|COMPILER_VERSION|APP_X(?:_DOM2)?|UTS_PLATFORM)\b/;
6
- const controlKeys = ['UNI_CUSTOM_DEFINE', 'UNI_SCRIPT_DEFINE', 'UNI_SCRIPT_ENV', 'UNI_COMPILER_VERSION', 'UNI_APP_X', 'UNI_APP_X_DOM2', 'UNI_UTS_PLATFORM'];
7
- const configNames = ['vite.config.js', 'vite.config.mjs', 'vite.config.cjs', 'vite.config.ts',
8
- 'vite.config.mts', 'vite.config.cts', 'vue.config.js', 'vue.config.cjs'];
9
- function staticConfiguration(source, filename, fail) {
10
- if (controls.test(source)) throw fail('Custom conditional macro or compiler controls require explicit scanner support');
11
- const tree = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), imports = new Map();
12
- if (tree.parseDiagnostics.length) throw fail('Conditional build configuration has invalid syntax');
13
- let exported = false, nodes = 0;
14
- const value = (node, depth = 0) => {
15
- if (++nodes > 2048 || depth > 24) throw fail('Conditional build configuration exceeds its static inspection bound');
16
- if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) return value(node.expression, depth + 1);
17
- if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)
18
- || [ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.NullKeyword].includes(node.kind)) return;
19
- if (ts.isArrayLiteralExpression(node)) { for (const item of node.elements) value(item, depth + 1); return; }
20
- if (ts.isObjectLiteralExpression(node)) {
21
- for (const property of node.properties) {
22
- if (!ts.isPropertyAssignment(property) || ts.isComputedPropertyName(property.name)) throw fail('Dynamic build properties cannot establish default conditional macros');
23
- value(property.initializer, depth + 1);
24
- }
25
- return;
26
- }
27
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
28
- const operation = imports.get(node.expression.text);
29
- if (operation === 'uni' && node.arguments.length === 0) return;
30
- if (operation === 'defineConfig' && node.arguments.length === 1) return value(node.arguments[0], depth + 1);
31
- }
32
- throw fail('Dynamic build configuration cannot establish default conditional macros without execution');
33
- };
34
- for (const statement of tree.statements) {
35
- if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
36
- const module = statement.moduleSpecifier.text, clause = statement.importClause;
37
- if (module === '@dcloudio/vite-plugin-uni' && clause?.name && !clause.namedBindings) imports.set(clause.name.text, 'uni');
38
- else if (module === 'vite' && !clause?.name && clause?.namedBindings && ts.isNamedImports(clause.namedBindings)
39
- && clause.namedBindings.elements.every(element => (element.propertyName ?? element.name).text === 'defineConfig')) {
40
- for (const element of clause.namedBindings.elements) imports.set(element.name.text, 'defineConfig');
41
- } else throw fail('Custom build imports cannot establish default conditional macros without execution');
42
- } else if (ts.isExportAssignment(statement) && !exported && !statement.isExportEquals) { exported = true; value(statement.expression); }
43
- else if (ts.isExpressionStatement(statement) && !exported && ts.isBinaryExpression(statement.expression)
44
- && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken
45
- && statement.expression.left.getText(tree) === 'module.exports') { exported = true; value(statement.expression.right); }
46
- else if (!ts.isEmptyStatement(statement)) throw fail('Custom build statements cannot establish default conditional macros without execution');
47
- }
48
- if (!exported) throw fail('Conditional build configuration requires a statically inspectable export');
6
+ const controlKeys = ['UNI_CUSTOM_DEFINE', 'UNI_CUSTOM_CONTEXT', 'UNI_SCRIPT_DEFINE', 'UNI_SCRIPT_ENV', 'UNI_COMPILER_VERSION',
7
+ 'UNI_APP_X', 'UNI_APP_X_DOM2', 'UNI_UTS_PLATFORM', 'UNI_INPUT_DIR', 'VITE_ROOT_DIR', 'UNI_CLI_CONTEXT',
8
+ 'UNI_HBUILDERX', 'UNI_HBUILDERX_PLUGINS', 'NODE_PATH', 'NODE_OPTIONS'];
9
+ function assertProcessControls(filename) {
10
+ const present = controlKeys.filter(key => Object.hasOwn(process.env, key) || Object.hasOwn(process, key));
11
+ if (present.length) throw uniConditionalError(filename, 0, `Build process override controls are unsupported: ${present.join(', ')}`);
49
12
  }
50
- function packageConfiguration(source, fail) {
51
- let manifest;
52
- try { manifest = JSON.parse(source); } catch { throw fail('Conditional build package manifest is invalid JSON'); }
53
- const scripts = manifest?.['uni-app']?.scripts;
54
- if (scripts !== undefined) {
55
- if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts)) throw fail('Custom uni-app build scripts are not statically inspectable');
56
- for (const configuration of Object.values(scripts)) {
57
- if (!configuration || typeof configuration !== 'object' || Array.isArray(configuration)) throw fail('Custom uni-app build entry is not statically inspectable');
58
- if (configuration.define !== undefined && (!configuration.define || typeof configuration.define !== 'object'
59
- || Array.isArray(configuration.define) || Object.keys(configuration.define).length)) {
60
- throw fail('Custom uni-app defines, including builtin macro overrides, require explicit scanner support');
61
- }
62
- if (controls.test(JSON.stringify(configuration))) throw fail('Custom conditional build environment requires explicit scanner support');
63
- }
64
- }
65
- for (const [name, command] of Object.entries(manifest?.scripts ?? {})) {
66
- if (typeof command !== 'string' || controls.test(command)) throw fail('Custom conditional build command requires explicit scanner support');
67
- if (/^(?:dev|build)(?::|$)/.test(name) && (!/^(?:cross-env\s+NODE_ENV=(?:development|production|test)\s+)?(?:uni|vite|vue-cli-service\s+uni-(?:build|serve))(?:\s+[A-Za-z0-9_./:@=-]+)*$/.test(command)
68
- || /(?:^|\s)(?:--config(?:[=\s]|$)|-c(?:\s|$)|--define(?:[=\s]|$))/.test(command))) {
69
- throw fail('Custom conditional build command requires explicit scanner support');
70
- }
71
- }
13
+ function jsonData(item) {
14
+ let value; try { value = JSON.parse(Buffer.from(item.contentBase64, 'base64').toString('utf8')); }
15
+ catch { throw uniConditionalError(item.path, 0, 'Build package data is invalid JSON'); }
16
+ if (!value || typeof value !== 'object' || Array.isArray(value) || value.scripts !== undefined
17
+ && (!value.scripts || typeof value.scripts !== 'object' || Array.isArray(value.scripts))) throw uniConditionalError(item.path, 0, 'Build package data requires an object and string script map');
18
+ return value;
72
19
  }
73
- function parentDirectories(relative, output, fail) {
74
- let directory = path.posix.dirname(relative);
75
- while (true) {
76
- output.add(directory);
77
- if (output.size > 128) throw fail('Conditional build roots exceed their 128-directory inspection bound');
78
- if (directory === '.') return;
20
+ async function hostData(sourcePath, configPath, state) {
21
+ let directory = path.posix.dirname(configPath ?? sourcePath);
22
+ for (let count = 0; count < 128; count++) {
23
+ const relative = path.posix.join(directory, 'package.json'), item = await state.observe(relative);
24
+ if (item) return { root: directory, item, manifest: jsonData(item) };
25
+ if (directory === '.') return { root: path.posix.dirname(configPath ?? sourcePath), item: null, manifest: {} };
79
26
  directory = path.posix.dirname(directory);
80
27
  }
28
+ throw uniConditionalError(sourcePath, 0, 'Build host data exceeds its bounded package ancestry');
81
29
  }
82
- function assertProcessControls(filename) {
83
- const present = controlKeys.filter(key => Object.hasOwn(process.env, key) || Object.hasOwn(process, key));
84
- if (present.length) throw uniConditionalError(filename, 0, `Conditional compilation override controls are unsupported: ${present.join(', ')}`);
85
- }
30
+ function sourceText(item) { return Buffer.from(item.contentBase64, 'base64').toString('utf8'); }
86
31
  export function createUniBuildInspection(read, inspectDirectory) {
87
- const inspected = new Map(), directoriesInspected = new Set(); let candidates = 0, bytes = 0, directoryEntries = 0;
88
- const inspect = async relative => {
89
- if (inspected.has(relative)) return inspected.get(relative);
90
- if (++candidates > 2048) throw uniConditionalError(relative, 0, 'Conditional build lookup exceeds its 2048-path bound');
91
- let item;
92
- try { item = await read(relative); } catch (error) { if (error.code === 'ENOENT') { inspected.set(relative, null); return null; } throw error; }
93
- const source = Buffer.from(item.contentBase64, 'base64').toString('utf8'), fail = reason => uniConditionalError(relative, 0, reason);
94
- bytes += Buffer.byteLength(source);
95
- if (Buffer.byteLength(source) > 1_048_576 || bytes > 8_388_608) throw fail('Conditional build metadata exceeds its bounded inspection size');
96
- if (relative.endsWith('package.json')) packageConfiguration(source, fail); else staticConfiguration(source, relative, fail);
97
- const observed = { path: item.path, sha256: item.sha256 }; inspected.set(relative, observed); return observed;
98
- };
99
- const inspectSource = async (sourcePath, configPath) => {
100
- const directories = new Set(), fail = reason => uniConditionalError(sourcePath, 0, reason);
101
- assertProcessControls(sourcePath);
102
- parentDirectories(sourcePath, directories, fail); if (configPath) parentDirectories(configPath, directories, fail);
103
- const observations = new Map();
104
- for (const directory of directories) {
105
- if (!directoriesInspected.has(directory)) {
106
- if (typeof inspectDirectory !== 'function') throw fail('Conditional build environment absence requires actual bounded directory inspection');
107
- const listing = await inspectDirectory(directory, Math.min(8192, 32768 - directoryEntries));
108
- directoryEntries += listing.entries;
109
- if (listing.environmentFiles.length) throw fail('Conditional sources with ancestor environment files require explicit scanner support; environment values were not read');
110
- directoriesInspected.add(directory);
32
+ const state = createBuildDataReadset(read, inspectDirectory), contexts = new Map(), modules = new Map(); let controlsRequired = false;
33
+ const inspectSource = async (sourcePath, configPath, context = { hasConditionals: true }) => {
34
+ const key = `${sourcePath}:${configPath ?? ''}:${Boolean(context.hasConditionals)}`;
35
+ if (contexts.has(key)) return contexts.get(key);
36
+ const host = await hostData(sourcePath, configPath, state), unresolved = new Map(), queue = [], visited = new Set();
37
+ const mark = (item, reason) => { if (item) unresolved.set(`${item.path}:${reason}`, { path: item.path, sha256: item.sha256, reason }); };
38
+ const enqueue = async (relative, reason) => {
39
+ const item = await state.observe(relative); if (item) { mark(item, reason); queue.push(item); } return item;
40
+ };
41
+ if (configPath) await state.observe(buildDataPath('.', configPath));
42
+ for (const name of buildConfigurationNames) await enqueue(buildDataPath(host.root, name), 'opaque-build-config');
43
+ const scripts = buildScriptReferences(host.manifest, host.item?.path);
44
+ if (scripts.opaque) mark(host.item, 'opaque-build-command');
45
+ for (const reference of scripts.references) await enqueue(buildDataPath(host.root, reference), 'opaque-build-loader');
46
+ if (host.manifest['uni-app']?.scripts !== undefined) mark(host.item, 'custom-build-definitions');
47
+ const roots = new Set([host.root]);
48
+ while (queue.length) {
49
+ const item = queue.shift(); if (visited.has(item.path)) continue; visited.add(item.path);
50
+ if (visited.size > 128) throw Object.assign(uniConditionalError(item.path, 0, 'Build source data exceeds its 128-module bound'), { code: 'ENTROROVE_SCOPE_LIMIT' });
51
+ if (/\.json$/.test(item.path) || /(?:^|\/)\.babelrc$/.test(item.path)) continue;
52
+ if (item.path.split('/').includes('node_modules')) continue;
53
+ if (!modules.has(item.path)) modules.set(item.path, buildModuleData(sourceText(item), item.path));
54
+ const parsed = modules.get(item.path);
55
+ for (const literal of parsed.literalRoots) {
56
+ const root = literal.root !== undefined ? buildDataPath(host.root, literal.root)
57
+ : literal.context !== undefined ? buildDataPath(host.root, literal.context) : host.root;
58
+ roots.add(literal.envDir === undefined ? root : buildDataPath(root, literal.envDir));
111
59
  }
112
- for (const name of ['package.json', ...configNames]) {
113
- const relative = directory === '.' ? name : `${directory}/${name}`, observed = await inspect(relative);
114
- if (observed) observations.set(relative, observed);
60
+ for (const reference of parsed.references) {
61
+ const target = buildDataPath(path.posix.dirname(item.path), reference);
62
+ const candidates = /\.[A-Za-z0-9]+$/.test(target) ? [target]
63
+ : ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.json', '/index.js', '/index.cjs', '/index.ts'].map(extension => `${target}${extension}`);
64
+ for (const candidate of candidates) await enqueue(candidate, 'opaque-build-loader');
115
65
  }
116
66
  }
117
- return [...observations.values()];
118
- };
119
- inspectSource.verify = async () => {
120
- if (!directoriesInspected.size) return;
121
- assertProcessControls('conditional-build'); let entries = 0;
122
- for (const directory of directoriesInspected) {
123
- const listing = await inspectDirectory(directory, Math.min(8192, 32768 - entries)); entries += listing.entries;
124
- if (listing.environmentFiles.length) throw Object.assign(uniConditionalError(directory, 0,
125
- 'Conditional build environment appeared after its actual inspection; environment values were not read'), { code: 'ENTROROVE_SCOPE_CHANGED' });
126
- }
127
- for (const [relative, expected] of inspected) {
128
- let actual;
129
- try { actual = await read(relative); } catch (error) { if (error.code !== 'ENOENT') throw error; }
130
- if ((actual?.sha256 ?? null) !== (expected?.sha256 ?? null)) throw Object.assign(
131
- uniConditionalError(relative, 0, 'Conditional build metadata changed during inspection'), { code: 'ENTROROVE_SCOPE_CHANGED' });
67
+ if (unresolved.size || context.hasConditionals) {
68
+ controlsRequired = true; assertProcessControls(sourcePath);
69
+ for (const root of roots) {
70
+ const listing = await state.directory(root);
71
+ if (listing?.environmentFiles.length) {
72
+ const origin = [...visited].map(path => state.observations.find(item => item.path === path)).find(Boolean) ?? host.item;
73
+ if (origin) mark(origin, 'build-environment-present');
74
+ else mark(await state.observe(sourcePath), 'build-environment-present');
75
+ }
76
+ }
132
77
  }
78
+ const proof = { sourceProjection: 'raw-source', buildObservation: unresolved.size ? 'opaque' : 'not-observed',
79
+ get observations() { return state.observations; },
80
+ unresolvedBuildSources: [...unresolved.values()].sort((a, b) => `${a.path}:${a.reason}`.localeCompare(`${b.path}:${b.reason}`)) };
81
+ contexts.set(key, proof); return proof;
133
82
  };
83
+ inspectSource.verify = async () => { if (controlsRequired) assertProcessControls('build-data'); await state.verify(); };
134
84
  return inspectSource;
135
85
  }
@@ -1,5 +1,5 @@
1
1
  import { uniDirectives } from './local-scope-uni-directives.mjs';
2
- import { uniCondition, uniConditionContexts, uniConditionalError } from './local-scope-uni-expressions.mjs';
2
+ import { uniCondition, uniConditionContexts, uniConditionalError, uniConditionalLimitError } from './local-scope-uni-expressions.mjs';
3
3
 
4
4
  const blank = source => source.replace(/[^\r\n]/g, ' ');
5
5
  function conditionalSegments(source, directives, filename) {
@@ -8,10 +8,11 @@ function conditionalSegments(source, directives, filename) {
8
8
  segments.push({ start: cursor, end: item.start, guards: stack.map(guard => ({ ...guard })) });
9
9
  segments.push({ start: item.start, end: item.end, directive: true }); cursor = item.end;
10
10
  const fail = reason => uniConditionalError(filename, item.start, reason, source);
11
+ const limit = reason => uniConditionalLimitError(filename, item.start, reason, source);
11
12
  if (['ifdef', 'ifndef', 'if'].includes(item.kind)) {
12
- if (stack.length >= 32) throw fail('Conditional compilation exceeds its 32-level nesting bound');
13
+ if (stack.length >= 32) throw limit('Conditional compilation exceeds its 32-level nesting bound');
13
14
  const condition = uniCondition(item.expression, fail), index = conditions.length;
14
- if ((conditionTokens += condition.tokens) > 4096) throw fail('Conditional expressions exceed their 4096-token aggregate inspection bound');
15
+ if ((conditionTokens += condition.tokens) > 4096) throw limit('Conditional expressions exceed their 4096-token aggregate inspection bound');
15
16
  conditions.push(condition); stack.push({ index, negated: item.kind === 'ifndef', syntax: item.syntax, alternative: false });
16
17
  } else {
17
18
  const active = stack.at(-1);
@@ -29,15 +30,16 @@ function conditionalSegments(source, directives, filename) {
29
30
  const activeSegment = (segment, key) => !segment.directive && segment.guards.every(guard => (key[guard.index] === '1') !== guard.negated);
30
31
  export function uniConditionalVariants(source, filename) {
31
32
  const fail = reason => uniConditionalError(filename, 0, reason, source);
33
+ const limit = reason => uniConditionalLimitError(filename, 0, reason, source);
32
34
  if (typeof source !== 'string') throw fail('Conditional inspection requires actual source text');
33
35
  const unchanged = () => ({ directives: [], variants: [{ source, key: '', label: 'unconditional' }] });
34
36
  if (!/(?:<!--|\/\/|\/\*)[ \t]*#/i.test(source)) return unchanged();
35
- if (Buffer.byteLength(source) > 2_097_152) throw fail('Conditional source exceeds its 2 MiB inspection bound');
37
+ if (Buffer.byteLength(source) > 2_097_152) throw limit('Conditional source exceeds its 2 MiB inspection bound');
36
38
  try {
37
39
  const directives = uniDirectives(source, filename);
38
40
  if (!directives.length) return unchanged();
39
41
  const { conditions, segments } = conditionalSegments(source, directives, filename), contexts = uniConditionContexts(conditions, fail);
40
- if (Buffer.byteLength(source) * contexts.length > 33_554_432) throw fail('Conditional source variants exceed their 32 MiB inspection bound');
42
+ if (Buffer.byteLength(source) * contexts.length > 33_554_432) throw limit('Conditional source variants exceed their 32 MiB inspection bound');
41
43
  for (const segment of segments) if (!segment.directive && source.slice(segment.start, segment.end).trim()
42
44
  && !contexts.some(context => activeSegment(segment, context.key))) {
43
45
  throw uniConditionalError(filename, segment.start, 'Conditional branch has no proven platform context; custom or contradictory build definitions require explicit support', source);
@@ -48,7 +50,7 @@ export function uniConditionalVariants(source, filename) {
48
50
  }).join('') }));
49
51
  return { directives: directives.map(({ kind, expression, syntax }) => ({ kind, expression, syntax })), variants };
50
52
  } catch (error) {
51
- if (error instanceof RangeError) throw Object.assign(fail('Conditional syntax exceeds its parser or traversal bound'), { code: 'ENTROROVE_SCOPE_LIMIT' });
53
+ if (error instanceof RangeError) throw limit('Conditional syntax exceeds its parser or traversal bound');
52
54
  throw error;
53
55
  }
54
56
  }
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { uniConditionalError } from './local-scope-uni-expressions.mjs';
2
+ import { uniConditionalError, uniConditionalLimitError } from './local-scope-uni-expressions.mjs';
3
3
 
4
4
  function directive(source, start, end, text, syntax, filename, output) {
5
5
  const match = /^\s*#([a-z]+)\b([\s\S]*)$/i.exec(text);
@@ -10,15 +10,15 @@ function directive(source, start, end, text, syntax, filename, output) {
10
10
  || ['else', 'endif'].includes(kind) && expression) {
11
11
  throw uniConditionalError(filename, start, 'Conditional directive syntax is unsupported', source);
12
12
  }
13
- if (output.length >= 256) throw uniConditionalError(filename, start, 'Conditional directives exceed their 256-entry bound', source);
13
+ if (output.length >= 256) throw uniConditionalLimitError(filename, start, 'Conditional directives exceed their 256-entry bound', source);
14
14
  output.push({ start, end, kind, expression, syntax });
15
15
  }
16
- function literalRanges(content, kind) {
16
+ function literalRanges(content, kind, filename, start, source) {
17
17
  const parsed = ts.createSourceFile('uni-script.ts', content, ts.ScriptTarget.Latest, true, kind);
18
18
  const pending = [parsed], ranges = []; let nodes = 0;
19
19
  while (pending.length) {
20
20
  const node = pending.pop();
21
- if (++nodes > 200_000) throw new RangeError('Conditional lexical inspection exceeds its node bound');
21
+ if (++nodes > 200_000) throw uniConditionalLimitError(filename, start, 'Conditional lexical inspection exceeds its 200000-node bound', source);
22
22
  if (ts.isStringLiteralLike(node) || ts.isRegularExpressionLiteral(node)
23
23
  || [ts.SyntaxKind.TemplateHead, ts.SyntaxKind.TemplateMiddle, ts.SyntaxKind.TemplateTail].includes(node.kind)) {
24
24
  ranges.push({ start: node.getStart(parsed), end: node.end });
@@ -27,7 +27,7 @@ function literalRanges(content, kind) {
27
27
  return ranges.sort((a, b) => a.start - b.start);
28
28
  }
29
29
  function scriptComments(source, start, end, filename, output, kind) {
30
- const content = source.slice(start, end), literals = literalRanges(content, kind); let literal = 0;
30
+ const content = source.slice(start, end), literals = literalRanges(content, kind, filename, start, source); let literal = 0;
31
31
  for (let index = 0; index < content.length;) {
32
32
  while (literals[literal]?.end <= index) literal++;
33
33
  if (literals[literal]?.start <= index) { index = literals[literal].end; continue; }