@suss/extractor 0.21.1 → 0.22.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 +40 -0
- package/dist/index.cjs +990 -41
- package/dist/index.d.cts +644 -1
- package/dist/index.d.ts +644 -1
- package/dist/index.js +952 -34
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1893,6 +1893,50 @@ declare function guardsHoldOn(preconditions: readonly RawCondition[] | undefined
|
|
|
1893
1893
|
*/
|
|
1894
1894
|
declare function runsBefore(effectLine: number, terminalEndLine: number): boolean;
|
|
1895
1895
|
|
|
1896
|
+
/**
|
|
1897
|
+
* moduleImports.ts: writing `metadata.moduleImports` on a summary set.
|
|
1898
|
+
*
|
|
1899
|
+
* Every adapter records the project files a summary's own file depends
|
|
1900
|
+
* on, and a checker rebuilds the module graph from that field to find
|
|
1901
|
+
* what runs inside a deployable unit. The adapters differ in where the
|
|
1902
|
+
* edges come from (TypeScript resolves import declarations, Python
|
|
1903
|
+
* resolves them through facts, Ruby has `require_relative` and constant
|
|
1904
|
+
* references), so the stamp takes a lookup and leaves the source to them.
|
|
1905
|
+
*
|
|
1906
|
+
* The paths a lookup returns must be spelled the way `location.file` is,
|
|
1907
|
+
* so both ends of the graph match. An empty list is stamped as one, which
|
|
1908
|
+
* makes the file a leaf of the graph; undefined leaves the summary alone.
|
|
1909
|
+
*/
|
|
1910
|
+
|
|
1911
|
+
declare function stampModuleImports(summaries: BehavioralSummary[], importsOf: (file: string) => Iterable<string> | undefined): void;
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* moduleInit.ts: the raw structure for what a module does when it loads.
|
|
1915
|
+
*
|
|
1916
|
+
* A module's top-level statements run once, when it is first imported,
|
|
1917
|
+
* and a service that reads its configuration there reads it there for
|
|
1918
|
+
* good. Walking unit bodies alone never sees that read, so the read gets
|
|
1919
|
+
* a unit of its own, one per file, named after the file, with no
|
|
1920
|
+
* boundary. Attributing it to each handler in the file would report one
|
|
1921
|
+
* read as several, and a file with no handler would still report nothing.
|
|
1922
|
+
*
|
|
1923
|
+
* The structure is one default branch whose terminal is `void`, because
|
|
1924
|
+
* module initialization returns to nobody. Every adapter builds it the
|
|
1925
|
+
* same way, so it lives here.
|
|
1926
|
+
*/
|
|
1927
|
+
|
|
1928
|
+
interface ModuleInitOptions {
|
|
1929
|
+
/** The unit name, which is the file's base name in every adapter. */
|
|
1930
|
+
name: string;
|
|
1931
|
+
file: string;
|
|
1932
|
+
range: {
|
|
1933
|
+
start: number;
|
|
1934
|
+
end: number;
|
|
1935
|
+
};
|
|
1936
|
+
effects: Effect[];
|
|
1937
|
+
}
|
|
1938
|
+
declare function moduleInitStructure(options: ModuleInitOptions): RawCodeStructure;
|
|
1939
|
+
|
|
1896
1940
|
/**
|
|
1897
1941
|
* Small helpers for pattern packs that are built the same way.
|
|
1898
1942
|
*
|
|
@@ -2207,6 +2251,605 @@ interface LanguageAdapter {
|
|
|
2207
2251
|
extractAll(): Promise<BehavioralSummary[]>;
|
|
2208
2252
|
}
|
|
2209
2253
|
|
|
2254
|
+
/**
|
|
2255
|
+
* adapterStamp.ts: the language-neutral half of an adapter's cache key.
|
|
2256
|
+
*
|
|
2257
|
+
* An adapter's own version.ts calls `createAdapterStamp` with its own
|
|
2258
|
+
* `import.meta.url` and its hand-bumped version, and keeps the result for
|
|
2259
|
+
* the life of the process. The stamp hashes the adapter's own dist file
|
|
2260
|
+
* plus every analysis package it ships beside (this package,
|
|
2261
|
+
* `@suss/resolution`, `@suss/datalog`, `@suss/behavioral-ir`), so a
|
|
2262
|
+
* release that changes any of them invalidates an older cache, and a
|
|
2263
|
+
* dev rebuild invalidates on its own with no version bump by hand.
|
|
2264
|
+
* Running from source has no dist file to hash, and that mode declines
|
|
2265
|
+
* to cache. The extraction cache's design is in this package's README.
|
|
2266
|
+
*/
|
|
2267
|
+
/**
|
|
2268
|
+
* Whether this process can see the adapter's own code. `bundle` includes
|
|
2269
|
+
* a hash that changes whenever the code does. `source` means nothing
|
|
2270
|
+
* here could find it, so a cache key built from this stamp says nothing
|
|
2271
|
+
* about the code that will produce the results.
|
|
2272
|
+
*/
|
|
2273
|
+
type AdapterCodeStamp = {
|
|
2274
|
+
kind: "bundle";
|
|
2275
|
+
hash: string;
|
|
2276
|
+
} | {
|
|
2277
|
+
kind: "source";
|
|
2278
|
+
};
|
|
2279
|
+
/** What `createAdapterStamp` hands back to an adapter's own version.ts. */
|
|
2280
|
+
interface AdapterStamp {
|
|
2281
|
+
/** The stamp for the running adapter, computed once per process. */
|
|
2282
|
+
codeStamp(): AdapterCodeStamp;
|
|
2283
|
+
/**
|
|
2284
|
+
* Cache-friendly identity for this adapter plus its packs. Stable
|
|
2285
|
+
* across processes given the same inputs; bumps when any pack arrives
|
|
2286
|
+
* with a new version stamp, the adapter version changes, or the
|
|
2287
|
+
* loaded adapter dist file changes (dev-mode rebuild auto-invalidation).
|
|
2288
|
+
*/
|
|
2289
|
+
packsDigest(packVersions: ReadonlyArray<{
|
|
2290
|
+
name: string;
|
|
2291
|
+
version?: string;
|
|
2292
|
+
}>): string;
|
|
2293
|
+
/**
|
|
2294
|
+
* `cacheDir` unless this process loaded the adapter from source, where
|
|
2295
|
+
* nothing can tell one build of it from another; then this returns
|
|
2296
|
+
* null and, once per process, says why on stderr.
|
|
2297
|
+
*/
|
|
2298
|
+
declineWhenRunFromSource(cacheDir: string | null): string | null;
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* Build the code stamp and packs digest for one adapter. `moduleUrl` has
|
|
2302
|
+
* to be the calling module's own `import.meta.url`, so the stamp finds
|
|
2303
|
+
* the dist file the adapter itself was loaded from rather than some
|
|
2304
|
+
* other package's.
|
|
2305
|
+
*/
|
|
2306
|
+
declare function createAdapterStamp(config: {
|
|
2307
|
+
moduleUrl: string;
|
|
2308
|
+
version: string;
|
|
2309
|
+
}): AdapterStamp;
|
|
2310
|
+
/**
|
|
2311
|
+
* The hash for a bundle directory: the bundle itself plus every analysis
|
|
2312
|
+
* package that can be located. Empty when the directory has no bundle in
|
|
2313
|
+
* it, which is what running from source looks like.
|
|
2314
|
+
*/
|
|
2315
|
+
declare function computeDistHashFrom(dir: string): string;
|
|
2316
|
+
/**
|
|
2317
|
+
* Content hash of the given files, in the order they arrive. Empty when
|
|
2318
|
+
* the list is empty or a file cannot be read, so a caller that could not
|
|
2319
|
+
* locate a file gets the same "no stamp" result as a run from source
|
|
2320
|
+
* rather than a hash of a shorter list.
|
|
2321
|
+
*
|
|
2322
|
+
* The caller resolves the paths, because where a specifier resolves to
|
|
2323
|
+
* depends on which package is asking.
|
|
2324
|
+
*/
|
|
2325
|
+
declare function computeContentHash(paths: readonly string[]): string;
|
|
2326
|
+
/**
|
|
2327
|
+
* Stamp for a set of project files a run reads without walking them: a
|
|
2328
|
+
* SAM template, a workspace package.json. Both the paths and the
|
|
2329
|
+
* content go in, so a file that moves counts as a change even when
|
|
2330
|
+
* every byte in it stayed the same.
|
|
2331
|
+
*
|
|
2332
|
+
* A file that cannot be read stamps as absent rather than voiding the
|
|
2333
|
+
* whole stamp, which is what `computeContentHash` does. These files
|
|
2334
|
+
* belong to the project rather than to the installed tool: one of them
|
|
2335
|
+
* being gone is a fact about the project the next run should notice,
|
|
2336
|
+
* not a reason to stop telling runs apart.
|
|
2337
|
+
*/
|
|
2338
|
+
declare function projectFileStamp(paths: readonly string[]): string;
|
|
2339
|
+
/**
|
|
2340
|
+
* The digest a run looks its cache entry up under. A pack may read
|
|
2341
|
+
* project files that are not among the ones a run walks, a SAM template
|
|
2342
|
+
* that decides which handlers exist for instance, so those belong in
|
|
2343
|
+
* the key next to the pack's own config. Which files they are depends
|
|
2344
|
+
* on the files this run walks, so the digest is settled per run rather
|
|
2345
|
+
* than once per adapter.
|
|
2346
|
+
*/
|
|
2347
|
+
declare function runDigest(packsDigest: string, packs: ReadonlyArray<{
|
|
2348
|
+
discoveryInputs?: (files: readonly string[]) => string[];
|
|
2349
|
+
}>, files: readonly string[]): string;
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* The on-disk extraction cache, shared by every language adapter.
|
|
2353
|
+
*
|
|
2354
|
+
* A run reuses the previous one's summaries whole when nothing
|
|
2355
|
+
* changed, and per file when some files did. The entry directory is
|
|
2356
|
+
* named after everything that has to agree before any reuse is sound:
|
|
2357
|
+
* schema version, adapter version and code hash, pack versions, the
|
|
2358
|
+
* extraction config stamp and the config file path. The manifest
|
|
2359
|
+
* inside records a stamp and content hash per file, each summary's
|
|
2360
|
+
* owning files, and per owning file the other files its walk read.
|
|
2361
|
+
* An adapter attaches whatever it needs to a cached file's record
|
|
2362
|
+
* through `meta`, which the cache stores and returns opaquely. The
|
|
2363
|
+
* full design is in this package's README.
|
|
2364
|
+
*/
|
|
2365
|
+
|
|
2366
|
+
/**
|
|
2367
|
+
* How many keys' worth of entries a cache directory keeps. Two lets a
|
|
2368
|
+
* pair of builds alternate (a branch switch, an adapter rebuild and a
|
|
2369
|
+
* revert) without either losing its entry, and bounds a directory at
|
|
2370
|
+
* twice one manifest.
|
|
2371
|
+
*/
|
|
2372
|
+
declare const MAX_ENTRIES = 2;
|
|
2373
|
+
/**
|
|
2374
|
+
* What one walked file contributed to the run, and what its walk read.
|
|
2375
|
+
* `deps` are the other files whose content went into this file's
|
|
2376
|
+
* summaries; a change to any of them re-extracts this file. `claims`
|
|
2377
|
+
* are the units this file's walk claimed, replayed before a partial
|
|
2378
|
+
* run walks anything so precedence comes out the same. `meta` is
|
|
2379
|
+
* whatever else the adapter needs to revalidate this file on a
|
|
2380
|
+
* partial run (a route's mount prefixes, for instance); the cache
|
|
2381
|
+
* never reads it. A file marked `cacheable: false` recorded a
|
|
2382
|
+
* dependency the cache cannot pin to files, and is re-extracted on
|
|
2383
|
+
* every partial run.
|
|
2384
|
+
*/
|
|
2385
|
+
interface RootRecord<Meta = unknown> {
|
|
2386
|
+
path: string;
|
|
2387
|
+
cacheable: boolean;
|
|
2388
|
+
deps: string[];
|
|
2389
|
+
claims: {
|
|
2390
|
+
key: string;
|
|
2391
|
+
pack: string;
|
|
2392
|
+
}[];
|
|
2393
|
+
meta: Meta;
|
|
2394
|
+
/** Packs that applied to the file, re-checked on a partial run. */
|
|
2395
|
+
packs: string[];
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Which files each summary belongs to. `owners[i]` lists the walked
|
|
2399
|
+
* files whose reuse keeps `summaries[i]` alive; an empty list marks a
|
|
2400
|
+
* run-level summary that a partial run always recomputes.
|
|
2401
|
+
*/
|
|
2402
|
+
interface CacheAttribution<Meta = unknown> {
|
|
2403
|
+
roots: RootRecord<Meta>[];
|
|
2404
|
+
owners: string[][];
|
|
2405
|
+
}
|
|
2406
|
+
/** Reported by `lookup`: what the cache decided, and why. */
|
|
2407
|
+
interface CacheDiagnostic {
|
|
2408
|
+
kind: "hit" | "miss" | "partial";
|
|
2409
|
+
/**
|
|
2410
|
+
* Reason the lookup missed (only set when kind === "miss").
|
|
2411
|
+
* `key-changed` means the cache directory contains entries, but none
|
|
2412
|
+
* under this run's schema, adapter, packs and config path.
|
|
2413
|
+
* `files-changed` means the include set is not the one the entry
|
|
2414
|
+
* was written from.
|
|
2415
|
+
*/
|
|
2416
|
+
missReason?: "no-manifest" | "key-changed" | "config-changed" | "files-changed";
|
|
2417
|
+
/** Set when kind === "partial". */
|
|
2418
|
+
partial?: {
|
|
2419
|
+
filesChanged: number;
|
|
2420
|
+
filesRemoved: number;
|
|
2421
|
+
rootsReused: number;
|
|
2422
|
+
rootsReextracted: number;
|
|
2423
|
+
rootsDeclined: number;
|
|
2424
|
+
summariesReused: number;
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Result of a cache lookup. A hit gives back the whole summary set; a miss
|
|
2429
|
+
* says why, so a caller can render the reason.
|
|
2430
|
+
*/
|
|
2431
|
+
type CacheLookup = {
|
|
2432
|
+
kind: "hit";
|
|
2433
|
+
summaries: BehavioralSummary[];
|
|
2434
|
+
diagnostic: CacheDiagnostic;
|
|
2435
|
+
} | {
|
|
2436
|
+
kind: "miss";
|
|
2437
|
+
diagnostic: CacheDiagnostic;
|
|
2438
|
+
};
|
|
2439
|
+
/**
|
|
2440
|
+
* What a `files-changed` miss can still reuse. `validRoots` is the
|
|
2441
|
+
* cache's own verdict from hashes and recorded dependencies; the
|
|
2442
|
+
* caller may demote further (a mount prefix that no longer matches)
|
|
2443
|
+
* before calling `reuse`. `reuse` returns the summaries owned by at
|
|
2444
|
+
* least one surviving root, in stored order, with their owners, so
|
|
2445
|
+
* the caller can merge them and write the result back.
|
|
2446
|
+
*/
|
|
2447
|
+
interface PartialPlan<Meta = unknown> {
|
|
2448
|
+
/** Paths whose content hash differs, plus paths new to the set. */
|
|
2449
|
+
changed: Set<string>;
|
|
2450
|
+
removed: Set<string>;
|
|
2451
|
+
roots: Map<string, RootRecord<Meta>>;
|
|
2452
|
+
validRoots: Set<string>;
|
|
2453
|
+
rootsDeclined: number;
|
|
2454
|
+
reuse(valid: ReadonlySet<string>): {
|
|
2455
|
+
summaries: BehavioralSummary[];
|
|
2456
|
+
owners: string[][];
|
|
2457
|
+
};
|
|
2458
|
+
allSummaries(): BehavioralSummary[];
|
|
2459
|
+
/** The stored attribution decoded, for a write that changes nothing. */
|
|
2460
|
+
attribution(): CacheAttribution<Meta>;
|
|
2461
|
+
}
|
|
2462
|
+
interface CacheLayer<Meta = unknown> {
|
|
2463
|
+
/** The summary list on a hit, null on a miss. */
|
|
2464
|
+
tryHit(input: CacheInput): Promise<BehavioralSummary[] | null>;
|
|
2465
|
+
/**
|
|
2466
|
+
* The lookup behind `tryHit`, with the reason a miss missed. Costs
|
|
2467
|
+
* stats alone: no file reads, no AST work.
|
|
2468
|
+
*/
|
|
2469
|
+
lookup(input: CacheInput): Promise<CacheLookup>;
|
|
2470
|
+
/**
|
|
2471
|
+
* After a `files-changed` miss: hash what the stats said moved and
|
|
2472
|
+
* work out which files' summaries survive. Null when the entry has
|
|
2473
|
+
* no per-file layer to reuse, or no entry matches the key at all.
|
|
2474
|
+
*/
|
|
2475
|
+
plan(input: CacheInput): Promise<PartialPlan<Meta> | null>;
|
|
2476
|
+
/**
|
|
2477
|
+
* Persist a fresh extraction's summaries to the cache, keyed
|
|
2478
|
+
* against the same file list. Subsequent `lookup` calls with the
|
|
2479
|
+
* same files return them. Without `attribution` the entry can only
|
|
2480
|
+
* ever be reused whole.
|
|
2481
|
+
*/
|
|
2482
|
+
write(input: CacheInput, summaries: BehavioralSummary[], attribution?: CacheAttribution<Meta>): Promise<void>;
|
|
2483
|
+
}
|
|
2484
|
+
interface CacheInput {
|
|
2485
|
+
/** Absolute paths of every file the run walks. */
|
|
2486
|
+
files: ReadonlyArray<string>;
|
|
2487
|
+
adapterPacksDigest: string;
|
|
2488
|
+
/**
|
|
2489
|
+
* One file whose stamp guards the whole entry alongside the file
|
|
2490
|
+
* list, such as a tsconfig or a project manifest. Optional because
|
|
2491
|
+
* not every adapter has one.
|
|
2492
|
+
*/
|
|
2493
|
+
configPath?: string;
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Construct a cache layer rooted at `cacheDir`. Pass `null` to
|
|
2497
|
+
* opt out of caching entirely, the returned layer's `tryHit`
|
|
2498
|
+
* always misses and `write` is a no-op. Useful for one-shot
|
|
2499
|
+
* extracts where caching adds latency without payoff.
|
|
2500
|
+
*/
|
|
2501
|
+
declare function createCacheLayer<Meta = unknown>(cacheDir: string | null): CacheLayer<Meta>;
|
|
2502
|
+
|
|
2503
|
+
/**
|
|
2504
|
+
* The extraction funnel, shared across languages.
|
|
2505
|
+
*
|
|
2506
|
+
* "Why did this run produce nothing" is always "at which stage did the
|
|
2507
|
+
* count reach zero", so the report is a funnel: files in the tsconfig,
|
|
2508
|
+
* files the import gates selected, units discovered, summaries built.
|
|
2509
|
+
* Each row is recorded by the stage that owns it, because the
|
|
2510
|
+
* alternative is the CLI re-deriving the pre-filter's decisions from a
|
|
2511
|
+
* second copy of its logic, and a second copy drifting from the first
|
|
2512
|
+
* is what made an entire pack family extract nothing in silence.
|
|
2513
|
+
*
|
|
2514
|
+
* TypeScript, Python and Ruby all build one of these, though only
|
|
2515
|
+
* TypeScript gates files by import specifier today. A pack with no gate
|
|
2516
|
+
* concept reports it the way TypeScript reports an ungated pack.
|
|
2517
|
+
*/
|
|
2518
|
+
|
|
2519
|
+
/** One pack's path through the funnel. */
|
|
2520
|
+
interface PackFunnel {
|
|
2521
|
+
pack: string;
|
|
2522
|
+
/**
|
|
2523
|
+
* What the pack calls this build of itself, or null when it declares
|
|
2524
|
+
* nothing. The cache keys on it, so a pack that never changes its
|
|
2525
|
+
* stamp can serve a later run with an earlier build's results.
|
|
2526
|
+
*/
|
|
2527
|
+
version: string | null;
|
|
2528
|
+
/**
|
|
2529
|
+
* Whether the pack looks for units of its own at all. A pack made only
|
|
2530
|
+
* of recognisers contributes effects to units other packs found, so it
|
|
2531
|
+
* always discovers nothing, and that tells you nothing about whether
|
|
2532
|
+
* it is working.
|
|
2533
|
+
*/
|
|
2534
|
+
discovers: boolean;
|
|
2535
|
+
/**
|
|
2536
|
+
* Whether the pack has any recognisers. Those fire inside units other
|
|
2537
|
+
* packs discovered, so a pack made only of them contributes effects
|
|
2538
|
+
* and never a summary.
|
|
2539
|
+
*/
|
|
2540
|
+
recognizes: boolean;
|
|
2541
|
+
/**
|
|
2542
|
+
* Import specifiers that make a file relevant to this pack. Empty
|
|
2543
|
+
* for an ungated pack, which walks every file.
|
|
2544
|
+
*/
|
|
2545
|
+
gates: string[];
|
|
2546
|
+
/**
|
|
2547
|
+
* Gate specifiers that do not resolve from the project. Non-empty
|
|
2548
|
+
* here usually means the target project's dependencies are not
|
|
2549
|
+
* installed, which stops symbol-resolution packs while leaving
|
|
2550
|
+
* textual-gate packs working, so the run half-succeeds in a way
|
|
2551
|
+
* nothing else surfaces.
|
|
2552
|
+
*/
|
|
2553
|
+
unresolvedGates: string[];
|
|
2554
|
+
/** Files the pre-filter selected for this pack. */
|
|
2555
|
+
candidateFiles: number;
|
|
2556
|
+
/** Units discovered across those files. */
|
|
2557
|
+
unitsDiscovered: number;
|
|
2558
|
+
/**
|
|
2559
|
+
* Unit bodies any pack walked in the files this pack's gate selected.
|
|
2560
|
+
*
|
|
2561
|
+
* This is what a recogniser pack had the chance to fire on. Its own
|
|
2562
|
+
* discovery count tells you nothing, since it discovers nothing by
|
|
2563
|
+
* design, and neither does its candidate-file count, because a
|
|
2564
|
+
* recogniser only runs where some pack found a unit to walk.
|
|
2565
|
+
*/
|
|
2566
|
+
unitsInGatedFiles: number;
|
|
2567
|
+
/** Effects this pack's recognisers returned. */
|
|
2568
|
+
effectsRecognized: number;
|
|
2569
|
+
/**
|
|
2570
|
+
* Units this pack kept. A unit an earlier pack already claimed is
|
|
2571
|
+
* dropped here, so a pack can discover plenty and keep none when it
|
|
2572
|
+
* comes after a pack that recognises the same code.
|
|
2573
|
+
*/
|
|
2574
|
+
unitsClaimed: number;
|
|
2575
|
+
/**
|
|
2576
|
+
* Units this pack discovered twice over. Cross-pack dedup is the
|
|
2577
|
+
* point of the claim set, but a pack colliding with itself means its
|
|
2578
|
+
* own discovery patterns overlap and one of the two is being thrown
|
|
2579
|
+
* away without anybody deciding which.
|
|
2580
|
+
*/
|
|
2581
|
+
selfCollisions: number;
|
|
2582
|
+
/** Summaries built from those units. */
|
|
2583
|
+
summariesProduced: number;
|
|
2584
|
+
/** Summaries in the finished run that credit this pack for finding them. */
|
|
2585
|
+
summariesBound: number;
|
|
2586
|
+
/**
|
|
2587
|
+
* Bound summaries on the provider side of their boundary.
|
|
2588
|
+
*
|
|
2589
|
+
* Kept apart from the count below because a provider that produced
|
|
2590
|
+
* no transitions is a different problem from a client that produced
|
|
2591
|
+
* none.
|
|
2592
|
+
*/
|
|
2593
|
+
providerSummaries: number;
|
|
2594
|
+
/**
|
|
2595
|
+
* Summaries with at least one transition, of any role. A provider's
|
|
2596
|
+
* transitions say what it does with a request and a client's say what
|
|
2597
|
+
* it expects back, so a pack whose summaries have none has bound
|
|
2598
|
+
* something and described nothing either way.
|
|
2599
|
+
*/
|
|
2600
|
+
summariesWithBehavior: number;
|
|
2601
|
+
/**
|
|
2602
|
+
* Where one of this pack's hooks threw. Every count above is a floor
|
|
2603
|
+
* while this is non-empty.
|
|
2604
|
+
*/
|
|
2605
|
+
failures: PackFailure[];
|
|
2606
|
+
/**
|
|
2607
|
+
* Registration helpers this pack's config asked for that no call in
|
|
2608
|
+
* the run matched. The helper belongs to the project, so a spelling
|
|
2609
|
+
* that matches nothing is a config mistake with no other symptom: the
|
|
2610
|
+
* routes go missing and every count reads the same as a project
|
|
2611
|
+
* without them.
|
|
2612
|
+
*/
|
|
2613
|
+
helpersUnmatched: string[];
|
|
2614
|
+
/**
|
|
2615
|
+
* What the pack wrote as data rather than as code, or null for a pack
|
|
2616
|
+
* written as a hand-rolled walk, or for a language whose packs have
|
|
2617
|
+
* no declared-pattern system at all. This is the one thing in the
|
|
2618
|
+
* funnel that no run produces: it is the pack's own shape, and it is
|
|
2619
|
+
* here so the migration onto the declared surface can be measured.
|
|
2620
|
+
*/
|
|
2621
|
+
declarations: PackDeclarations | null;
|
|
2622
|
+
}
|
|
2623
|
+
interface ExtractionReport {
|
|
2624
|
+
/** Files in the project's include set, or null when nothing states one separately from the walk. */
|
|
2625
|
+
filesInProject: number | null;
|
|
2626
|
+
/** Files the adapter loaded and the extract walked. */
|
|
2627
|
+
filesWalked: number;
|
|
2628
|
+
packs: PackFunnel[];
|
|
2629
|
+
summaries: number;
|
|
2630
|
+
/**
|
|
2631
|
+
* Files whose exports the checker could not follow, so the run read
|
|
2632
|
+
* them as exporting nothing.
|
|
2633
|
+
*
|
|
2634
|
+
* Without this the artifact cannot tell the two apart: a module whose
|
|
2635
|
+
* barrel chain outran the call stack and a module that really does
|
|
2636
|
+
* export nothing both produce no summaries and exit 0.
|
|
2637
|
+
* Anything reachable only through these files is missing, and every
|
|
2638
|
+
* count below is a floor while this is non-empty.
|
|
2639
|
+
*/
|
|
2640
|
+
filesWithUnreadableExports: string[];
|
|
2641
|
+
/**
|
|
2642
|
+
* The first stage whose count was zero, when the run produced
|
|
2643
|
+
* nothing. Null when the run produced summaries.
|
|
2644
|
+
*/
|
|
2645
|
+
emptyStage: EmptyStage | null;
|
|
2646
|
+
/**
|
|
2647
|
+
* Reassigned names the run stated nothing for, because control flow
|
|
2648
|
+
* decides which write a reader sees. Each is a value resolution the
|
|
2649
|
+
* facts decline; the count across a large corpus says whether scoped
|
|
2650
|
+
* reaching definitions is worth writing.
|
|
2651
|
+
*/
|
|
2652
|
+
reassignedNamesUnstated: number;
|
|
2653
|
+
}
|
|
2654
|
+
type EmptyStage = "tsconfig" | "gateResolution" | "candidateFiles" | "discovery" | "assembly";
|
|
2655
|
+
/**
|
|
2656
|
+
* A pack's hook throwing on one file.
|
|
2657
|
+
*
|
|
2658
|
+
* The run continues with the other files, so every count for that pack
|
|
2659
|
+
* afterwards is a floor rather than a total. Somebody reading those
|
|
2660
|
+
* counts has to be told, or a pack that broke looks the same as a pack
|
|
2661
|
+
* that looked and found nothing.
|
|
2662
|
+
*/
|
|
2663
|
+
interface PackFailure {
|
|
2664
|
+
/** The hook that threw, called what a pack author would call it. */
|
|
2665
|
+
hook: string;
|
|
2666
|
+
/** The file the pack was reading. */
|
|
2667
|
+
file: string;
|
|
2668
|
+
message: string;
|
|
2669
|
+
}
|
|
2670
|
+
/** Per-pack running counts, filled as the extract proceeds. */
|
|
2671
|
+
interface PackTally {
|
|
2672
|
+
candidateFiles: number;
|
|
2673
|
+
unitsDiscovered: number;
|
|
2674
|
+
unitsInGatedFiles: number;
|
|
2675
|
+
effectsRecognized: number;
|
|
2676
|
+
unitsClaimed: number;
|
|
2677
|
+
selfCollisions: number;
|
|
2678
|
+
summariesProduced: number;
|
|
2679
|
+
failures: PackFailure[];
|
|
2680
|
+
/** Registration helpers from this pack's config that produced a unit. */
|
|
2681
|
+
helpersMatched: Set<string>;
|
|
2682
|
+
}
|
|
2683
|
+
declare const emptyTally: () => PackTally;
|
|
2684
|
+
/**
|
|
2685
|
+
* Record that a pack's hook threw, and phrase it in one sentence a caller
|
|
2686
|
+
* can print. Both callers want the same wording, and a failure that only
|
|
2687
|
+
* reached stderr left the counts looking like an empty pack.
|
|
2688
|
+
*/
|
|
2689
|
+
declare function recordPackFailure(tally: PackTally | undefined, failure: {
|
|
2690
|
+
pack: string;
|
|
2691
|
+
hook: string;
|
|
2692
|
+
file: string;
|
|
2693
|
+
error: unknown;
|
|
2694
|
+
}): string;
|
|
2695
|
+
declare function createPackTallies(packs: ReadonlyArray<{
|
|
2696
|
+
name: string;
|
|
2697
|
+
}>): Map<string, PackTally>;
|
|
2698
|
+
/** Credit the pack whose name a raw unit's boundary binding gives as `recognition`. */
|
|
2699
|
+
declare function tallyUnit(tallies: Map<string, PackTally>, recognition: string | undefined): void;
|
|
2700
|
+
/**
|
|
2701
|
+
* The summary-side funnel counts, per pack.
|
|
2702
|
+
*
|
|
2703
|
+
* These are read back off the finished run rather than tallied during
|
|
2704
|
+
* it, because the summaries a pack is responsible for are not all built
|
|
2705
|
+
* where the pack is in scope: wrapper expansion and sub-unit synthesis
|
|
2706
|
+
* both add summaries after discovery has moved on. Every summary
|
|
2707
|
+
* records what recognised it, so grouping on that gets each one back to
|
|
2708
|
+
* the pack that owns it however late it arrived.
|
|
2709
|
+
*/
|
|
2710
|
+
interface SummaryCounts {
|
|
2711
|
+
bound: number;
|
|
2712
|
+
providers: number;
|
|
2713
|
+
withBehavior: number;
|
|
2714
|
+
}
|
|
2715
|
+
declare function summaryCountsByPack(summaries: ReadonlyArray<BehavioralSummary>): Map<string, SummaryCounts>;
|
|
2716
|
+
/**
|
|
2717
|
+
* An extraction report for an adapter with no gate stage of its own,
|
|
2718
|
+
* the way TypeScript reports an ungated pack: every file is a
|
|
2719
|
+
* candidate, and `unitsClaimed` tracks `unitsDiscovered` one for one
|
|
2720
|
+
* because nothing dedups across packs yet.
|
|
2721
|
+
*/
|
|
2722
|
+
declare function buildUngatedExtractionReport(args: {
|
|
2723
|
+
packs: ReadonlyArray<{
|
|
2724
|
+
name: string;
|
|
2725
|
+
version: string | null;
|
|
2726
|
+
discovers: boolean;
|
|
2727
|
+
}>;
|
|
2728
|
+
tallies: ReadonlyMap<string, PackTally>;
|
|
2729
|
+
filesWalked: number;
|
|
2730
|
+
summaries: ReadonlyArray<BehavioralSummary>;
|
|
2731
|
+
}): ExtractionReport;
|
|
2732
|
+
|
|
2733
|
+
/**
|
|
2734
|
+
* Checks that tell you a pack is probably not working.
|
|
2735
|
+
*
|
|
2736
|
+
* The funnel shows where a run's counts dropped to zero. These checks
|
|
2737
|
+
* ask something narrower of each pack on its own: did this pack lose
|
|
2738
|
+
* everything at some stage, when the stage before it had something?
|
|
2739
|
+
*
|
|
2740
|
+
* A pack that finds nothing in a codebase that does not use its library
|
|
2741
|
+
* is working correctly, so a count of zero on its own is never the
|
|
2742
|
+
* signal. What makes zero a signal is the count before it. A pack whose
|
|
2743
|
+
* import gate picked forty files and whose discovery then found no unit
|
|
2744
|
+
* in any of them said "look here" and failed to look. Every later stage
|
|
2745
|
+
* works the same way, which is why these checks are one comparison run
|
|
2746
|
+
* over a list of pairs rather than one check written per stage.
|
|
2747
|
+
*/
|
|
2748
|
+
|
|
2749
|
+
/** One thing that looks wrong, reported the way the dogfood invariants are. */
|
|
2750
|
+
interface HealthViolation {
|
|
2751
|
+
label: string;
|
|
2752
|
+
detail: string;
|
|
2753
|
+
}
|
|
2754
|
+
interface HealthCheck {
|
|
2755
|
+
/** The property being checked, as something either true or false. */
|
|
2756
|
+
name: string;
|
|
2757
|
+
/**
|
|
2758
|
+
* The short word this check reports under, kebab-case.
|
|
2759
|
+
*
|
|
2760
|
+
* It is the first column of every line the check prints, so a reader
|
|
2761
|
+
* works out what it means once and recognises it after that. `name`
|
|
2762
|
+
* is written as an assertion and would read backwards over a list of
|
|
2763
|
+
* things failing it.
|
|
2764
|
+
*/
|
|
2765
|
+
code: string;
|
|
2766
|
+
/**
|
|
2767
|
+
* Who the finding is addressed to.
|
|
2768
|
+
*
|
|
2769
|
+
* A `run` check found something about the code in front of it, and
|
|
2770
|
+
* the person who started the run can do something about it: drop a
|
|
2771
|
+
* pack, install a dependency, open an issue with the file that broke.
|
|
2772
|
+
* A `pack` check found something about how a pack was built, which
|
|
2773
|
+
* only whoever ships that pack can fix. Printing the second kind on
|
|
2774
|
+
* every run would teach people to skim past the first.
|
|
2775
|
+
*/
|
|
2776
|
+
audience: "run" | "pack";
|
|
2777
|
+
violations: HealthViolation[];
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* What one pack paid for what it matches.
|
|
2781
|
+
*
|
|
2782
|
+
* Expressiveness is bought link by link, and #542 asks for the price to
|
|
2783
|
+
* be printed. A pack with every link written as data runs on any
|
|
2784
|
+
* adapter with the executor ops. A link written as a function runs only
|
|
2785
|
+
* where its own language does, and one that reads the syntax tree is
|
|
2786
|
+
* the floor. All three are allowed, and this says which is which.
|
|
2787
|
+
*/
|
|
2788
|
+
interface PackGradient {
|
|
2789
|
+
pack: string;
|
|
2790
|
+
/** Links written as data, across every declaration. */
|
|
2791
|
+
dataLinks: number;
|
|
2792
|
+
/** Links written as a function, as "declaration.question". */
|
|
2793
|
+
functionLinks: string[];
|
|
2794
|
+
/** Links whose function reads the adapter's own syntax tree. */
|
|
2795
|
+
astLinks: string[];
|
|
2796
|
+
/** Declarations shipped without a line of code to run against them. */
|
|
2797
|
+
withoutExample: string[];
|
|
2798
|
+
}
|
|
2799
|
+
/** The gradient for every pack in a run that declared anything. */
|
|
2800
|
+
declare function packGradients(report: ExtractionReport): PackGradient[];
|
|
2801
|
+
/**
|
|
2802
|
+
* Run every health check over one extraction report and return what
|
|
2803
|
+
* fired, grouped by which check caught it.
|
|
2804
|
+
*/
|
|
2805
|
+
declare function evaluatePackHealth(report: ExtractionReport): HealthCheck[];
|
|
2806
|
+
/**
|
|
2807
|
+
* The health checks that fired, as lines for a terminal.
|
|
2808
|
+
*
|
|
2809
|
+
* `audiences` is who the caller is printing for. It is required because
|
|
2810
|
+
* no single choice suits every caller: a CLI run prints what the person
|
|
2811
|
+
* who started it can act on, while a run a pack author is reading wants
|
|
2812
|
+
* both kinds.
|
|
2813
|
+
*/
|
|
2814
|
+
declare function formatPackHealth(checks: ReadonlyArray<HealthCheck>, audiences: ReadonlyArray<HealthCheck["audience"]>): string;
|
|
2815
|
+
|
|
2816
|
+
/**
|
|
2817
|
+
* Lightweight phase-timing instrumentation.
|
|
2818
|
+
*
|
|
2819
|
+
* Measures wall time spent in named phases of an adapter run so the
|
|
2820
|
+
* CLI can surface "extract took 17s, of which 11s was in the
|
|
2821
|
+
* reachable closure" without engaging a profiler. Cheap enough to
|
|
2822
|
+
* always be on; a phase that isn't entered contributes zero.
|
|
2823
|
+
*
|
|
2824
|
+
* Not OpenTelemetry: this is single-process, stdout-bound, and should
|
|
2825
|
+
* not ship transitively to consumers. If suss ever becomes a
|
|
2826
|
+
* long-running daemon (LSP / file-watcher), revisit then.
|
|
2827
|
+
*/
|
|
2828
|
+
interface TimingReport {
|
|
2829
|
+
totalMs: number;
|
|
2830
|
+
phases: Array<{
|
|
2831
|
+
label: string;
|
|
2832
|
+
durationMs: number;
|
|
2833
|
+
calls: number;
|
|
2834
|
+
}>;
|
|
2835
|
+
}
|
|
2836
|
+
interface Timer {
|
|
2837
|
+
/** Run `fn`, accumulate wall time under `label`, return its result. */
|
|
2838
|
+
time<T>(label: string, fn: () => T): T;
|
|
2839
|
+
/** Async variant: same accumulation rule. */
|
|
2840
|
+
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
2841
|
+
/** Snapshot of all accumulated phases, ordered by total time descending. */
|
|
2842
|
+
report(): TimingReport;
|
|
2843
|
+
}
|
|
2844
|
+
/**
|
|
2845
|
+
* Build a fresh timer. Each adapter run gets its own, keeps
|
|
2846
|
+
* concurrent extracts independent (irrelevant today, will matter if
|
|
2847
|
+
* we add a server mode).
|
|
2848
|
+
*/
|
|
2849
|
+
declare function createTimer(): Timer;
|
|
2850
|
+
/** Null implementation for callers that opt out of timing entirely. */
|
|
2851
|
+
declare function noopTimer(): Timer;
|
|
2852
|
+
|
|
2210
2853
|
/**
|
|
2211
2854
|
* The vocabulary a language adapter implements so that any declared
|
|
2212
2855
|
* pack runs on it.
|
|
@@ -2757,4 +3400,4 @@ declare function rawConditionToPredicate(c: RawCondition): Predicate;
|
|
|
2757
3400
|
declare function effectToIR(effect: RawEffect): Effect;
|
|
2758
3401
|
declare function paramToInput(param: RawParameter): Input;
|
|
2759
3402
|
|
|
2760
|
-
export { type AccessRecognizer, type AstCapableOps, type BindingExtraction, type BodyContent, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type ExitKind, type ExtractorOptions, type FailureDelivery, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_PATHS, type NodeVisitor, type OpsCarrier, type PackDeclarations, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, composeWrappers, configuredCallOption, detectGaps, effectToIR, enumerateOrDegrade, enumerateStructuredPaths, firstWrittenReading, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, paramToInput, rawConditionToPredicate, registrationHelperDiscovery, routeHelperIndex, runsBefore, scopeOption, sharedGatingConditions, storageSystemOption, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
|
|
3403
|
+
export { type AccessRecognizer, type AdapterCodeStamp, type AdapterStamp, type AstCapableOps, type BindingExtraction, type BodyContent, type CacheAttribution, type CacheDiagnostic, type CacheInput, type CacheLayer, type CacheLookup, type CallOps, type CaseGroup, type ChannelSource, type ChosenReading, type ConditionHandle, type ConditionInfo, type ConditionSource, type ConstructedFrom, type ContractPattern, type DeclaredBinding, type DeclaredBy, type DeclaredMatch, type DefaultedReading, type DiscoveredCustomUnit, type DiscoveredSubUnit, type DiscoveredSubUnitParent, type DiscoveryMatch, type DiscoveryPattern, type EffectArg, type EmptyStage, type ExitKind, type ExtractionReport, type ExtractorOptions, type FailureDelivery, type HealthCheck, type HealthViolation, type HelperDeclarations, type HelperSearch, type HelperSink, type HelperValue, IdMap, IdSet, type Identified, type InputMappingPattern, type InputRead, type InvocationRecognizer, type LanguageAdapter, type LoweredStatementParts, MAX_ENTRIES, MAX_PATHS, type ModuleInitOptions, type NodeVisitor, type OpsCarrier, type PackDeclarations, type PackFailure, type PackFunnel, type PackGradient, type PackTally, type PartialPlan, PathBudgetExceeded, type PatternPack, type ProjectHelper, type ProjectHelpers, type RawBranch, type RawCodeStructure, type RawCondition, type RawDeclaredContract, type RawDependencyCall, type RawEffect, type RawParameter, type RawTerminal, type Reading, type ReceiverOrigin, type RegistrationHelper, type ResponsePropertyMapping, type ResponsePropertyMeaning, type RootRecord, SKIP_CHILDREN, type SourceRange, type StatementBlock, type StructuredPathConditionsInput, type StructuredPathConditionsResult, type StructuredStatement, type TerminalExtraction, type TerminalMatch, type TerminalPattern, type Timer, type TimingReport, type TransparentWrapper, UnmodeledFlow, type UnsettledName, type ValueEntry, type ValueOps, type WalkableNode, absentReading, ambiguousReading, andThenReading, assembleSummary, assessConfidence, buildUngatedExtractionReport, composeWrappers, computeContentHash, computeDistHashFrom, configuredCallOption, createAdapterStamp, createCacheLayer, createPackTallies, createTimer, detectGaps, effectToIR, emptyTally, enumerateOrDegrade, enumerateStructuredPaths, evaluatePackHealth, firstWrittenReading, formatPackHealth, guardsHoldOn, httpRouteDiscovery, makeTransitionId, mapReading, moduleInitStructure, noopTimer, packGradients, paramToInput, projectFileStamp, rawConditionToPredicate, recordPackFailure, registrationHelperDiscovery, routeHelperIndex, runDigest, runsBefore, scopeOption, sharedGatingConditions, stampModuleImports, storageSystemOption, summaryCountsByPack, tallyUnit, terminalToOutput, unreadableReading, unwrapJsonStringify, valueToReadFurtherFrom, walkDescendants, wrapperDiscovery, writtenReading };
|