@suss/extractor 0.33.1 → 0.34.1
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/README.md +3 -3
- package/dist/index.cjs +982 -225
- package/dist/index.d.cts +1335 -936
- package/dist/index.d.ts +1335 -936
- package/dist/index.js +952 -212
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { createHash as
|
|
2
|
+
import { createHash as createHash4 } from "crypto";
|
|
3
3
|
import {
|
|
4
|
+
effectKey,
|
|
4
5
|
exchangesHttpResponses as exchangesHttpResponses2,
|
|
5
6
|
foldRepeatedEffects,
|
|
6
7
|
normalizeCalleeText,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
withMountMetadata,
|
|
10
11
|
withRequestSpellingMetadata,
|
|
11
12
|
withSourceDocumentMetadata,
|
|
13
|
+
withTestMetadata,
|
|
12
14
|
withWrapperMetadata as withWrapperMetadata2
|
|
13
15
|
} from "@suss/behavioral-ir";
|
|
14
16
|
|
|
@@ -197,29 +199,82 @@ function composeOne(summary, chain, keepGaps) {
|
|
|
197
199
|
if (wrappers.length === 0) {
|
|
198
200
|
return narrowed;
|
|
199
201
|
}
|
|
200
|
-
const responses = respondedInstead(wrappers);
|
|
201
|
-
const
|
|
202
|
-
const
|
|
202
|
+
const responses = settledUncaught(respondedInstead(wrappers), wrappers);
|
|
203
|
+
const unitOwn = settledUncaught(summary.transitions, wrappers);
|
|
204
|
+
const own = [...responses, ...unitOwn];
|
|
205
|
+
const handled = handledThrows(wrappers, thrownBy(own));
|
|
206
|
+
const transitions = beside(summary, unitOwn, responses, handled);
|
|
203
207
|
if (!keepGaps) {
|
|
204
208
|
return transitions === summary.transitions ? narrowed : { ...narrowed, transitions };
|
|
205
209
|
}
|
|
206
210
|
const gaps = withContractStatusGaps(
|
|
207
211
|
summary,
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
own,
|
|
213
|
+
everyHandlerOutcome(wrappers)
|
|
210
214
|
);
|
|
211
215
|
if (transitions === summary.transitions && gaps === summary.gaps) {
|
|
212
216
|
return narrowed;
|
|
213
217
|
}
|
|
214
218
|
return { ...narrowed, transitions, gaps };
|
|
215
219
|
}
|
|
216
|
-
function beside(summary, responses, handled) {
|
|
217
|
-
|
|
218
|
-
const onThrow = throws ? handled : [];
|
|
219
|
-
if (responses.length === 0 && onThrow.length === 0) {
|
|
220
|
+
function beside(summary, unitOwn, responses, handled) {
|
|
221
|
+
if (responses.length === 0 && handled.length === 0 && unitOwn === summary.transitions) {
|
|
220
222
|
return summary.transitions;
|
|
221
223
|
}
|
|
222
|
-
return withDistinctIds([...responses, ...
|
|
224
|
+
return withDistinctIds([...responses, ...unitOwn, ...handled]);
|
|
225
|
+
}
|
|
226
|
+
function settledUncaught(transitions, wrappers) {
|
|
227
|
+
const handlers = wrappers.filter(
|
|
228
|
+
(wrapper) => wrapper.reference.onThrow === true
|
|
229
|
+
);
|
|
230
|
+
let changed = false;
|
|
231
|
+
const settled = transitions.flatMap((transition) => {
|
|
232
|
+
const { output } = transition;
|
|
233
|
+
if (output.type !== "throw") {
|
|
234
|
+
return [transition];
|
|
235
|
+
}
|
|
236
|
+
const caught = caughtOnRoute(handlers, output);
|
|
237
|
+
if (caught === "sure") {
|
|
238
|
+
changed = true;
|
|
239
|
+
return [markedCaught(transition)];
|
|
240
|
+
}
|
|
241
|
+
if (output.statusWhenUncaught === void 0) {
|
|
242
|
+
return [transition];
|
|
243
|
+
}
|
|
244
|
+
changed = true;
|
|
245
|
+
const response = {
|
|
246
|
+
...transition,
|
|
247
|
+
output: {
|
|
248
|
+
type: "response",
|
|
249
|
+
statusCode: { type: "literal", value: output.statusWhenUncaught },
|
|
250
|
+
body: null,
|
|
251
|
+
headers: {}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
return caught === "uncertain" ? [transition, response] : [response];
|
|
255
|
+
});
|
|
256
|
+
return changed ? settled : transitions;
|
|
257
|
+
}
|
|
258
|
+
function markedCaught(transition) {
|
|
259
|
+
return {
|
|
260
|
+
...transition,
|
|
261
|
+
metadata: withWrapperMetadata(transition.metadata, {
|
|
262
|
+
...readWrapperMetadata2(transition),
|
|
263
|
+
caught: true
|
|
264
|
+
})
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function caughtOnRoute(handlers, thrown) {
|
|
268
|
+
if (handlers.some((handler) => handler.reference.catches === void 0)) {
|
|
269
|
+
return "sure";
|
|
270
|
+
}
|
|
271
|
+
const matches = handlers.map(
|
|
272
|
+
(handler) => catchMatch(handler.reference, thrown)
|
|
273
|
+
);
|
|
274
|
+
if (matches.includes("yes")) {
|
|
275
|
+
return "sure";
|
|
276
|
+
}
|
|
277
|
+
return matches.includes("maybe") ? "uncertain" : "no";
|
|
223
278
|
}
|
|
224
279
|
function respondedInstead(wrappers) {
|
|
225
280
|
return wrappers.flatMap(
|
|
@@ -231,11 +286,91 @@ function respondedInstead(wrappers) {
|
|
|
231
286
|
)
|
|
232
287
|
);
|
|
233
288
|
}
|
|
234
|
-
function
|
|
289
|
+
function thrownBy(transitions) {
|
|
290
|
+
return transitions.flatMap(
|
|
291
|
+
(t) => t.output.type === "throw" ? [t.output] : []
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
function everyHandlerOutcome(wrappers) {
|
|
235
295
|
return wrappers.flatMap(
|
|
236
296
|
(wrapper) => wrapper.reference.onThrow === true ? attribute(wrapper.summary.transitions, wrapper.reference) : []
|
|
237
297
|
);
|
|
238
298
|
}
|
|
299
|
+
function handledThrows(wrappers, thrown) {
|
|
300
|
+
const caught = catchesAny(wrappers, thrown);
|
|
301
|
+
return [...oncePerHandler(wrappers, caught)].flatMap(
|
|
302
|
+
([wrapper, how]) => attribute(
|
|
303
|
+
wrapper.summary.transitions,
|
|
304
|
+
wrapper.reference,
|
|
305
|
+
how === "uncertain"
|
|
306
|
+
)
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
function oncePerHandler(wrappers, caught) {
|
|
310
|
+
const byHandler = /* @__PURE__ */ new Map();
|
|
311
|
+
for (const wrapper of wrappers) {
|
|
312
|
+
const how = caught.get(wrapper);
|
|
313
|
+
if (how === void 0) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const earlier = byHandler.get(wrapper.summary);
|
|
317
|
+
if (earlier === void 0 || earlier[1] === "uncertain" && how === "sure") {
|
|
318
|
+
byHandler.set(wrapper.summary, [wrapper, how]);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return new Map(byHandler.values());
|
|
322
|
+
}
|
|
323
|
+
function catchesAny(wrappers, thrown) {
|
|
324
|
+
const caught = /* @__PURE__ */ new Map();
|
|
325
|
+
if (thrown.length === 0) {
|
|
326
|
+
return caught;
|
|
327
|
+
}
|
|
328
|
+
const handlers = wrappers.filter(
|
|
329
|
+
(wrapper) => wrapper.reference.onThrow === true
|
|
330
|
+
);
|
|
331
|
+
for (const handler of handlers) {
|
|
332
|
+
if (handler.reference.catches === void 0) {
|
|
333
|
+
caught.set(handler, "sure");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const listing = handlers.filter(
|
|
337
|
+
(handler) => handler.reference.catches !== void 0
|
|
338
|
+
);
|
|
339
|
+
for (const one of thrown) {
|
|
340
|
+
for (const [handler, how] of catchersOf(listing, one)) {
|
|
341
|
+
if (caught.get(handler) !== "sure") {
|
|
342
|
+
caught.set(handler, how);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return caught;
|
|
347
|
+
}
|
|
348
|
+
function catchersOf(handlers, thrown) {
|
|
349
|
+
const found = [];
|
|
350
|
+
for (const handler of handlers) {
|
|
351
|
+
const match = catchMatch(handler.reference, thrown);
|
|
352
|
+
if (match === "yes") {
|
|
353
|
+
found.push([handler, found.length === 0 ? "sure" : "uncertain"]);
|
|
354
|
+
return found;
|
|
355
|
+
}
|
|
356
|
+
if (match === "maybe") {
|
|
357
|
+
found.push([handler, "uncertain"]);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return found;
|
|
361
|
+
}
|
|
362
|
+
function catchMatch(reference, thrown) {
|
|
363
|
+
const ancestors = thrown.exceptionAncestors;
|
|
364
|
+
if (thrown.exceptionType === null || ancestors === void 0) {
|
|
365
|
+
return "maybe";
|
|
366
|
+
}
|
|
367
|
+
const classes = /* @__PURE__ */ new Set([thrown.exceptionType, ...ancestors]);
|
|
368
|
+
if ((reference.catches ?? []).some((name) => classes.has(name))) {
|
|
369
|
+
return "yes";
|
|
370
|
+
}
|
|
371
|
+
const unread = thrown.ancestryIncomplete === true && reference.mayCatchUnreadClasses === true;
|
|
372
|
+
return unread || reference.mayCatchAny === true ? "maybe" : "no";
|
|
373
|
+
}
|
|
239
374
|
function withContractStatusGaps(summary, composed, handled) {
|
|
240
375
|
const contract = readHttpMetadata(summary)?.declaredContract;
|
|
241
376
|
const binding = summary.identity.boundaryBinding;
|
|
@@ -266,10 +401,13 @@ function coversUnit(reference, summary) {
|
|
|
266
401
|
const binding = summary.identity.boundaryBinding;
|
|
267
402
|
return binding !== null && withinScope(binding, reference.scope);
|
|
268
403
|
}
|
|
269
|
-
function attribute(transitions, reference) {
|
|
404
|
+
function attribute(transitions, reference, catchUncertain = false) {
|
|
270
405
|
return transitions.map((transition) => ({
|
|
271
406
|
...transition,
|
|
272
|
-
metadata: withWrapperMetadata(transition.metadata, {
|
|
407
|
+
metadata: withWrapperMetadata(transition.metadata, {
|
|
408
|
+
from: reference,
|
|
409
|
+
...catchUncertain ? { catchUncertain } : {}
|
|
410
|
+
})
|
|
273
411
|
}));
|
|
274
412
|
}
|
|
275
413
|
function withDistinctIds(transitions) {
|
|
@@ -281,6 +419,56 @@ function withDistinctIds(transitions) {
|
|
|
281
419
|
});
|
|
282
420
|
}
|
|
283
421
|
|
|
422
|
+
// src/declaredModules.ts
|
|
423
|
+
import fs from "fs";
|
|
424
|
+
import path from "path";
|
|
425
|
+
var MODULE_SURFACE_RECOGNITION = "modules";
|
|
426
|
+
function settleModules(modules, candidates) {
|
|
427
|
+
return (modules ?? []).map((module) => ({
|
|
428
|
+
...module,
|
|
429
|
+
public: module.public ?? candidates.map(
|
|
430
|
+
(one) => path.join(module.root, one.replace("{name}", module.name))
|
|
431
|
+
).filter((file) => fs.existsSync(file))
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
function isInside(file, root) {
|
|
435
|
+
const relative = path.relative(root, file);
|
|
436
|
+
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
437
|
+
}
|
|
438
|
+
function moduleOfFile(modules, file) {
|
|
439
|
+
const byPublicFile = modules.find((module) => module.public.includes(file));
|
|
440
|
+
if (byPublicFile !== void 0) {
|
|
441
|
+
return byPublicFile;
|
|
442
|
+
}
|
|
443
|
+
let deepest;
|
|
444
|
+
for (const module of modules) {
|
|
445
|
+
if (isInside(file, module.root) && (deepest === void 0 || module.root.length > deepest.root.length)) {
|
|
446
|
+
deepest = module;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return deepest;
|
|
450
|
+
}
|
|
451
|
+
function stampModules(summaries, modules, root) {
|
|
452
|
+
if (modules.length === 0) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
for (const summary of summaries) {
|
|
456
|
+
const file = path.resolve(root ?? "", summary.location.file);
|
|
457
|
+
const module = moduleOfFile(modules, file);
|
|
458
|
+
if (module !== void 0) {
|
|
459
|
+
summary.location.module = module.name;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function modulesStamp(modules) {
|
|
464
|
+
if (modules === void 0 || modules.length === 0) {
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
return JSON.stringify(
|
|
468
|
+
modules.map((module) => [module.name, module.root, [...module.public]])
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
284
472
|
// src/effectGuards.ts
|
|
285
473
|
var keyOf = (condition) => `${condition.polarity}:${condition.sourceText}`;
|
|
286
474
|
var opposite = (condition) => `${condition.polarity === "positive" ? "negative" : "positive"}:${condition.sourceText}`;
|
|
@@ -297,6 +485,139 @@ function runsBefore(effectLine, terminalEndLine) {
|
|
|
297
485
|
return effectLine <= terminalEndLine;
|
|
298
486
|
}
|
|
299
487
|
|
|
488
|
+
// src/exitCode.ts
|
|
489
|
+
import path2 from "path";
|
|
490
|
+
import { declarationKey } from "@suss/behavioral-ir";
|
|
491
|
+
function markReturnsAsExitCode(summaries, returnsTheCode, workspaceRoot) {
|
|
492
|
+
for (const summary of summaries) {
|
|
493
|
+
const span = summary.location.span;
|
|
494
|
+
const file = workspaceRoot === void 0 ? summary.location.file : path2.resolve(workspaceRoot, summary.location.file);
|
|
495
|
+
markExitCodeFrom(
|
|
496
|
+
summary,
|
|
497
|
+
span !== void 0 && returnsTheCode.has(declarationKey(file, span))
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function markExitCodeFrom(summary, marked) {
|
|
502
|
+
const process2 = summary.metadata?.process;
|
|
503
|
+
if (marked) {
|
|
504
|
+
summary.metadata = {
|
|
505
|
+
...summary.metadata ?? {},
|
|
506
|
+
process: { ...process2 ?? {}, exitCodeFrom: "return" }
|
|
507
|
+
};
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (process2 === void 0 || !("exitCodeFrom" in process2)) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const { exitCodeFrom: _dropped, ...rest } = process2;
|
|
514
|
+
const { process: _process, ...others } = summary.metadata ?? {};
|
|
515
|
+
const left = Object.keys(rest).length === 0 ? others : { ...others, process: rest };
|
|
516
|
+
if (Object.keys(left).length === 0) {
|
|
517
|
+
Reflect.deleteProperty(summary, "metadata");
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
summary.metadata = left;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/helperHandOff.ts
|
|
524
|
+
import { predicateRefs, replacePredicateRefs } from "@suss/behavioral-ir";
|
|
525
|
+
var MAX_HELPER_PATHS = 16;
|
|
526
|
+
function throughHelper(branch, at, helperBranches) {
|
|
527
|
+
return helperBranches.map((helper) => {
|
|
528
|
+
const conditions = [...branch.conditions, ...helper.conditions];
|
|
529
|
+
const expectedInput = mergeReadShapes(
|
|
530
|
+
branch.expectedInput ?? null,
|
|
531
|
+
helper.expectedInput ?? null
|
|
532
|
+
);
|
|
533
|
+
const reads = expectedInput === null ? {} : { expectedInput };
|
|
534
|
+
if (helper.terminal.kind !== "throw" && helper.terminal.kind !== "exit") {
|
|
535
|
+
return {
|
|
536
|
+
...branch,
|
|
537
|
+
...reads,
|
|
538
|
+
conditions,
|
|
539
|
+
isDefault: branch.isDefault && helper.isDefault
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
...branch,
|
|
544
|
+
...reads,
|
|
545
|
+
conditions,
|
|
546
|
+
terminal: { ...helper.terminal, location: at },
|
|
547
|
+
location: at,
|
|
548
|
+
isDefault: false
|
|
549
|
+
};
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
function mergeReadShapes(a, b) {
|
|
553
|
+
if (a === null || b === null) {
|
|
554
|
+
return a ?? b;
|
|
555
|
+
}
|
|
556
|
+
if (a.type !== "record" || b.type !== "record") {
|
|
557
|
+
return a.type === "record" ? a : b;
|
|
558
|
+
}
|
|
559
|
+
const properties = { ...a.properties };
|
|
560
|
+
for (const [name, shape] of Object.entries(b.properties)) {
|
|
561
|
+
const known = properties[name];
|
|
562
|
+
properties[name] = known === void 0 ? shape : mergeReadShapes(known, shape) ?? shape;
|
|
563
|
+
}
|
|
564
|
+
return { ...a, properties };
|
|
565
|
+
}
|
|
566
|
+
function splitAtHandOffs(branch, sites, from = 0) {
|
|
567
|
+
for (let index = from; index < sites.length; index++) {
|
|
568
|
+
const site = sites[index];
|
|
569
|
+
const reached = site.alwaysRuns === true || runsBefore(site.line, branch.terminal.location.end);
|
|
570
|
+
if (!reached || !guardsHoldOn(site.preconditions, branch.conditions)) {
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
return throughHelper(branch, site.at, site.helper).flatMap(
|
|
574
|
+
(path7) => path7.terminal === branch.terminal ? splitAtHandOffs(path7, sites, index + 1) : [path7]
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return [branch];
|
|
578
|
+
}
|
|
579
|
+
function helperPathsOnVariables(helperBranches, passed) {
|
|
580
|
+
const renamed = helperBranches.map((branch) => ({
|
|
581
|
+
...branch,
|
|
582
|
+
conditions: branch.conditions.map((condition) => ({
|
|
583
|
+
...condition,
|
|
584
|
+
structured: condition.structured === null ? null : onCallerVariables(condition.structured, passed)
|
|
585
|
+
}))
|
|
586
|
+
}));
|
|
587
|
+
const variables = new Set(passed.values());
|
|
588
|
+
const testsWhatItGot = renamed.some(
|
|
589
|
+
(branch) => branch.conditions.some(
|
|
590
|
+
(condition) => condition.structured !== null && predicateRefs(condition.structured).some(
|
|
591
|
+
(ref) => variables.has(rootName(ref) ?? "")
|
|
592
|
+
)
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
return testsWhatItGot && renamed.length <= MAX_HELPER_PATHS ? renamed : null;
|
|
596
|
+
}
|
|
597
|
+
function onCallerVariables(predicate, passed) {
|
|
598
|
+
const rename = (ref) => {
|
|
599
|
+
if (ref.type === "derived") {
|
|
600
|
+
return { ...ref, from: rename(ref.from) };
|
|
601
|
+
}
|
|
602
|
+
if (ref.type === "input") {
|
|
603
|
+
const name = passed.get(ref.inputRef);
|
|
604
|
+
return name === void 0 ? ref : { type: "dependency", name, accessChain: ref.path };
|
|
605
|
+
}
|
|
606
|
+
if (ref.type === "dependency") {
|
|
607
|
+
const name = passed.get(ref.name);
|
|
608
|
+
return name === void 0 ? ref : { ...ref, name };
|
|
609
|
+
}
|
|
610
|
+
return ref;
|
|
611
|
+
};
|
|
612
|
+
return replacePredicateRefs(predicate, rename);
|
|
613
|
+
}
|
|
614
|
+
function rootName(ref) {
|
|
615
|
+
if (ref.type === "derived") {
|
|
616
|
+
return rootName(ref.from);
|
|
617
|
+
}
|
|
618
|
+
return ref.type === "dependency" ? ref.name : null;
|
|
619
|
+
}
|
|
620
|
+
|
|
300
621
|
// src/moduleImports.ts
|
|
301
622
|
function stampModuleImports(summaries, importsOf) {
|
|
302
623
|
const byFile = /* @__PURE__ */ new Map();
|
|
@@ -373,8 +694,8 @@ function httpRouteDiscovery(opts) {
|
|
|
373
694
|
registrationChain: [...opts.methods]
|
|
374
695
|
},
|
|
375
696
|
bindingExtraction: {
|
|
376
|
-
// `.all` registers every method, so it records "*", which
|
|
377
|
-
//
|
|
697
|
+
// `.all` registers every method, so it records "*", which pairs with
|
|
698
|
+
// any method.
|
|
378
699
|
method: {
|
|
379
700
|
type: "fromRegistration",
|
|
380
701
|
position: "methodName",
|
|
@@ -390,10 +711,9 @@ function httpRouteDiscovery(opts) {
|
|
|
390
711
|
}
|
|
391
712
|
return [
|
|
392
713
|
...calls,
|
|
393
|
-
// Routes registered in a loop over an array of specs,
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
// never touches this library's routable is left alone.
|
|
714
|
+
// Routes registered in a loop over an array of specs, which the calls
|
|
715
|
+
// above cannot see. The receiver must come from the same import, so a
|
|
716
|
+
// loop that never touches this library's routable is left alone.
|
|
397
717
|
{
|
|
398
718
|
kind,
|
|
399
719
|
match: {
|
|
@@ -540,7 +860,7 @@ var configuredCallOption = z.object({
|
|
|
540
860
|
receiver: z.string(),
|
|
541
861
|
/** Method that performs the send. */
|
|
542
862
|
method: z.string(),
|
|
543
|
-
/**
|
|
863
|
+
/** Index of the argument that is the subject. */
|
|
544
864
|
subjectArg: z.number(),
|
|
545
865
|
/**
|
|
546
866
|
* Which argument the message body is. Left out when the method has no
|
|
@@ -599,8 +919,8 @@ var catchEntryCond = () => fixedCond(
|
|
|
599
919
|
"positive",
|
|
600
920
|
"catchBlock"
|
|
601
921
|
);
|
|
602
|
-
function classify(
|
|
603
|
-
return
|
|
922
|
+
function classify(path7, home, isAncestorOrSelf) {
|
|
923
|
+
return path7.map((cond) => {
|
|
604
924
|
if (cond.branchStmt === null) {
|
|
605
925
|
return cond.info;
|
|
606
926
|
}
|
|
@@ -665,9 +985,16 @@ function chargeBudget(state) {
|
|
|
665
985
|
);
|
|
666
986
|
}
|
|
667
987
|
}
|
|
668
|
-
function
|
|
988
|
+
function limitOpenPaths(paths) {
|
|
989
|
+
if (paths.length > MAX_PATHS) {
|
|
990
|
+
throw new PathBudgetExceeded(
|
|
991
|
+
`path budget exceeded, more than ${MAX_PATHS} paths`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
function recordTerminal(ctx, terminal, home, path7) {
|
|
669
996
|
chargeBudget(ctx.state);
|
|
670
|
-
const info = classify(
|
|
997
|
+
const info = classify(path7, home, ctx.isAncestorOrSelf);
|
|
671
998
|
const existing = ctx.state.byTerminal.get(terminal) ?? [];
|
|
672
999
|
existing.push(info);
|
|
673
1000
|
ctx.state.byTerminal.set(terminal, existing);
|
|
@@ -678,15 +1005,15 @@ function containsTerminal(ctx, stmt) {
|
|
|
678
1005
|
}
|
|
679
1006
|
return childrenOf(stmt).some((child) => containsTerminal(ctx, child));
|
|
680
1007
|
}
|
|
681
|
-
function stepIf(stmt,
|
|
1008
|
+
function stepIf(stmt, path7, ctx) {
|
|
682
1009
|
const thenExit = exitKindOfList(stmt.thenBody);
|
|
683
1010
|
const elseExit = stmt.elseBody === null ? null : exitKindOfList(stmt.elseBody);
|
|
684
1011
|
const thenPath = [
|
|
685
|
-
...
|
|
1012
|
+
...path7,
|
|
686
1013
|
branchCond(stmt, stmt.condition, "positive", elseExit)
|
|
687
1014
|
];
|
|
688
1015
|
const elsePath = [
|
|
689
|
-
...
|
|
1016
|
+
...path7,
|
|
690
1017
|
branchCond(
|
|
691
1018
|
stmt,
|
|
692
1019
|
stmt.condition,
|
|
@@ -703,7 +1030,7 @@ function stepIf(stmt, path4, ctx) {
|
|
|
703
1030
|
}
|
|
704
1031
|
return out;
|
|
705
1032
|
}
|
|
706
|
-
function stepSwitch(stmt,
|
|
1033
|
+
function stepSwitch(stmt, path7, ctx) {
|
|
707
1034
|
const out = [];
|
|
708
1035
|
const negations = [];
|
|
709
1036
|
let defaultRanBody = false;
|
|
@@ -713,7 +1040,7 @@ function stepSwitch(stmt, path4, ctx) {
|
|
|
713
1040
|
if (hasStrayBreak(group.body)) {
|
|
714
1041
|
throw new UnmodeledFlow("non-trailing break in switch clause");
|
|
715
1042
|
}
|
|
716
|
-
const groupPath = group.condition === null ? [...
|
|
1043
|
+
const groupPath = group.condition === null ? [...path7, ...negations] : [...path7, fixedCond(group.condition, "positive", "explicit")];
|
|
717
1044
|
const conts = enumerate(ctx, group.body, groupPath);
|
|
718
1045
|
if (conts.length > 0) {
|
|
719
1046
|
if (!group.hasTrailingBreak && !fallthroughSafe) {
|
|
@@ -730,24 +1057,24 @@ function stepSwitch(stmt, path4, ctx) {
|
|
|
730
1057
|
}
|
|
731
1058
|
}
|
|
732
1059
|
if (!defaultRanBody) {
|
|
733
|
-
out.push([...
|
|
1060
|
+
out.push([...path7, ...negations]);
|
|
734
1061
|
}
|
|
735
1062
|
return out;
|
|
736
1063
|
}
|
|
737
|
-
function stepLoop(stmt,
|
|
1064
|
+
function stepLoop(stmt, path7, ctx) {
|
|
738
1065
|
const bodyExit = exitKindOfList(stmt.body);
|
|
739
1066
|
if (containsTerminal(ctx, stmt)) {
|
|
740
1067
|
enumerate(ctx, stmt.body, [
|
|
741
|
-
...
|
|
1068
|
+
...path7,
|
|
742
1069
|
loopIterationCond(stmt.condition.sourceText)
|
|
743
1070
|
]);
|
|
744
1071
|
}
|
|
745
1072
|
if (bodyExit !== null) {
|
|
746
|
-
return [[...
|
|
1073
|
+
return [[...path7, loopCompletedCond(stmt.condition.sourceText, bodyExit)]];
|
|
747
1074
|
}
|
|
748
|
-
return [
|
|
1075
|
+
return [path7];
|
|
749
1076
|
}
|
|
750
|
-
function stepTry(stmt,
|
|
1077
|
+
function stepTry(stmt, path7, ctx) {
|
|
751
1078
|
if (stmt.finallyBody !== null) {
|
|
752
1079
|
const finallyHasTerminal = stmt.finallyBody.some(
|
|
753
1080
|
(s) => containsTerminal(ctx, s)
|
|
@@ -757,22 +1084,22 @@ function stepTry(stmt, path4, ctx) {
|
|
|
757
1084
|
}
|
|
758
1085
|
}
|
|
759
1086
|
const out = [];
|
|
760
|
-
out.push(...enumerate(ctx, stmt.tryBody,
|
|
1087
|
+
out.push(...enumerate(ctx, stmt.tryBody, path7));
|
|
761
1088
|
if (stmt.catchBody !== null) {
|
|
762
|
-
const catchPath = [...
|
|
1089
|
+
const catchPath = [...path7, catchEntryCond()];
|
|
763
1090
|
out.push(...enumerate(ctx, stmt.catchBody, catchPath));
|
|
764
1091
|
}
|
|
765
1092
|
return out;
|
|
766
1093
|
}
|
|
767
|
-
function stepExit(stmt,
|
|
1094
|
+
function stepExit(stmt, path7, ctx) {
|
|
768
1095
|
const collector = ctx.state.callbackExits[ctx.state.callbackExits.length - 1];
|
|
769
1096
|
if (collector !== void 0 && (stmt.exit === "return" || stmt.exit === "throw")) {
|
|
770
|
-
collector.push(
|
|
1097
|
+
collector.push(path7);
|
|
771
1098
|
}
|
|
772
1099
|
return [];
|
|
773
1100
|
}
|
|
774
|
-
function stepOpaque(_stmt,
|
|
775
|
-
return [
|
|
1101
|
+
function stepOpaque(_stmt, path7, _ctx) {
|
|
1102
|
+
return [path7];
|
|
776
1103
|
}
|
|
777
1104
|
var stepHandlers = {
|
|
778
1105
|
if: stepIf,
|
|
@@ -782,22 +1109,23 @@ var stepHandlers = {
|
|
|
782
1109
|
exit: stepExit,
|
|
783
1110
|
opaque: stepOpaque
|
|
784
1111
|
};
|
|
785
|
-
function stepCallback(ctx, body,
|
|
1112
|
+
function stepCallback(ctx, body, path7) {
|
|
786
1113
|
const exits = [];
|
|
787
1114
|
ctx.state.callbackExits.push(exits);
|
|
788
1115
|
let fellThrough;
|
|
789
1116
|
try {
|
|
790
|
-
fellThrough = enumerate(ctx, body,
|
|
1117
|
+
fellThrough = enumerate(ctx, body, path7);
|
|
791
1118
|
} finally {
|
|
792
1119
|
ctx.state.callbackExits.pop();
|
|
793
1120
|
}
|
|
794
1121
|
const out = [...fellThrough, ...exits];
|
|
795
|
-
return out.length === 0 ? [
|
|
1122
|
+
return out.length === 0 ? [path7] : out;
|
|
796
1123
|
}
|
|
797
|
-
function stepStatement(ctx, stmt,
|
|
798
|
-
let paths = [
|
|
1124
|
+
function stepStatement(ctx, stmt, path7) {
|
|
1125
|
+
let paths = [path7];
|
|
799
1126
|
for (const body of stmt.callbacks ?? []) {
|
|
800
1127
|
paths = paths.flatMap((each) => stepCallback(ctx, body, each));
|
|
1128
|
+
limitOpenPaths(paths);
|
|
801
1129
|
}
|
|
802
1130
|
const ownTerminals = ctx.terminalsByStmt.get(stmt) ?? [];
|
|
803
1131
|
for (const each of withRejoined(paths)) {
|
|
@@ -814,7 +1142,7 @@ function stepStatement(ctx, stmt, path4) {
|
|
|
814
1142
|
)
|
|
815
1143
|
);
|
|
816
1144
|
}
|
|
817
|
-
function soleDisagreement(left, right) {
|
|
1145
|
+
function soleDisagreement(left, right, keepsArms) {
|
|
818
1146
|
if (left.length !== right.length) {
|
|
819
1147
|
return null;
|
|
820
1148
|
}
|
|
@@ -833,9 +1161,10 @@ function soleDisagreement(left, right) {
|
|
|
833
1161
|
}
|
|
834
1162
|
found = at;
|
|
835
1163
|
}
|
|
836
|
-
|
|
1164
|
+
const test = found === null ? null : left[found]?.info.expression ?? null;
|
|
1165
|
+
return test !== null && keepsArms(test) ? null : found;
|
|
837
1166
|
}
|
|
838
|
-
function mergeRejoined(paths) {
|
|
1167
|
+
function mergeRejoined(paths, keepsArms = KEEPS_NO_ARMS) {
|
|
839
1168
|
let current = paths;
|
|
840
1169
|
for (let round = 0; round < current.length; round++) {
|
|
841
1170
|
const out = [];
|
|
@@ -850,7 +1179,11 @@ function mergeRejoined(paths) {
|
|
|
850
1179
|
if (taken.has(j)) {
|
|
851
1180
|
continue;
|
|
852
1181
|
}
|
|
853
|
-
const at = soleDisagreement(
|
|
1182
|
+
const at = soleDisagreement(
|
|
1183
|
+
combined,
|
|
1184
|
+
current[j],
|
|
1185
|
+
keepsArms
|
|
1186
|
+
);
|
|
854
1187
|
if (at === null) {
|
|
855
1188
|
continue;
|
|
856
1189
|
}
|
|
@@ -876,21 +1209,18 @@ function enumerate(ctx, stmts, prefix) {
|
|
|
876
1209
|
let frontiers = [[...prefix]];
|
|
877
1210
|
for (const stmt of stmts) {
|
|
878
1211
|
const nextFrontiers = [];
|
|
879
|
-
for (const
|
|
880
|
-
nextFrontiers.push(...stepStatement(ctx, stmt,
|
|
1212
|
+
for (const path7 of mergeRejoined(frontiers, ctx.keepsArms)) {
|
|
1213
|
+
nextFrontiers.push(...stepStatement(ctx, stmt, path7));
|
|
881
1214
|
}
|
|
882
1215
|
frontiers = nextFrontiers;
|
|
883
|
-
|
|
884
|
-
throw new PathBudgetExceeded(
|
|
885
|
-
`path budget exceeded, more than ${MAX_PATHS} paths`
|
|
886
|
-
);
|
|
887
|
-
}
|
|
1216
|
+
limitOpenPaths(frontiers);
|
|
888
1217
|
if (frontiers.length === 0) {
|
|
889
1218
|
break;
|
|
890
1219
|
}
|
|
891
1220
|
}
|
|
892
1221
|
return frontiers;
|
|
893
1222
|
}
|
|
1223
|
+
var KEEPS_NO_ARMS = () => false;
|
|
894
1224
|
function buildParentMap(statements) {
|
|
895
1225
|
const parentOf = /* @__PURE__ */ new Map();
|
|
896
1226
|
const visit = (stmt, parent) => {
|
|
@@ -924,13 +1254,14 @@ function enumerateStructuredPaths(input) {
|
|
|
924
1254
|
};
|
|
925
1255
|
const ctx = {
|
|
926
1256
|
terminalsByStmt: input.terminalsByStmt,
|
|
1257
|
+
keepsArms: input.keepsArms ?? KEEPS_NO_ARMS,
|
|
927
1258
|
state,
|
|
928
1259
|
isAncestorOrSelf
|
|
929
1260
|
};
|
|
930
1261
|
const continuations = withRejoined(enumerate(ctx, input.statements, []));
|
|
931
|
-
for (const
|
|
1262
|
+
for (const path7 of continuations) {
|
|
932
1263
|
chargeBudget(state);
|
|
933
|
-
state.fallthrough.push(classify(
|
|
1264
|
+
state.fallthrough.push(classify(path7, null, isAncestorOrSelf));
|
|
934
1265
|
}
|
|
935
1266
|
return { byTerminal: state.byTerminal, fallthrough: state.fallthrough };
|
|
936
1267
|
}
|
|
@@ -973,8 +1304,8 @@ function sharedGatingConditions(paths, readPredicate) {
|
|
|
973
1304
|
return [];
|
|
974
1305
|
}
|
|
975
1306
|
const shared = new Set(first.map(keyOf2));
|
|
976
|
-
for (const
|
|
977
|
-
const here = new Set(
|
|
1307
|
+
for (const path7 of rest) {
|
|
1308
|
+
const here = new Set(path7.map(keyOf2));
|
|
978
1309
|
for (const value of shared) {
|
|
979
1310
|
if (!here.has(value)) {
|
|
980
1311
|
shared.delete(value);
|
|
@@ -1004,7 +1335,7 @@ var IdSet = class {
|
|
|
1004
1335
|
has(node) {
|
|
1005
1336
|
return this.byKey.has(node.id);
|
|
1006
1337
|
}
|
|
1007
|
-
/** The
|
|
1338
|
+
/** The wrapper that was added, which may be a different object from `node`. */
|
|
1008
1339
|
get(node) {
|
|
1009
1340
|
return this.byKey.get(node.id);
|
|
1010
1341
|
}
|
|
@@ -1035,39 +1366,81 @@ var IdMap = class {
|
|
|
1035
1366
|
}
|
|
1036
1367
|
};
|
|
1037
1368
|
|
|
1369
|
+
// src/paths/nodeReads.ts
|
|
1370
|
+
var EnclosingNodes = class {
|
|
1371
|
+
constructor(types) {
|
|
1372
|
+
this.types = types;
|
|
1373
|
+
}
|
|
1374
|
+
types;
|
|
1375
|
+
byTree = /* @__PURE__ */ new WeakMap();
|
|
1376
|
+
/**
|
|
1377
|
+
* The nearest ancestor of one of the types, or null when there is none.
|
|
1378
|
+
* Every node passed on the way up has the same result, so each one is
|
|
1379
|
+
* kept, and a later read from a sibling stops one step up.
|
|
1380
|
+
*/
|
|
1381
|
+
of(node) {
|
|
1382
|
+
const kept = this.keptFor(node.tree);
|
|
1383
|
+
const known = kept.get(node.id);
|
|
1384
|
+
if (known !== void 0) {
|
|
1385
|
+
return known;
|
|
1386
|
+
}
|
|
1387
|
+
const passed = [node.id];
|
|
1388
|
+
let current = node.parent;
|
|
1389
|
+
while (current !== null && !this.types.has(current.type)) {
|
|
1390
|
+
const above = kept.get(current.id);
|
|
1391
|
+
if (above !== void 0) {
|
|
1392
|
+
current = above;
|
|
1393
|
+
break;
|
|
1394
|
+
}
|
|
1395
|
+
passed.push(current.id);
|
|
1396
|
+
current = current.parent;
|
|
1397
|
+
}
|
|
1398
|
+
for (const id of passed) {
|
|
1399
|
+
kept.set(id, current);
|
|
1400
|
+
}
|
|
1401
|
+
return current;
|
|
1402
|
+
}
|
|
1403
|
+
keptFor(tree) {
|
|
1404
|
+
const known = this.byTree.get(tree);
|
|
1405
|
+
if (known !== void 0) {
|
|
1406
|
+
return known;
|
|
1407
|
+
}
|
|
1408
|
+
const fresh = /* @__PURE__ */ new Map();
|
|
1409
|
+
this.byTree.set(tree, fresh);
|
|
1410
|
+
return fresh;
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1038
1414
|
// src/paths/nodeWalk.ts
|
|
1039
1415
|
var SKIP_CHILDREN = /* @__PURE__ */ Symbol("skipChildren");
|
|
1040
|
-
function walkDescendants(root, carried, visitor) {
|
|
1041
|
-
for (const child of root
|
|
1042
|
-
if (child === null) {
|
|
1043
|
-
continue;
|
|
1044
|
-
}
|
|
1416
|
+
function walkDescendants(root, carried, childrenOf2, visitor) {
|
|
1417
|
+
for (const child of childrenOf2(root)) {
|
|
1045
1418
|
visitor.at(child, carried);
|
|
1046
1419
|
const inner = visitor.into(child, carried);
|
|
1047
1420
|
if (inner !== SKIP_CHILDREN) {
|
|
1048
|
-
walkDescendants(child, inner, visitor);
|
|
1421
|
+
walkDescendants(child, inner, childrenOf2, visitor);
|
|
1049
1422
|
}
|
|
1050
1423
|
}
|
|
1051
1424
|
}
|
|
1052
1425
|
|
|
1053
1426
|
// src/projectRoot.ts
|
|
1054
|
-
import
|
|
1427
|
+
import path3 from "path";
|
|
1055
1428
|
function commonDirectoryOf(files) {
|
|
1056
|
-
const absolute = files.filter((f) =>
|
|
1429
|
+
const absolute = files.filter((f) => path3.isAbsolute(f));
|
|
1057
1430
|
const first = absolute[0];
|
|
1058
1431
|
if (first === void 0) {
|
|
1059
1432
|
return void 0;
|
|
1060
1433
|
}
|
|
1061
|
-
let shared =
|
|
1434
|
+
let shared = path3.dirname(first).split(path3.sep);
|
|
1062
1435
|
for (const file of absolute.slice(1)) {
|
|
1063
|
-
const parts =
|
|
1436
|
+
const parts = path3.dirname(file).split(path3.sep);
|
|
1064
1437
|
let i = 0;
|
|
1065
1438
|
while (i < shared.length && i < parts.length && shared[i] === parts[i]) {
|
|
1066
1439
|
i += 1;
|
|
1067
1440
|
}
|
|
1068
1441
|
shared = shared.slice(0, i);
|
|
1069
1442
|
}
|
|
1070
|
-
const joined = shared.join(
|
|
1443
|
+
const joined = shared.join(path3.sep);
|
|
1071
1444
|
return joined.length > 1 ? joined : void 0;
|
|
1072
1445
|
}
|
|
1073
1446
|
|
|
@@ -1124,11 +1497,84 @@ function valueToReadFurtherFrom(reading) {
|
|
|
1124
1497
|
return reading.kind === "written" ? reading.value : null;
|
|
1125
1498
|
}
|
|
1126
1499
|
|
|
1500
|
+
// src/redirectOption.ts
|
|
1501
|
+
function redirectDeliveryWhenSet(option, value) {
|
|
1502
|
+
return option.handsBack.includes(value) ? "response" : "followed";
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// src/testFiles.ts
|
|
1506
|
+
function isListedTestFile(file, listed) {
|
|
1507
|
+
if (listed === void 0) {
|
|
1508
|
+
return true;
|
|
1509
|
+
}
|
|
1510
|
+
const whole = withForwardSlashes(file);
|
|
1511
|
+
return listed.some((entry) => {
|
|
1512
|
+
const tail = withForwardSlashes(entry).replace(/^\.\//, "");
|
|
1513
|
+
return whole === tail || whole.endsWith(`/${tail}`);
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
function matchesTestFileName(file, patterns) {
|
|
1517
|
+
const base = withForwardSlashes(file).split("/").pop() ?? "";
|
|
1518
|
+
return patterns.some((pattern) => globToRegExp(pattern).test(base));
|
|
1519
|
+
}
|
|
1520
|
+
function globToRegExp(pattern) {
|
|
1521
|
+
const escaped = pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
1522
|
+
return new RegExp(`^${escaped}$`);
|
|
1523
|
+
}
|
|
1524
|
+
function withForwardSlashes(file) {
|
|
1525
|
+
return file.replace(/\\/g, "/");
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
// src/valueSources.ts
|
|
1529
|
+
var QUOTED_LENGTH = 80;
|
|
1530
|
+
function sourceRefsOf(leaves, spelling) {
|
|
1531
|
+
const refs = [];
|
|
1532
|
+
const said = /* @__PURE__ */ new Set();
|
|
1533
|
+
for (const leaf of leaves) {
|
|
1534
|
+
const ref = convertedFrom(refOf(leaf, spelling), leaf.conversions);
|
|
1535
|
+
const spelled = JSON.stringify(ref);
|
|
1536
|
+
if (!said.has(spelled)) {
|
|
1537
|
+
said.add(spelled);
|
|
1538
|
+
refs.push(ref);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return refs;
|
|
1542
|
+
}
|
|
1543
|
+
function convertedFrom(ref, conversions) {
|
|
1544
|
+
return conversions.reduceRight(
|
|
1545
|
+
(from, method) => ({
|
|
1546
|
+
type: "derived",
|
|
1547
|
+
from,
|
|
1548
|
+
derivation: { type: "methodCall", method, args: [] }
|
|
1549
|
+
}),
|
|
1550
|
+
ref
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
function refOf(leaf, spelling) {
|
|
1554
|
+
const whole = leaf.computedAt === null;
|
|
1555
|
+
const input = whole ? spelling.inputOf(leaf) : null;
|
|
1556
|
+
if (input !== null) {
|
|
1557
|
+
return input;
|
|
1558
|
+
}
|
|
1559
|
+
if (whole && leaf.end.is === "written" && leaf.path.length === 0) {
|
|
1560
|
+
const literal = spelling.literalAt(leaf.key);
|
|
1561
|
+
if (literal !== void 0) {
|
|
1562
|
+
return { type: "literal", value: literal };
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
const at = leaf.computedAt ?? leaf.key;
|
|
1566
|
+
return { type: "unresolved", sourceText: quoted(spelling.textAt(at), at) };
|
|
1567
|
+
}
|
|
1568
|
+
function quoted(text, key) {
|
|
1569
|
+
const written = (text ?? key.slice(key.lastIndexOf("#") + 1)).replace(/\s+/g, " ").trim();
|
|
1570
|
+
return written.length <= QUOTED_LENGTH ? written : `${written.slice(0, QUOTED_LENGTH - 3)}...`;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1127
1573
|
// src/adapterStamp.ts
|
|
1128
1574
|
import { createHash } from "crypto";
|
|
1129
|
-
import
|
|
1575
|
+
import fs2 from "fs";
|
|
1130
1576
|
import { createRequire } from "module";
|
|
1131
|
-
import
|
|
1577
|
+
import path4 from "path";
|
|
1132
1578
|
import { fileURLToPath } from "url";
|
|
1133
1579
|
var ANALYSIS_PACKAGES = [
|
|
1134
1580
|
"@suss/extractor",
|
|
@@ -1170,16 +1616,16 @@ function createAdapterStamp(config) {
|
|
|
1170
1616
|
function readAdapterCodeStamp(moduleUrl) {
|
|
1171
1617
|
try {
|
|
1172
1618
|
const selfPath = fileURLToPath(moduleUrl);
|
|
1173
|
-
const hash = computeDistHashFrom(
|
|
1619
|
+
const hash = computeDistHashFrom(path4.dirname(selfPath));
|
|
1174
1620
|
return hash.length > 0 ? { kind: "bundle", hash } : SOURCE_STAMP;
|
|
1175
1621
|
} catch {
|
|
1176
1622
|
return SOURCE_STAMP;
|
|
1177
1623
|
}
|
|
1178
1624
|
}
|
|
1179
1625
|
function computeDistHashFrom(dir) {
|
|
1180
|
-
const candidates = [
|
|
1626
|
+
const candidates = [path4.join(dir, "index.js"), path4.join(dir, "index.cjs")];
|
|
1181
1627
|
for (const candidate of candidates) {
|
|
1182
|
-
if (!
|
|
1628
|
+
if (!fs2.existsSync(candidate)) {
|
|
1183
1629
|
continue;
|
|
1184
1630
|
}
|
|
1185
1631
|
return computeContentHash([candidate, ...analysisBundles()]);
|
|
@@ -1193,7 +1639,7 @@ function computeContentHash(paths) {
|
|
|
1193
1639
|
const hash = createHash("sha256");
|
|
1194
1640
|
for (const file of paths) {
|
|
1195
1641
|
try {
|
|
1196
|
-
hash.update(
|
|
1642
|
+
hash.update(fs2.readFileSync(file));
|
|
1197
1643
|
} catch {
|
|
1198
1644
|
return "";
|
|
1199
1645
|
}
|
|
@@ -1209,13 +1655,30 @@ function projectFileStamp(paths) {
|
|
|
1209
1655
|
hash.update(file);
|
|
1210
1656
|
hash.update("\0");
|
|
1211
1657
|
try {
|
|
1212
|
-
hash.update(
|
|
1658
|
+
hash.update(fs2.readFileSync(file));
|
|
1213
1659
|
} catch {
|
|
1214
1660
|
hash.update("absent");
|
|
1215
1661
|
}
|
|
1216
1662
|
}
|
|
1217
1663
|
return hash.digest("hex").slice(0, 16);
|
|
1218
1664
|
}
|
|
1665
|
+
function extractionConfigStamp(config) {
|
|
1666
|
+
const parts = [
|
|
1667
|
+
["gapHandling", config.gapHandling ?? "permissive"],
|
|
1668
|
+
["includeReachable", optionalText(config.includeReachable)],
|
|
1669
|
+
["workspaceRoot", config.workspaceRoot],
|
|
1670
|
+
["projectRoot", config.projectRoot],
|
|
1671
|
+
["importRoots", config.importRoots?.join(path4.delimiter)],
|
|
1672
|
+
["modules", modulesStamp(config.modules)],
|
|
1673
|
+
["walkedDirectory", config.walkedDirectory]
|
|
1674
|
+
];
|
|
1675
|
+
return parts.flatMap(
|
|
1676
|
+
([name, value]) => value === void 0 ? [] : [`${name}=${value}`]
|
|
1677
|
+
).join(",");
|
|
1678
|
+
}
|
|
1679
|
+
function optionalText(value) {
|
|
1680
|
+
return value === void 0 ? void 0 : String(value);
|
|
1681
|
+
}
|
|
1219
1682
|
function runDigest(packsDigest, packs, files) {
|
|
1220
1683
|
const inputs = packs.flatMap((pack) => pack.discoveryInputs?.(files) ?? []);
|
|
1221
1684
|
return `${packsDigest}|reads:${projectFileStamp(inputs)}`;
|
|
@@ -1233,10 +1696,130 @@ function analysisBundles() {
|
|
|
1233
1696
|
}
|
|
1234
1697
|
|
|
1235
1698
|
// src/cache.ts
|
|
1699
|
+
import { createHash as createHash3 } from "crypto";
|
|
1700
|
+
import fs5 from "fs/promises";
|
|
1701
|
+
import path6 from "path";
|
|
1702
|
+
import { readJsonFile, writeDocumentFile } from "@suss/behavioral-ir/files";
|
|
1703
|
+
|
|
1704
|
+
// src/cacheStamps.ts
|
|
1236
1705
|
import { createHash as createHash2 } from "crypto";
|
|
1237
|
-
import
|
|
1238
|
-
|
|
1239
|
-
|
|
1706
|
+
import fs3 from "fs/promises";
|
|
1707
|
+
var RECENT_CHANGE_MS = 2e3;
|
|
1708
|
+
function stampMayMissAWrite(stat, statAt) {
|
|
1709
|
+
return Math.max(stat.mtimeMs, stat.ctimeMs) > statAt - RECENT_CHANGE_MS;
|
|
1710
|
+
}
|
|
1711
|
+
async function startRun(files, configPath) {
|
|
1712
|
+
const startedAt = Date.now();
|
|
1713
|
+
const config = configPath === void 0 ? null : await statAtStart(configPath);
|
|
1714
|
+
const stats = await Promise.all(files.map(statAtStart));
|
|
1715
|
+
stats.sort(
|
|
1716
|
+
(a, b) => a.stamp.path < b.stamp.path ? -1 : a.stamp.path > b.stamp.path ? 1 : 0
|
|
1717
|
+
);
|
|
1718
|
+
return {
|
|
1719
|
+
startedAt,
|
|
1720
|
+
config: config?.ctimeMs === null ? null : config,
|
|
1721
|
+
files: stats,
|
|
1722
|
+
hashes: /* @__PURE__ */ new Map()
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
async function statAtStart(filePath) {
|
|
1726
|
+
try {
|
|
1727
|
+
const stat = await fs3.stat(filePath);
|
|
1728
|
+
return {
|
|
1729
|
+
stamp: { path: filePath, mtimeMs: stat.mtimeMs, size: stat.size },
|
|
1730
|
+
ctimeMs: stat.ctimeMs
|
|
1731
|
+
};
|
|
1732
|
+
} catch {
|
|
1733
|
+
return { stamp: { path: filePath, mtimeMs: -1, size: -1 }, ctimeMs: null };
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
async function hashOf(run, filePath) {
|
|
1737
|
+
const known = run.hashes.get(filePath);
|
|
1738
|
+
if (known !== void 0) {
|
|
1739
|
+
return known;
|
|
1740
|
+
}
|
|
1741
|
+
const hash = await hashFile(filePath);
|
|
1742
|
+
run.hashes.set(filePath, hash);
|
|
1743
|
+
return hash;
|
|
1744
|
+
}
|
|
1745
|
+
async function hashRecentChanges(run) {
|
|
1746
|
+
const recent = [...run.config === null ? [] : [run.config], ...run.files].filter(
|
|
1747
|
+
(file) => file.ctimeMs !== null && stampMayMissAWrite(
|
|
1748
|
+
{ mtimeMs: file.stamp.mtimeMs, ctimeMs: file.ctimeMs },
|
|
1749
|
+
run.startedAt
|
|
1750
|
+
)
|
|
1751
|
+
).map((file) => file.stamp.path);
|
|
1752
|
+
await Promise.all(recent.map((filePath) => hashOf(run, filePath)));
|
|
1753
|
+
}
|
|
1754
|
+
async function stillMatches(stored, found, run) {
|
|
1755
|
+
if (!fileStampEquals(stored, found)) {
|
|
1756
|
+
return false;
|
|
1757
|
+
}
|
|
1758
|
+
if (stored === null || stored.verifyHash !== true) {
|
|
1759
|
+
return true;
|
|
1760
|
+
}
|
|
1761
|
+
return await hashOf(run, stored.path) === stored.contentHash;
|
|
1762
|
+
}
|
|
1763
|
+
async function filesStillMatch(stored, run) {
|
|
1764
|
+
if (stored.length !== run.files.length || !stored.every((s, i) => fileStampEquals(s, run.files[i]?.stamp ?? null))) {
|
|
1765
|
+
return false;
|
|
1766
|
+
}
|
|
1767
|
+
const checks = await Promise.all(
|
|
1768
|
+
stored.map((s, i) => stillMatches(s, run.files[i]?.stamp ?? null, run))
|
|
1769
|
+
);
|
|
1770
|
+
return checks.every(Boolean);
|
|
1771
|
+
}
|
|
1772
|
+
async function recordedStamp(start, run, prior) {
|
|
1773
|
+
if (start.ctimeMs === null) {
|
|
1774
|
+
return start.stamp;
|
|
1775
|
+
}
|
|
1776
|
+
const filePath = start.stamp.path;
|
|
1777
|
+
const now = await fs3.stat(filePath).catch(() => null);
|
|
1778
|
+
if (now === null || now.mtimeMs !== start.stamp.mtimeMs || now.size !== start.stamp.size || now.ctimeMs !== start.ctimeMs) {
|
|
1779
|
+
return null;
|
|
1780
|
+
}
|
|
1781
|
+
const atStart = { mtimeMs: start.stamp.mtimeMs, ctimeMs: start.ctimeMs };
|
|
1782
|
+
if (stampMayMissAWrite(atStart, run.startedAt)) {
|
|
1783
|
+
const readBefore = run.hashes.get(filePath);
|
|
1784
|
+
return readBefore === void 0 || readBefore === null ? null : { ...start.stamp, contentHash: readBefore, verifyHash: true };
|
|
1785
|
+
}
|
|
1786
|
+
const hash = run.hashes.get(filePath) ?? reusableHash(prior, start.stamp) ?? await hashFile(filePath);
|
|
1787
|
+
return hash === null ? start.stamp : { ...start.stamp, contentHash: hash };
|
|
1788
|
+
}
|
|
1789
|
+
function reusableHash(prior, stamp) {
|
|
1790
|
+
return prior !== void 0 && prior.verifyHash !== true && fileStampEquals(prior, stamp) ? prior.contentHash : void 0;
|
|
1791
|
+
}
|
|
1792
|
+
async function hashFile(filePath) {
|
|
1793
|
+
try {
|
|
1794
|
+
const content = await fs3.readFile(filePath);
|
|
1795
|
+
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
1796
|
+
} catch {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function fileStampEquals(a, b) {
|
|
1801
|
+
if (a === null && b === null) {
|
|
1802
|
+
return true;
|
|
1803
|
+
}
|
|
1804
|
+
if (a === null || b === null) {
|
|
1805
|
+
return false;
|
|
1806
|
+
}
|
|
1807
|
+
return a.path === b.path && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/ignoredDirectory.ts
|
|
1811
|
+
import fs4 from "fs";
|
|
1812
|
+
import path5 from "path";
|
|
1813
|
+
function makeIgnoredDirectory(dir) {
|
|
1814
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
1815
|
+
const ignore = path5.join(dir, ".gitignore");
|
|
1816
|
+
if (!fs4.existsSync(ignore)) {
|
|
1817
|
+
fs4.writeFileSync(ignore, "*\n");
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// src/cache.ts
|
|
1822
|
+
var SCHEMA_VERSION = "9";
|
|
1240
1823
|
var MAX_ENTRIES = 2;
|
|
1241
1824
|
var ENTRY_PREFIX = "key-";
|
|
1242
1825
|
var ENTRY_DIR_NAME = new RegExp(`^${ENTRY_PREFIX}[0-9a-f]{16}$`);
|
|
@@ -1253,56 +1836,88 @@ function createCacheLayer(cacheDir) {
|
|
|
1253
1836
|
}
|
|
1254
1837
|
};
|
|
1255
1838
|
}
|
|
1839
|
+
const manifests = new ManifestReads();
|
|
1840
|
+
const runs = /* @__PURE__ */ new WeakMap();
|
|
1841
|
+
const runFor = async (input) => runs.get(input) ?? await startRun(input.files, input.configPath);
|
|
1842
|
+
const lookupWith = async (input, run) => {
|
|
1843
|
+
const entryDir = entryDirFor(cacheDir, input);
|
|
1844
|
+
const manifest = await manifests.readAtRunStart(
|
|
1845
|
+
path6.join(entryDir, "manifest.json")
|
|
1846
|
+
);
|
|
1847
|
+
if (manifest === null) {
|
|
1848
|
+
return missDiag(await describeAbsentEntry(cacheDir));
|
|
1849
|
+
}
|
|
1850
|
+
if (!await stillMatches(
|
|
1851
|
+
manifest.configStamp,
|
|
1852
|
+
run.config?.stamp ?? null,
|
|
1853
|
+
run
|
|
1854
|
+
)) {
|
|
1855
|
+
return missDiag("config-changed");
|
|
1856
|
+
}
|
|
1857
|
+
if (!await filesStillMatch(manifest.files, run)) {
|
|
1858
|
+
return missDiag("files-changed");
|
|
1859
|
+
}
|
|
1860
|
+
await markUsed(entryDir);
|
|
1861
|
+
manifests.forget();
|
|
1862
|
+
return {
|
|
1863
|
+
kind: "hit",
|
|
1864
|
+
summaries: manifest.summaries,
|
|
1865
|
+
diagnostic: { kind: "hit" }
|
|
1866
|
+
};
|
|
1867
|
+
};
|
|
1256
1868
|
return {
|
|
1257
1869
|
async tryHit(input) {
|
|
1258
1870
|
const result = await this.lookup(input);
|
|
1259
1871
|
return result.kind === "hit" ? result.summaries : null;
|
|
1260
1872
|
},
|
|
1261
1873
|
async lookup(input) {
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
)
|
|
1266
|
-
|
|
1267
|
-
return missDiag(await describeAbsentEntry(cacheDir));
|
|
1268
|
-
}
|
|
1269
|
-
const currentConfigStamp = await stampConfigFile(input.configPath);
|
|
1270
|
-
if (!fileStampEquals(manifest.configStamp, currentConfigStamp)) {
|
|
1271
|
-
return missDiag("config-changed");
|
|
1874
|
+
const run = await startRun(input.files, input.configPath);
|
|
1875
|
+
runs.set(input, run);
|
|
1876
|
+
const result = await lookupWith(input, run);
|
|
1877
|
+
if (result.kind === "miss") {
|
|
1878
|
+
await hashRecentChanges(run);
|
|
1272
1879
|
}
|
|
1273
|
-
|
|
1274
|
-
if (!fileStampsEqual(manifest.files, currentFiles)) {
|
|
1275
|
-
return missDiag("files-changed");
|
|
1276
|
-
}
|
|
1277
|
-
await markUsed(entryDir);
|
|
1278
|
-
return {
|
|
1279
|
-
kind: "hit",
|
|
1280
|
-
summaries: manifest.summaries,
|
|
1281
|
-
diagnostic: { kind: "hit" }
|
|
1282
|
-
};
|
|
1880
|
+
return result;
|
|
1283
1881
|
},
|
|
1284
1882
|
async plan(input) {
|
|
1285
1883
|
const entryDir = entryDirFor(cacheDir, input);
|
|
1286
|
-
const manifest = await
|
|
1287
|
-
|
|
1884
|
+
const manifest = await manifests.readAgain(
|
|
1885
|
+
path6.join(entryDir, "manifest.json")
|
|
1288
1886
|
);
|
|
1289
1887
|
if (manifest === null || manifest.roots === void 0 || manifest.rootMeta === void 0 || manifest.owners === void 0) {
|
|
1290
1888
|
return null;
|
|
1291
1889
|
}
|
|
1292
|
-
const
|
|
1293
|
-
if (!
|
|
1890
|
+
const run = await runFor(input);
|
|
1891
|
+
if (!await stillMatches(
|
|
1892
|
+
manifest.configStamp,
|
|
1893
|
+
run.config?.stamp ?? null,
|
|
1894
|
+
run
|
|
1895
|
+
)) {
|
|
1294
1896
|
return null;
|
|
1295
1897
|
}
|
|
1296
1898
|
await markUsed(entryDir);
|
|
1297
|
-
return buildPlan(manifest,
|
|
1899
|
+
return buildPlan(manifest, run);
|
|
1298
1900
|
},
|
|
1299
1901
|
async write(input, summaries, attribution) {
|
|
1300
1902
|
const entryDir = entryDirFor(cacheDir, input);
|
|
1301
|
-
const previous = await
|
|
1302
|
-
|
|
1903
|
+
const previous = await manifests.readAgain(
|
|
1904
|
+
path6.join(entryDir, "manifest.json")
|
|
1303
1905
|
);
|
|
1304
|
-
|
|
1305
|
-
const
|
|
1906
|
+
manifests.forget();
|
|
1907
|
+
const known = runs.get(input);
|
|
1908
|
+
const run = known ?? await runFor(input);
|
|
1909
|
+
if (known === void 0) {
|
|
1910
|
+
await hashRecentChanges(run);
|
|
1911
|
+
}
|
|
1912
|
+
runs.delete(input);
|
|
1913
|
+
const configStamp = run.config === null ? null : await recordedStamp(run.config, run, void 0);
|
|
1914
|
+
const prior = new Map((previous?.files ?? []).map((f) => [f.path, f]));
|
|
1915
|
+
const recorded = await Promise.all(
|
|
1916
|
+
run.files.map(
|
|
1917
|
+
(file) => recordedStamp(file, run, prior.get(file.stamp.path))
|
|
1918
|
+
)
|
|
1919
|
+
);
|
|
1920
|
+
const files = recorded.filter((stamp) => stamp !== null);
|
|
1306
1921
|
const manifest = {
|
|
1307
1922
|
schemaVersion: SCHEMA_VERSION,
|
|
1308
1923
|
adapterPacksDigest: input.adapterPacksDigest,
|
|
@@ -1311,19 +1926,17 @@ function createCacheLayer(cacheDir) {
|
|
|
1311
1926
|
summaries,
|
|
1312
1927
|
...attribution === void 0 ? {} : encodeAttribution(attribution)
|
|
1313
1928
|
};
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
path3.join(entryDir, "manifest.json"),
|
|
1317
|
-
JSON.stringify(manifest)
|
|
1318
|
-
);
|
|
1929
|
+
makeIgnoredDirectory(cacheDir);
|
|
1930
|
+
writeDocumentFile(path6.join(entryDir, "manifest.json"), manifest);
|
|
1319
1931
|
await evictOldEntries(cacheDir, entryDir);
|
|
1320
1932
|
}
|
|
1321
1933
|
};
|
|
1322
1934
|
}
|
|
1323
|
-
async function buildPlan(manifest,
|
|
1935
|
+
async function buildPlan(manifest, run) {
|
|
1324
1936
|
const stored = new Map(manifest.files.map((f) => [f.path, f]));
|
|
1325
|
-
const current = new Map(
|
|
1937
|
+
const current = new Map(run.files.map((f) => [f.stamp.path, f.stamp]));
|
|
1326
1938
|
const changed = /* @__PURE__ */ new Set();
|
|
1939
|
+
const added = /* @__PURE__ */ new Set();
|
|
1327
1940
|
const removed = /* @__PURE__ */ new Set();
|
|
1328
1941
|
for (const p of stored.keys()) {
|
|
1329
1942
|
if (!current.has(p)) {
|
|
@@ -1335,17 +1948,20 @@ async function buildPlan(manifest, currentStamps) {
|
|
|
1335
1948
|
const before = stored.get(p);
|
|
1336
1949
|
if (before === void 0) {
|
|
1337
1950
|
changed.add(p);
|
|
1951
|
+
added.add(p);
|
|
1338
1952
|
} else if (!fileStampEquals(before, stamp)) {
|
|
1339
1953
|
if (before.contentHash === void 0) {
|
|
1340
1954
|
changed.add(p);
|
|
1341
1955
|
} else {
|
|
1342
1956
|
toVerify.push(p);
|
|
1343
1957
|
}
|
|
1958
|
+
} else if (before.verifyHash === true) {
|
|
1959
|
+
toVerify.push(p);
|
|
1344
1960
|
}
|
|
1345
1961
|
}
|
|
1346
1962
|
await Promise.all(
|
|
1347
1963
|
toVerify.map(async (p) => {
|
|
1348
|
-
const hash = await
|
|
1964
|
+
const hash = await hashOf(run, p);
|
|
1349
1965
|
if (hash === null || hash !== stored.get(p)?.contentHash) {
|
|
1350
1966
|
changed.add(p);
|
|
1351
1967
|
}
|
|
@@ -1354,6 +1970,10 @@ async function buildPlan(manifest, currentStamps) {
|
|
|
1354
1970
|
const roots = /* @__PURE__ */ new Map();
|
|
1355
1971
|
const rootNames = manifest.roots ?? [];
|
|
1356
1972
|
const depPaths = manifest.depPaths ?? [];
|
|
1973
|
+
const pathsOf = (ids) => ids.flatMap((d) => {
|
|
1974
|
+
const p = depPaths[d];
|
|
1975
|
+
return p === void 0 ? [] : [p];
|
|
1976
|
+
});
|
|
1357
1977
|
let rootsDeclined = 0;
|
|
1358
1978
|
rootNames.forEach((rootPath, i) => {
|
|
1359
1979
|
const meta = manifest.rootMeta?.[i];
|
|
@@ -1366,35 +1986,54 @@ async function buildPlan(manifest, currentStamps) {
|
|
|
1366
1986
|
roots.set(rootPath, {
|
|
1367
1987
|
path: rootPath,
|
|
1368
1988
|
cacheable: meta.cacheable,
|
|
1369
|
-
deps: meta.deps
|
|
1370
|
-
const p = depPaths[d];
|
|
1371
|
-
return p === void 0 ? [] : [p];
|
|
1372
|
-
}),
|
|
1989
|
+
deps: pathsOf(meta.deps),
|
|
1373
1990
|
claims: meta.claims,
|
|
1374
1991
|
meta: meta.meta,
|
|
1375
|
-
packs: meta.packs
|
|
1992
|
+
packs: meta.packs,
|
|
1993
|
+
...meta.imports === void 0 ? {} : { imports: pathsOf(meta.imports) }
|
|
1376
1994
|
});
|
|
1377
1995
|
});
|
|
1996
|
+
const unchanged = (filePath, deps) => current.has(filePath) && !changed.has(filePath) && !deps.some((d) => changed.has(d) || removed.has(d));
|
|
1378
1997
|
const validRoots = /* @__PURE__ */ new Set();
|
|
1379
1998
|
for (const [rootPath, record] of roots) {
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1999
|
+
if (record.cacheable && unchanged(rootPath, record.deps)) {
|
|
2000
|
+
validRoots.add(rootPath);
|
|
1382
2001
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
2002
|
+
}
|
|
2003
|
+
const units = (manifest.units ?? []).flatMap((stored2) => {
|
|
2004
|
+
const file = depPaths[stored2.file];
|
|
2005
|
+
return file === void 0 ? [] : [
|
|
2006
|
+
{
|
|
2007
|
+
key: stored2.key,
|
|
2008
|
+
file,
|
|
2009
|
+
deps: pathsOf(stored2.deps),
|
|
2010
|
+
data: stored2.data
|
|
2011
|
+
}
|
|
2012
|
+
];
|
|
2013
|
+
});
|
|
2014
|
+
const validUnits = /* @__PURE__ */ new Map();
|
|
2015
|
+
for (const unit of units) {
|
|
2016
|
+
if (unchanged(unit.file, unit.deps)) {
|
|
2017
|
+
validUnits.set(unit.key, unit);
|
|
1385
2018
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
2019
|
+
}
|
|
2020
|
+
const resolvedImports = /* @__PURE__ */ new Map();
|
|
2021
|
+
if (added.size === 0 && removed.size === 0) {
|
|
2022
|
+
for (const [rootPath, record] of roots) {
|
|
2023
|
+
if (record.imports !== void 0 && !changed.has(rootPath)) {
|
|
2024
|
+
resolvedImports.set(rootPath, record.imports);
|
|
2025
|
+
}
|
|
1389
2026
|
}
|
|
1390
2027
|
}
|
|
1391
2028
|
const owners = manifest.owners ?? [];
|
|
1392
2029
|
return {
|
|
1393
2030
|
changed,
|
|
1394
2031
|
removed,
|
|
2032
|
+
resolvedImports,
|
|
1395
2033
|
roots,
|
|
1396
2034
|
validRoots,
|
|
1397
2035
|
rootsDeclined,
|
|
2036
|
+
validUnits,
|
|
1398
2037
|
reuse(valid) {
|
|
1399
2038
|
const summaries = [];
|
|
1400
2039
|
const reusedOwners = [];
|
|
@@ -1421,7 +2060,8 @@ async function buildPlan(manifest, currentStamps) {
|
|
|
1421
2060
|
const p = rootNames[o];
|
|
1422
2061
|
return p === void 0 ? [] : [p];
|
|
1423
2062
|
})
|
|
1424
|
-
)
|
|
2063
|
+
),
|
|
2064
|
+
units
|
|
1425
2065
|
};
|
|
1426
2066
|
}
|
|
1427
2067
|
};
|
|
@@ -1446,7 +2086,8 @@ function encodeAttribution(attribution) {
|
|
|
1446
2086
|
deps: r.deps.map(depIdOf),
|
|
1447
2087
|
claims: r.claims,
|
|
1448
2088
|
meta: r.meta,
|
|
1449
|
-
packs: r.packs
|
|
2089
|
+
packs: r.packs,
|
|
2090
|
+
...r.imports === void 0 ? {} : { imports: r.imports.map(depIdOf) }
|
|
1450
2091
|
}));
|
|
1451
2092
|
const owners = attribution.owners.map(
|
|
1452
2093
|
(ownerPaths) => ownerPaths.flatMap((p) => {
|
|
@@ -1454,32 +2095,23 @@ function encodeAttribution(attribution) {
|
|
|
1454
2095
|
return i === void 0 ? [] : [i];
|
|
1455
2096
|
})
|
|
1456
2097
|
);
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
);
|
|
1471
|
-
}
|
|
1472
|
-
async function hashFile(filePath) {
|
|
1473
|
-
try {
|
|
1474
|
-
const content = await fs2.readFile(filePath);
|
|
1475
|
-
return createHash2("sha256").update(content).digest("hex").slice(0, 16);
|
|
1476
|
-
} catch {
|
|
1477
|
-
return null;
|
|
1478
|
-
}
|
|
2098
|
+
const units = attribution.units?.map((unit) => ({
|
|
2099
|
+
key: unit.key,
|
|
2100
|
+
file: depIdOf(unit.file),
|
|
2101
|
+
deps: unit.deps.map(depIdOf),
|
|
2102
|
+
data: unit.data
|
|
2103
|
+
}));
|
|
2104
|
+
return {
|
|
2105
|
+
roots,
|
|
2106
|
+
rootMeta,
|
|
2107
|
+
depPaths,
|
|
2108
|
+
owners,
|
|
2109
|
+
...units === void 0 ? {} : { units }
|
|
2110
|
+
};
|
|
1479
2111
|
}
|
|
1480
2112
|
async function describeAbsentEntry(cacheDir) {
|
|
1481
2113
|
try {
|
|
1482
|
-
const entries = await
|
|
2114
|
+
const entries = await fs5.readdir(cacheDir, { withFileTypes: true });
|
|
1483
2115
|
return entries.some((e) => e.isDirectory() && isEntryDir(e.name)) ? "key-changed" : "no-manifest";
|
|
1484
2116
|
} catch {
|
|
1485
2117
|
return "no-manifest";
|
|
@@ -1491,8 +2123,8 @@ function entryDirFor(cacheDir, input) {
|
|
|
1491
2123
|
input.adapterPacksDigest,
|
|
1492
2124
|
input.configPath ?? ""
|
|
1493
2125
|
].join(" ");
|
|
1494
|
-
const name =
|
|
1495
|
-
return
|
|
2126
|
+
const name = createHash3("sha256").update(key).digest("hex").slice(0, 16);
|
|
2127
|
+
return path6.join(cacheDir, `${ENTRY_PREFIX}${name}`);
|
|
1496
2128
|
}
|
|
1497
2129
|
function isEntryDir(name) {
|
|
1498
2130
|
return ENTRY_DIR_NAME.test(name);
|
|
@@ -1500,24 +2132,24 @@ function isEntryDir(name) {
|
|
|
1500
2132
|
async function markUsed(entryDir) {
|
|
1501
2133
|
const now = /* @__PURE__ */ new Date();
|
|
1502
2134
|
try {
|
|
1503
|
-
await
|
|
2135
|
+
await fs5.utimes(entryDir, now, now);
|
|
1504
2136
|
} catch {
|
|
1505
2137
|
}
|
|
1506
2138
|
}
|
|
1507
2139
|
async function evictOldEntries(cacheDir, keepDir) {
|
|
1508
2140
|
try {
|
|
1509
|
-
await
|
|
1510
|
-
const entries = await
|
|
2141
|
+
await fs5.rm(path6.join(cacheDir, "manifest.json"), { force: true });
|
|
2142
|
+
const entries = await fs5.readdir(cacheDir, { withFileTypes: true });
|
|
1511
2143
|
const dirs = await Promise.all(
|
|
1512
2144
|
entries.filter((e) => e.isDirectory() && isEntryDir(e.name)).map(async (e) => {
|
|
1513
|
-
const dir =
|
|
1514
|
-
const stat = await
|
|
2145
|
+
const dir = path6.join(cacheDir, e.name);
|
|
2146
|
+
const stat = await fs5.stat(dir);
|
|
1515
2147
|
return { dir, mtimeMs: stat.mtimeMs };
|
|
1516
2148
|
})
|
|
1517
2149
|
);
|
|
1518
2150
|
const doomed = dirs.filter((d) => d.dir !== keepDir).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(MAX_ENTRIES - 1);
|
|
1519
2151
|
await Promise.all(
|
|
1520
|
-
doomed.map((d) =>
|
|
2152
|
+
doomed.map((d) => fs5.rm(d.dir, { recursive: true, force: true }))
|
|
1521
2153
|
);
|
|
1522
2154
|
} catch {
|
|
1523
2155
|
}
|
|
@@ -1528,51 +2160,40 @@ function missDiag(reason) {
|
|
|
1528
2160
|
diagnostic: { kind: "miss", missReason: reason }
|
|
1529
2161
|
};
|
|
1530
2162
|
}
|
|
1531
|
-
|
|
2163
|
+
var ManifestReads = class {
|
|
2164
|
+
last = null;
|
|
2165
|
+
async readAtRunStart(manifestPath) {
|
|
2166
|
+
const stamp = await manifestStamp(manifestPath);
|
|
2167
|
+
const manifest = await readManifest(manifestPath);
|
|
2168
|
+
this.last = { path: manifestPath, stamp, manifest };
|
|
2169
|
+
return manifest;
|
|
2170
|
+
}
|
|
2171
|
+
async readAgain(manifestPath) {
|
|
2172
|
+
const last = this.last;
|
|
2173
|
+
if (last !== null && last.path === manifestPath && last.stamp !== null && last.stamp === await manifestStamp(manifestPath)) {
|
|
2174
|
+
return last.manifest;
|
|
2175
|
+
}
|
|
2176
|
+
return await this.readAtRunStart(manifestPath);
|
|
2177
|
+
}
|
|
2178
|
+
forget() {
|
|
2179
|
+
this.last = null;
|
|
2180
|
+
}
|
|
2181
|
+
};
|
|
2182
|
+
async function manifestStamp(manifestPath) {
|
|
1532
2183
|
try {
|
|
1533
|
-
const
|
|
1534
|
-
return
|
|
2184
|
+
const stat = await fs5.stat(manifestPath);
|
|
2185
|
+
return `${stat.ino}:${stat.mtimeMs}:${stat.size}`;
|
|
1535
2186
|
} catch {
|
|
1536
2187
|
return null;
|
|
1537
2188
|
}
|
|
1538
2189
|
}
|
|
1539
|
-
async function
|
|
1540
|
-
if (configPath === void 0) {
|
|
1541
|
-
return null;
|
|
1542
|
-
}
|
|
2190
|
+
async function readManifest(manifestPath) {
|
|
1543
2191
|
try {
|
|
1544
|
-
|
|
1545
|
-
return { path: configPath, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
2192
|
+
return readJsonFile(manifestPath);
|
|
1546
2193
|
} catch {
|
|
1547
2194
|
return null;
|
|
1548
2195
|
}
|
|
1549
2196
|
}
|
|
1550
|
-
async function resolveFileStamps(input) {
|
|
1551
|
-
const stamped = await Promise.all(
|
|
1552
|
-
input.files.map(async (p) => {
|
|
1553
|
-
try {
|
|
1554
|
-
const stat = await fs2.stat(p);
|
|
1555
|
-
return { path: p, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
1556
|
-
} catch {
|
|
1557
|
-
return { path: p, mtimeMs: -1, size: -1 };
|
|
1558
|
-
}
|
|
1559
|
-
})
|
|
1560
|
-
);
|
|
1561
|
-
stamped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
1562
|
-
return stamped;
|
|
1563
|
-
}
|
|
1564
|
-
function fileStampsEqual(a, b) {
|
|
1565
|
-
return a.length === b.length && a.every((s, i) => fileStampEquals(s, b[i]));
|
|
1566
|
-
}
|
|
1567
|
-
function fileStampEquals(a, b) {
|
|
1568
|
-
if (a === null && b === null) {
|
|
1569
|
-
return true;
|
|
1570
|
-
}
|
|
1571
|
-
if (a === null || b === null) {
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
return a.path === b.path && a.mtimeMs === b.mtimeMs && a.size === b.size;
|
|
1575
|
-
}
|
|
1576
2197
|
|
|
1577
2198
|
// src/extractionReport.ts
|
|
1578
2199
|
import { BOUNDARY_ROLE } from "@suss/behavioral-ir";
|
|
@@ -1681,6 +2302,38 @@ function firstEmptyUngatedStage(packs, filesWalked) {
|
|
|
1681
2302
|
return "assembly";
|
|
1682
2303
|
}
|
|
1683
2304
|
|
|
2305
|
+
// src/keptParses.ts
|
|
2306
|
+
var KeptParses = class {
|
|
2307
|
+
constructor(release = () => {
|
|
2308
|
+
}) {
|
|
2309
|
+
this.release = release;
|
|
2310
|
+
}
|
|
2311
|
+
release;
|
|
2312
|
+
byFile = /* @__PURE__ */ new Map();
|
|
2313
|
+
async parse(file, source, make) {
|
|
2314
|
+
const kept = this.byFile.get(file);
|
|
2315
|
+
if (kept !== void 0 && kept.source === source) {
|
|
2316
|
+
return kept.value;
|
|
2317
|
+
}
|
|
2318
|
+
const value = await make(source);
|
|
2319
|
+
if (kept !== void 0) {
|
|
2320
|
+
this.release(kept.value);
|
|
2321
|
+
}
|
|
2322
|
+
this.byFile.set(file, { source, value });
|
|
2323
|
+
return value;
|
|
2324
|
+
}
|
|
2325
|
+
/** Lets go of every file not in this run. */
|
|
2326
|
+
keepOnly(files) {
|
|
2327
|
+
const wanted = new Set(files);
|
|
2328
|
+
for (const [file, kept] of this.byFile) {
|
|
2329
|
+
if (!wanted.has(file)) {
|
|
2330
|
+
this.release(kept.value);
|
|
2331
|
+
this.byFile.delete(file);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
|
|
1684
2337
|
// src/packHealth.ts
|
|
1685
2338
|
function stagesOf(funnel) {
|
|
1686
2339
|
const stages = [];
|
|
@@ -1727,8 +2380,8 @@ function funnelDrops(packs) {
|
|
|
1727
2380
|
function recognizersWithNoUnits(packs) {
|
|
1728
2381
|
return packs.filter(
|
|
1729
2382
|
(funnel) => funnel.recognizes && !funnel.discovers && funnel.gates.length > 0 && funnel.unresolvedGates.length === 0 && funnel.candidateFiles > 0 && funnel.unitsInGatedFiles === 0 && // The closure walks a recognizer-only pack's gated exports as
|
|
1730
|
-
// roots, so effects recognized there mean the run worked and
|
|
1731
|
-
// the
|
|
2383
|
+
// roots, so effects recognized there mean the run worked and only
|
|
2384
|
+
// the framework pack's attribution is missing.
|
|
1732
2385
|
funnel.effectsRecognized === 0
|
|
1733
2386
|
).map((funnel) => ({
|
|
1734
2387
|
label: funnel.pack,
|
|
@@ -1970,7 +2623,7 @@ function makeTransitionId(functionName, branch) {
|
|
|
1970
2623
|
const { terminal } = branch;
|
|
1971
2624
|
const statusKey = terminal.statusCode === null ? "none" : terminal.statusCode.type === "literal" ? String(terminal.statusCode.value) : `dyn:${terminal.statusCode.sourceText}`;
|
|
1972
2625
|
const conditionSig = branch.conditions.map((c) => `${c.polarity}:${c.sourceText}`).join(";");
|
|
1973
|
-
const conditionHash =
|
|
2626
|
+
const conditionHash = createHash4("sha1").update(conditionSig).digest("hex").slice(0, 7);
|
|
1974
2627
|
return `${functionName}:${terminal.kind}:${statusKey}:${conditionHash}`;
|
|
1975
2628
|
}
|
|
1976
2629
|
function collapseReading(table, reading) {
|
|
@@ -2029,6 +2682,7 @@ function unreadSentences(raw) {
|
|
|
2029
2682
|
...raw.branches.flatMap(readingsOfBranch)
|
|
2030
2683
|
];
|
|
2031
2684
|
return [
|
|
2685
|
+
...raw.readFailure !== void 0 ? [raw.readFailure] : [],
|
|
2032
2686
|
...raw.unreadBinding !== void 0 ? [raw.unreadBinding] : [],
|
|
2033
2687
|
...handedOver.map(unreadReasonOf).filter((reason) => reason !== null)
|
|
2034
2688
|
];
|
|
@@ -2040,6 +2694,39 @@ function effectsOfBranch(branch) {
|
|
|
2040
2694
|
];
|
|
2041
2695
|
return foldRepeatedEffects(effects.map(withNormalizedCallee));
|
|
2042
2696
|
}
|
|
2697
|
+
function provenanceOf(raw, effects) {
|
|
2698
|
+
if (raw.length === 0) {
|
|
2699
|
+
return [];
|
|
2700
|
+
}
|
|
2701
|
+
const positionOf = new Map(effects.map((one, at) => [effectKey(one), at]));
|
|
2702
|
+
const bySlot = /* @__PURE__ */ new Map();
|
|
2703
|
+
const said = /* @__PURE__ */ new Map();
|
|
2704
|
+
for (const one of raw) {
|
|
2705
|
+
const effect = positionOf.get(effectKey(withNormalizedCallee(one.effect)));
|
|
2706
|
+
if (effect === void 0) {
|
|
2707
|
+
continue;
|
|
2708
|
+
}
|
|
2709
|
+
const slotKey = `${effect}\0${one.slot}\0${one.name}`;
|
|
2710
|
+
let entry = bySlot.get(slotKey);
|
|
2711
|
+
let seen = said.get(slotKey);
|
|
2712
|
+
if (entry === void 0 || seen === void 0) {
|
|
2713
|
+
entry = { at: { slot: one.slot, effect, name: one.name }, from: [] };
|
|
2714
|
+
seen = /* @__PURE__ */ new Set();
|
|
2715
|
+
bySlot.set(slotKey, entry);
|
|
2716
|
+
said.set(slotKey, seen);
|
|
2717
|
+
}
|
|
2718
|
+
for (const source of one.from) {
|
|
2719
|
+
const spelled = JSON.stringify(source);
|
|
2720
|
+
if (!seen.has(spelled)) {
|
|
2721
|
+
seen.add(spelled);
|
|
2722
|
+
entry.from.push(source);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
return [...bySlot.values()].sort(
|
|
2727
|
+
(a, b) => a.at.effect - b.at.effect || a.at.slot.localeCompare(b.at.slot) || a.at.name.localeCompare(b.at.name)
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2043
2730
|
function withNormalizedCallee(effect) {
|
|
2044
2731
|
if (effect.type !== "invocation" && effect.type !== "interaction") {
|
|
2045
2732
|
return effect;
|
|
@@ -2055,17 +2742,22 @@ function assembleSummary(raw, options = DEFAULT_OPTIONS) {
|
|
|
2055
2742
|
const conditions = branch.conditions.map(
|
|
2056
2743
|
rawConditionToPredicate
|
|
2057
2744
|
);
|
|
2745
|
+
const effects = effectsOfBranch(branch);
|
|
2058
2746
|
const transition = {
|
|
2059
2747
|
id: makeTransitionId(raw.identity.name, branch),
|
|
2060
2748
|
conditions,
|
|
2061
2749
|
output: terminalToOutput(branch.terminal),
|
|
2062
|
-
effects
|
|
2750
|
+
effects,
|
|
2063
2751
|
location: branch.location,
|
|
2064
2752
|
isDefault: branch.isDefault
|
|
2065
2753
|
};
|
|
2066
2754
|
if (branch.expectedInput != null) {
|
|
2067
2755
|
transition.expectedInput = branch.expectedInput;
|
|
2068
2756
|
}
|
|
2757
|
+
const provenance = provenanceOf(branch.provenance ?? [], effects);
|
|
2758
|
+
if (provenance.length > 0) {
|
|
2759
|
+
transition.provenance = provenance;
|
|
2760
|
+
}
|
|
2069
2761
|
return transition;
|
|
2070
2762
|
});
|
|
2071
2763
|
const gaps = detectGaps(raw, transitions, options);
|
|
@@ -2127,6 +2819,9 @@ function buildMetadata(raw) {
|
|
|
2127
2819
|
if (raw.requestSpelling !== void 0) {
|
|
2128
2820
|
metadata = withRequestSpellingMetadata(metadata, raw.requestSpelling);
|
|
2129
2821
|
}
|
|
2822
|
+
if (raw.test !== void 0) {
|
|
2823
|
+
metadata = withTestMetadata(metadata, raw.test);
|
|
2824
|
+
}
|
|
2130
2825
|
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
2131
2826
|
}
|
|
2132
2827
|
function buildHttpMetadataValue(raw) {
|
|
@@ -2150,6 +2845,12 @@ function buildHttpMetadataValue(raw) {
|
|
|
2150
2845
|
if (raw.failureDelivery !== void 0) {
|
|
2151
2846
|
http.failureDelivery = raw.failureDelivery;
|
|
2152
2847
|
}
|
|
2848
|
+
if (raw.redirectDelivery !== void 0) {
|
|
2849
|
+
http.redirectDelivery = raw.redirectDelivery;
|
|
2850
|
+
}
|
|
2851
|
+
if (raw.declaredStatuses !== void 0 && raw.declaredStatuses.length > 0) {
|
|
2852
|
+
http.declaredStatuses = raw.declaredStatuses;
|
|
2853
|
+
}
|
|
2153
2854
|
return Object.keys(http).length > 0 ? http : null;
|
|
2154
2855
|
}
|
|
2155
2856
|
function buildGraphqlMetadataValue(raw) {
|
|
@@ -2217,13 +2918,16 @@ function detectGaps(raw, transitions, options) {
|
|
|
2217
2918
|
return gaps;
|
|
2218
2919
|
}
|
|
2219
2920
|
function bodyWentUnread(raw) {
|
|
2921
|
+
if (raw.readFailure !== void 0) {
|
|
2922
|
+
return true;
|
|
2923
|
+
}
|
|
2220
2924
|
if (raw.bodyContent === "absent" || raw.bodyContent === "elsewhere") {
|
|
2221
2925
|
return true;
|
|
2222
2926
|
}
|
|
2223
2927
|
return raw.bodyContent === "statements" && raw.branches.length === 0;
|
|
2224
2928
|
}
|
|
2225
2929
|
function describeUnreadBody(raw) {
|
|
2226
|
-
if (!bodyWentUnread(raw)) {
|
|
2930
|
+
if (!bodyWentUnread(raw) || raw.readFailure !== void 0) {
|
|
2227
2931
|
return null;
|
|
2228
2932
|
}
|
|
2229
2933
|
if (raw.bodyContent === "absent") {
|
|
@@ -2270,26 +2974,41 @@ function bodyToShape(body) {
|
|
|
2270
2974
|
}
|
|
2271
2975
|
return null;
|
|
2272
2976
|
}
|
|
2977
|
+
function statusValueRef(code) {
|
|
2978
|
+
if (code === null) {
|
|
2979
|
+
return null;
|
|
2980
|
+
}
|
|
2981
|
+
return code.type === "literal" ? { type: "literal", value: code.value } : { type: "unresolved", sourceText: code.sourceText };
|
|
2982
|
+
}
|
|
2273
2983
|
var terminalConverters = {
|
|
2274
2984
|
response: (t) => {
|
|
2275
|
-
const statusCode = t.statusCode ? t.statusCode.type === "literal" ? { type: "literal", value: t.statusCode.value } : { type: "unresolved", sourceText: t.statusCode.sourceText } : null;
|
|
2276
2985
|
const body = bodyToShape(t.body);
|
|
2277
|
-
return {
|
|
2986
|
+
return {
|
|
2987
|
+
type: "response",
|
|
2988
|
+
statusCode: statusValueRef(t.statusCode),
|
|
2989
|
+
body,
|
|
2990
|
+
headers: {}
|
|
2991
|
+
};
|
|
2278
2992
|
},
|
|
2279
2993
|
throw: (t) => {
|
|
2280
2994
|
if (t.statusCode && t.producesResponse === true) {
|
|
2281
|
-
const statusCode = t.statusCode.type === "literal" ? { type: "literal", value: t.statusCode.value } : { type: "unresolved", sourceText: t.statusCode.sourceText };
|
|
2282
2995
|
return {
|
|
2283
2996
|
type: "response",
|
|
2284
|
-
statusCode,
|
|
2997
|
+
statusCode: statusValueRef(t.statusCode),
|
|
2285
2998
|
body: bodyToShape(t.body),
|
|
2286
2999
|
headers: {}
|
|
2287
3000
|
};
|
|
2288
3001
|
}
|
|
3002
|
+
const ancestry = t.exceptionAncestry;
|
|
2289
3003
|
return {
|
|
2290
3004
|
type: "throw",
|
|
2291
3005
|
exceptionType: t.exceptionType,
|
|
2292
|
-
message: t.message
|
|
3006
|
+
message: t.message,
|
|
3007
|
+
...ancestry === void 0 ? {} : {
|
|
3008
|
+
exceptionAncestors: ancestry.ancestors,
|
|
3009
|
+
...ancestry.incomplete ? { ancestryIncomplete: true } : {}
|
|
3010
|
+
},
|
|
3011
|
+
...t.statusWhenUncaught === void 0 ? {} : { statusWhenUncaught: t.statusWhenUncaught }
|
|
2293
3012
|
};
|
|
2294
3013
|
},
|
|
2295
3014
|
render: (t) => ({
|
|
@@ -2309,7 +3028,8 @@ var terminalConverters = {
|
|
|
2309
3028
|
type: "return",
|
|
2310
3029
|
value: bodyToShape(t.body)
|
|
2311
3030
|
}),
|
|
2312
|
-
void: (_t) => ({ type: "void" })
|
|
3031
|
+
void: (_t) => ({ type: "void" }),
|
|
3032
|
+
exit: (t) => ({ type: "exit", code: statusValueRef(t.statusCode) })
|
|
2313
3033
|
};
|
|
2314
3034
|
function terminalToOutput(terminal) {
|
|
2315
3035
|
return terminalConverters[terminal.kind](terminal);
|
|
@@ -2352,14 +3072,19 @@ function paramToInput(param) {
|
|
|
2352
3072
|
name: param.name,
|
|
2353
3073
|
position: param.position,
|
|
2354
3074
|
role: param.role,
|
|
3075
|
+
...param.field === void 0 ? {} : { field: param.field },
|
|
2355
3076
|
shape: param.typeText ? { type: "ref", name: param.typeText } : null
|
|
2356
3077
|
};
|
|
2357
3078
|
}
|
|
2358
3079
|
export {
|
|
3080
|
+
EnclosingNodes,
|
|
2359
3081
|
IdMap,
|
|
2360
3082
|
IdSet,
|
|
3083
|
+
KeptParses,
|
|
2361
3084
|
MAX_ENTRIES,
|
|
3085
|
+
MAX_HELPER_PATHS,
|
|
2362
3086
|
MAX_PATHS,
|
|
3087
|
+
MODULE_SURFACE_RECOGNITION,
|
|
2363
3088
|
PathBudgetExceeded,
|
|
2364
3089
|
SKIP_CHILDREN,
|
|
2365
3090
|
UnmodeledFlow,
|
|
@@ -2384,30 +3109,45 @@ export {
|
|
|
2384
3109
|
enumerateOrDegrade,
|
|
2385
3110
|
enumerateStructuredPaths,
|
|
2386
3111
|
evaluatePackHealth,
|
|
3112
|
+
extractionConfigStamp,
|
|
2387
3113
|
firstWrittenReading,
|
|
2388
3114
|
formatPackHealth,
|
|
2389
3115
|
guardsHoldOn,
|
|
3116
|
+
helperPathsOnVariables,
|
|
2390
3117
|
httpRouteDiscovery,
|
|
3118
|
+
isListedTestFile,
|
|
3119
|
+
makeIgnoredDirectory,
|
|
2391
3120
|
makeTransitionId,
|
|
2392
3121
|
mapReading,
|
|
3122
|
+
markReturnsAsExitCode,
|
|
3123
|
+
matchesTestFileName,
|
|
3124
|
+
mergeReadShapes,
|
|
2393
3125
|
moduleInitStructure,
|
|
3126
|
+
moduleOfFile,
|
|
2394
3127
|
noopTimer,
|
|
2395
3128
|
packGradients,
|
|
2396
3129
|
paramToInput,
|
|
2397
3130
|
projectFileStamp,
|
|
2398
3131
|
rawConditionToPredicate,
|
|
2399
3132
|
recordPackFailure,
|
|
3133
|
+
redirectDeliveryWhenSet,
|
|
2400
3134
|
registrationHelperDiscovery,
|
|
2401
3135
|
routeHelperIndex,
|
|
2402
3136
|
runDigest,
|
|
2403
3137
|
runsBefore,
|
|
2404
3138
|
scopeOption,
|
|
3139
|
+
settleModules,
|
|
2405
3140
|
sharedGatingConditions,
|
|
3141
|
+
sourceRefsOf,
|
|
3142
|
+
splitAtHandOffs,
|
|
3143
|
+
stampMayMissAWrite,
|
|
2406
3144
|
stampModuleImports,
|
|
3145
|
+
stampModules,
|
|
2407
3146
|
storageSystemOption,
|
|
2408
3147
|
summaryCountsByPack,
|
|
2409
3148
|
tallyUnit,
|
|
2410
3149
|
terminalToOutput,
|
|
3150
|
+
throughHelper,
|
|
2411
3151
|
unreadableReading,
|
|
2412
3152
|
unwrapJsonStringify,
|
|
2413
3153
|
valueToReadFurtherFrom,
|