carveout 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/AGENTS.md +21 -3
  2. package/NOTICE.md +2 -1
  3. package/README.md +248 -473
  4. package/dist/src/account.d.ts +37 -0
  5. package/dist/src/account.js +275 -0
  6. package/dist/src/api.d.ts +148 -0
  7. package/dist/src/api.js +120 -0
  8. package/dist/src/carve.d.ts +788 -0
  9. package/dist/src/carve.js +1721 -125
  10. package/dist/src/cli.d.ts +10 -0
  11. package/dist/src/cli.js +674 -47
  12. package/dist/src/command.d.ts +10 -0
  13. package/dist/src/core.d.ts +11 -0
  14. package/dist/src/cut.d.ts +78 -0
  15. package/dist/src/cut.js +25 -7
  16. package/dist/src/extract/compiler-config.d.ts +49 -0
  17. package/dist/src/extract/fixture.d.ts +103 -0
  18. package/dist/src/extract/hash.d.ts +6 -0
  19. package/dist/src/extract/package-registry.d.ts +47 -0
  20. package/dist/src/extract/package-types.d.ts +52 -0
  21. package/dist/src/extract/surface.d.ts +44 -0
  22. package/dist/src/extract/typescript.d.ts +180 -0
  23. package/dist/src/gate.d.ts +55 -0
  24. package/dist/src/gate.js +1 -0
  25. package/dist/src/graph.d.ts +142 -0
  26. package/dist/src/graph.js +4 -3
  27. package/dist/src/impact.d.ts +113 -0
  28. package/dist/src/imports.d.ts +67 -0
  29. package/dist/src/mergeback.d.ts +182 -0
  30. package/dist/src/mergeback.js +580 -35
  31. package/dist/src/platform.d.ts +69 -0
  32. package/dist/src/platform.js +184 -0
  33. package/dist/src/policy.d.ts +181 -0
  34. package/dist/src/policy.js +716 -230
  35. package/dist/src/project/build.d.ts +23 -0
  36. package/dist/src/project/catalog.d.ts +25 -0
  37. package/dist/src/project/project.d.ts +287 -0
  38. package/dist/src/project/project.js +479 -52
  39. package/dist/src/project/provenance.d.ts +73 -0
  40. package/dist/src/project/provenance.js +12 -0
  41. package/dist/src/project/runner.d.ts +19 -0
  42. package/dist/src/project/secrets.d.ts +64 -0
  43. package/dist/src/project/vitest-config.d.ts +70 -0
  44. package/dist/src/project/vitest-config.js +291 -0
  45. package/dist/src/pseudonyms/aliases.d.ts +107 -0
  46. package/dist/src/pseudonyms/aliases.js +394 -0
  47. package/dist/src/pseudonyms/draft.d.ts +64 -0
  48. package/dist/src/pseudonyms/draft.js +284 -0
  49. package/dist/src/pseudonyms/folder.d.ts +172 -0
  50. package/dist/src/pseudonyms/folder.js +527 -0
  51. package/dist/src/pseudonyms/index.d.ts +11 -0
  52. package/dist/src/pseudonyms/index.js +11 -0
  53. package/dist/src/pseudonyms/vocabulary.d.ts +113 -0
  54. package/dist/src/pseudonyms/vocabulary.js +522 -0
  55. package/dist/src/readiness.d.ts +71 -0
  56. package/dist/src/readiness.js +131 -0
  57. package/dist/src/receipt.d.ts +154 -0
  58. package/dist/src/receipt.js +296 -0
  59. package/dist/src/sandbox.d.ts +53 -0
  60. package/dist/src/sandbox.js +154 -0
  61. package/dist/src/slice/analysis.d.ts +75 -0
  62. package/dist/src/slice/externals.d.ts +51 -0
  63. package/dist/src/slice/graph.d.ts +69 -0
  64. package/dist/src/slice/manifest.d.ts +262 -0
  65. package/dist/src/slice/manifest.js +4 -3
  66. package/dist/src/slice/reach.d.ts +204 -0
  67. package/dist/src/slice/reach.js +11 -5
  68. package/dist/src/slice/report.d.ts +49 -0
  69. package/dist/src/slice/report.js +5 -2
  70. package/dist/src/specifiers.d.ts +48 -0
  71. package/dist/src/status.d.ts +46 -0
  72. package/dist/src/status.js +3 -2
  73. package/dist/src/test-integrity.d.ts +54 -0
  74. package/dist/src/test-integrity.js +812 -0
  75. package/package.json +24 -5
  76. package/skills/carveout/SKILL.md +517 -0
  77. package/skills/carveout-gate/SKILL.md +98 -0
  78. package/SKILL.md +0 -423
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Runs a tool to completion and returns its stdout. `env` is added to a
3
+ * minimal base, so nothing of the caller's environment but PATH and HOME
4
+ * passes through; an abort kills the whole process group.
5
+ */
6
+ export declare function command(executable: string, args: readonly string[], options: {
7
+ signal: AbortSignal;
8
+ cwd?: string;
9
+ env?: Record<string, string>;
10
+ }): Promise<string>;
@@ -0,0 +1,11 @@
1
+ /** The pure rules the extractor reads, from one place. */
2
+ export * from "./slice/analysis.js";
3
+ export * from "./slice/graph.js";
4
+ export * from "./slice/reach.js";
5
+ export * from "./slice/report.js";
6
+ export * from "./slice/externals.js";
7
+ export * from "./slice/manifest.js";
8
+ export * from "./project/provenance.js";
9
+ export * from "./project/secrets.js";
10
+ export * from "./project/build.js";
11
+ export * from "./project/project.js";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The cut: from the checkout's import graph and its files, the files a
3
+ * slice needs and the shape of their boundary.
4
+ *
5
+ * Walks the graph with `reach`, reads the included files, extracts
6
+ * signatures for the cut modules and the public surface (TypeScript and
7
+ * JavaScript; other languages get names only), compiles the fixture with
8
+ * the stubs in place, and assembles the manifest and the contract. A pure
9
+ * function of the graph, the source bytes and the request, apart from the
10
+ * package types it may fetch.
11
+ */
12
+ import type { BoundaryContract, ReachResult, SliceExternals, SliceGraph, SliceManifest, SliceParams } from "./core.js";
13
+ import { type PackageTypesFetcher } from "./extract/package-types.js";
14
+ /** Why a cut could not be made at all, as opposed to a blocker it reports. */
15
+ export declare class CutError extends Error {
16
+ }
17
+ /** Bytes of included source one slice may copy. */
18
+ export declare const INCLUDED_BYTES_MAX: number;
19
+ /** Everything a slice of one request is, before anything is written. */
20
+ export interface SliceAnalysis {
21
+ readonly manifest: SliceManifest;
22
+ readonly contract: BoundaryContract;
23
+ readonly contractJson: string;
24
+ readonly stubs: readonly {
25
+ path: string;
26
+ module: string;
27
+ text: string;
28
+ }[];
29
+ /** The public surface's declarations and check; see `surfaceFiles`. */
30
+ readonly surface: readonly {
31
+ path: string;
32
+ text: string;
33
+ }[];
34
+ readonly ready: boolean;
35
+ /**
36
+ * Whether the slice compiles with the source's noImplicitAny, so the
37
+ * folder can hold its code to it; false when the source has it off.
38
+ */
39
+ readonly keepsImplicitAny: boolean;
40
+ /**
41
+ * The files where the stubs leave an implicit any, when the source holds
42
+ * its code to noImplicitAny: the folder relaxes it for them alone when
43
+ * nothing else in the slice imports them.
44
+ */
45
+ readonly implicitAnyFiles: readonly string[];
46
+ /** The walk the slice was cut from. */
47
+ readonly walk: ReachResult;
48
+ }
49
+ /**
50
+ * Slices one request: walks the graph, reads the included files, extracts
51
+ * the boundary, compiles the fixture and assembles the manifest. A pure
52
+ * function of the graph, the source bytes and the parameters; the slice
53
+ * tool writes its result, and the scope agent checks its proposals with it.
54
+ */
55
+ export declare function analyseSlice(input: {
56
+ readonly sourceDir: string;
57
+ readonly graph: SliceGraph;
58
+ readonly params: SliceParams;
59
+ readonly source: {
60
+ readonly commitSha: string | null;
61
+ /** Whether an included file differs from that commit. */
62
+ readonly isDirty: (paths: readonly string[]) => boolean;
63
+ };
64
+ readonly signal: AbortSignal;
65
+ readonly log: (line: string) => void;
66
+ /**
67
+ * The externals of the included files when an index already has them
68
+ * all, as `detectExternals` would give them; null to read them here.
69
+ */
70
+ readonly externalsOf?: (included: readonly string[]) => SliceExternals | null;
71
+ /**
72
+ * Fetches the types of the public packages the slice reads; without it,
73
+ * or when it fails, every package is untyped.
74
+ */
75
+ readonly packageTypes?: PackageTypesFetcher;
76
+ /** See `extractTypeScriptBoundary`; true unless told otherwise. */
77
+ readonly widenLiterals?: boolean;
78
+ }): Promise<SliceAnalysis>;
package/dist/src/cut.js CHANGED
@@ -12,7 +12,7 @@
12
12
  import { constants, readFileSync } from "node:fs";
13
13
  import { open, readFile } from "node:fs/promises";
14
14
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
15
- import { BOUNDARY_CONTRACT_SCHEMA_VERSION, OPERATION_POLICY, SLICE_MANIFEST_SCHEMA_VERSION, SLICE_TOOL_VERSION, canonicalJson, detectExternals, isRegistryVersion, isSecretPath, pathMatcher, purityRules, reach, sliceReadiness, } from "./core.js";
15
+ import { BOUNDARY_CONTRACT_SCHEMA_VERSION, IMPLICIT_ANY_CODE, OPERATION_POLICY, SLICE_MANIFEST_SCHEMA_VERSION, SLICE_TOOL_VERSION, canonicalJson, detectExternals, isPackageName, isRegistryVersion, isSecretPath, pathMatcher, purityRules, reach, sliceReadiness, } from "./core.js";
16
16
  import { catalogResolver } from "./project/catalog.js";
17
17
  import { testRunnerResolver } from "./project/runner.js";
18
18
  import { workspaceModuleResolver, workspacePackages, } from "./extract/compiler-config.js";
@@ -214,6 +214,7 @@ export async function analyseSlice(input) {
214
214
  : pathMatcher(params.private);
215
215
  const reached = reach(graph, params.entryPoints, params.budget, {
216
216
  tests: params.tests,
217
+ ...(params.setupFiles === undefined || params.setupFiles.length === 0 ? {} : { roots: params.setupFiles }),
217
218
  packageOf: packageFolder(input.sourceDir),
218
219
  runnerOf: testRunnerResolver(input.sourceDir, packageFolder(input.sourceDir)),
219
220
  ...(params.wholePackage === true ? { wholePackage: true } : {}),
@@ -293,7 +294,9 @@ export async function analyseSlice(input) {
293
294
  continue;
294
295
  if (dependency.status === "dynamic" && carriedTests.has(dependency.file))
295
296
  continue;
296
- if (dependency.status !== "dynamic" && isBare(dependency.specifier)) {
297
+ // A path read is a repository path, never a package, however it is
298
+ // spelled (`apps/worker/python/extract.py` is no package named `apps`).
299
+ if (dependency.status !== "dynamic" && dependency.status !== "path" && isBare(dependency.specifier)) {
297
300
  if (isNodeBuiltin(dependency.specifier))
298
301
  continue;
299
302
  if (scripted && workspaceModule(dependency.specifier) !== null)
@@ -314,7 +317,7 @@ export async function analyseSlice(input) {
314
317
  detail: dependency.status === "dynamic"
315
318
  ? "A dynamic import of a computed name cannot be resolved."
316
319
  : dependency.status === "path"
317
- ? `${dependency.specifier ?? ""} is read by path, and is a folder or a kind of file a slice does not copy.`
320
+ ? `${dependency.specifier ?? ""} is read by path, and is a folder or a kind of file a slice does not copy: cut again with --copy ${dependency.specifier ?? "<path>"} to bring it.`
318
321
  : `${dependency.specifier ?? ""} does not resolve to a repository file.`,
319
322
  });
320
323
  }
@@ -326,6 +329,15 @@ export async function analyseSlice(input) {
326
329
  files.add(test);
327
330
  packageFiles.set("vitest", files);
328
331
  }
332
+ // So does a package whose tests run on Vitest when no test of the slice
333
+ // came along: the tests the contributor writes run on it, as the
334
+ // repository's do. Pinned as the entry points' package pins it.
335
+ if (params.tests && params.testRunner === "vitest") {
336
+ const files = packageFiles.get("vitest") ?? new Set();
337
+ for (const entry of walk.entries)
338
+ files.add(entry);
339
+ packageFiles.set("vitest", files);
340
+ }
329
341
  // A file read by path must be there itself; a stub at its path is not it.
330
342
  for (const edge of walk.cuts.outbound)
331
343
  if (edge.relation === "reads" && !copied(edge.to))
@@ -343,6 +355,7 @@ export async function analyseSlice(input) {
343
355
  let surface = [];
344
356
  let configs = [];
345
357
  let keepsImplicitAny = false;
358
+ let implicitAnyFiles = [];
346
359
  let compilation = {
347
360
  attempted: false,
348
361
  ok: false,
@@ -397,7 +410,9 @@ export async function analyseSlice(input) {
397
410
  ...pinned,
398
411
  ...(registry === null ? {} : { registry }),
399
412
  });
400
- if (pinned.version === null)
413
+ // A module that is no package (`virtual:build-info`) is mocked by its
414
+ // name and installed by none: no pin to look for.
415
+ if (pinned.version === null && isPackageName(name))
401
416
  unpinned.push({
402
417
  code: "missing_build_input",
403
418
  file: from === "" ? null : from,
@@ -588,11 +603,13 @@ export async function analyseSlice(input) {
588
603
  const sourceImplicitAny = options["noImplicitAny"] === true ||
589
604
  (options["noImplicitAny"] === undefined && options["strict"] === true);
590
605
  if (sourceImplicitAny && compilation.ok) {
591
- // TS7xxx: the implicit-any family.
592
- const implicit = compile(fixture, stubModules, false).diagnostics.filter(({ code }) => /^(TS)?7\d{3}$/.test(String(code)));
606
+ // The implicit-any family (TS7xxx but the checks that share it).
607
+ const implicitCode = new RegExp(`^(TS)?${IMPLICIT_ANY_CODE}$`);
608
+ const implicit = compile(fixture, stubModules, false).diagnostics.filter(({ code }) => implicitCode.test(String(code)));
593
609
  keepsImplicitAny = implicit.length === 0;
610
+ implicitAnyFiles = [...new Set(implicit.flatMap((d) => (d.file === undefined || d.file === null ? [] : [String(d.file)])))].sort();
594
611
  if (!keepsImplicitAny)
595
- input.log(`noImplicitAny is relaxed in the folder: ${implicit.length} implicit any with the stubs (${implicit
612
+ input.log(`The folder keeps noImplicitAny; its build does not count the ${implicit.length} implicit any(s) the stubs leave (${implicit
596
613
  .slice(0, 3)
597
614
  .map((d) => `${d.file ?? ""}:${d.line ?? ""} ${d.code}`)
598
615
  .join(", ")}).`);
@@ -726,6 +743,7 @@ export async function analyseSlice(input) {
726
743
  surface,
727
744
  ready: readiness.ready,
728
745
  keepsImplicitAny,
746
+ implicitAnyFiles,
729
747
  walk,
730
748
  };
731
749
  }
@@ -0,0 +1,49 @@
1
+ import ts from "typescript";
2
+ export interface LoadedConfig {
3
+ readonly path: string | null;
4
+ readonly options: ts.CompilerOptions;
5
+ readonly rawOptions: Record<string, unknown> | undefined;
6
+ readonly errors: readonly string[];
7
+ }
8
+ /**
9
+ * The `packages:` list of a `pnpm-workspace.yaml`, block or flow style.
10
+ * Read by line: the file is a list of globs, and the worker has no YAML
11
+ * parser to spare.
12
+ */
13
+ export declare function pnpmWorkspacePatterns(text: string): string[];
14
+ /**
15
+ * The repository's own workspace packages, by name, to their directories.
16
+ * A shared base (`@scope/tsconfig/react.json`) is usually one of them, and a
17
+ * snapshot has no node_modules to find it in; and an import of one is the
18
+ * repository's own code, which the slice must never fetch by its name.
19
+ * Every directory a workspace glob names (`workspacePatterns`) that holds a
20
+ * named `package.json`, less those a `!` glob excludes.
21
+ */
22
+ export declare function workspacePackages(root: string): Map<string, string>;
23
+ /**
24
+ * The nearest `tsconfig.json` above a file, read without following `extends`
25
+ * outside the root. A base in an installed package resolves when the
26
+ * package is one of the repository's own workspaces.
27
+ */
28
+ export declare function loadConfig(root: string, file: string,
29
+ /**
30
+ * For a caller loading many files' configurations: the workspaces read
31
+ * once, and each tsconfig loaded once, for that run.
32
+ */
33
+ shared?: {
34
+ readonly packages?: () => ReadonlyMap<string, string>;
35
+ readonly loaded?: Map<string, LoadedConfig>;
36
+ }): LoadedConfig;
37
+ /** The target an `exports` map gives a package subpath, or null. */
38
+ export declare function exportTarget(exports: unknown, subpath: string): string | null;
39
+ /**
40
+ * The repository source a bare specifier of one of the repository's own
41
+ * packages stands for, by path, or null for any other specifier. The
42
+ * package's `exports` (or `types`, `module`, `main`) name what it ships,
43
+ * which is usually a build output the snapshot does not have; a target
44
+ * under the package's `outDir` is read back under its `rootDir`, as the
45
+ * build wrote it from there, and a script target that is not an output is
46
+ * the source itself. Memoized; the resolver never leaves the root.
47
+ */
48
+ export declare function workspaceModuleResolver(root: string, packages: () => ReadonlyMap<string, string>): (specifier: string) => string | null;
49
+ export declare function nearestCompilerOptions(root: string, file: string): Record<string, unknown> | undefined;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The isolated compile that tells whether a slice closes.
3
+ *
4
+ * The included files and the generated stubs are type-checked together in
5
+ * memory, at the paths they hold in the repository, with the source's own
6
+ * compiler settings. Nothing else of the repository is visible, so an
7
+ * import the stubs do not cover is an error here rather than a surprise in
8
+ * the sandbox. Packages are declared as `any` for this check, since the
9
+ * worker installs nothing; the sandbox phase installs the pinned versions.
10
+ * Each name the slice reads of a package is declared as a type as well as
11
+ * a value, so a package's types used in the slice do not fail the check:
12
+ * only what the slice itself leaves unresolved does.
13
+ */
14
+ import ts from "typescript";
15
+ import type { CompilationResult } from "../core.js";
16
+ export declare const DIAGNOSTICS_MAX = 100;
17
+ export declare const SHIM_PATH = "__slice__/packages.d.ts";
18
+ export interface FixtureInput {
19
+ readonly root: string;
20
+ /** Repository-relative path to text: included files, stubs and shims. */
21
+ readonly files: ReadonlyMap<string, string>;
22
+ readonly compilerOptions: ts.CompilerOptions;
23
+ readonly shimmedPackages: readonly string[];
24
+ /** Diagnostic codes not to report: see `UNTYPED_PACKAGE_CODES`. */
25
+ readonly ignoredCodes?: readonly number[];
26
+ /**
27
+ * Bare specifiers of the repository's own packages, to the path in
28
+ * `files` each stands for: no node_modules holds them here.
29
+ */
30
+ readonly modules?: ReadonlyMap<string, string>;
31
+ /** Diagnostics reported at most; defaults to `DIAGNOSTICS_MAX`. */
32
+ readonly limit?: number;
33
+ /**
34
+ * A directory whose node_modules holds fetched package types, read from
35
+ * disk; a package that resolves there is typed, and its shim yields.
36
+ */
37
+ readonly packageTypes?: string | null;
38
+ }
39
+ /**
40
+ * Settings for a compile whose packages are shimmed as `any`. A callback
41
+ * passed to an untyped function has no parameter types to take, which
42
+ * `noImplicitAny` reports though the package's own types would give them.
43
+ */
44
+ export declare const UNTYPED_PACKAGE_OPTIONS: ts.CompilerOptions;
45
+ /**
46
+ * Errors only a shimmed package raises: TS2347, a type argument given to
47
+ * an untyped call (`useState<string>()`).
48
+ */
49
+ export declare const UNTYPED_PACKAGE_CODES: readonly number[];
50
+ /**
51
+ * What the slice reads of one package specifier: the names it imports, and
52
+ * under each the members it reads off it (`React.ComponentProps`,
53
+ * `z.infer`), as deep as it reads them. A default import is `default`.
54
+ */
55
+ export interface PackageUse {
56
+ readonly members: Map<string, PackageUse>;
57
+ }
58
+ export interface PackageUses {
59
+ /** By specifier, as imported. */
60
+ readonly modules: ReadonlyMap<string, PackageUse>;
61
+ /** By global name: `React`. */
62
+ readonly globals: ReadonlyMap<string, PackageUse>;
63
+ }
64
+ /**
65
+ * What the files read of each specifier that `shimmed` says is a package,
66
+ * and of the globals such a package declares (`PACKAGE_GLOBALS`),
67
+ * by the packages' names (`react` covers `react/jsx-runtime`). Read from the
68
+ * syntax alone: a name shadowed in a nested scope may be counted, which
69
+ * only declares one more `any`.
70
+ */
71
+ export declare function packageUses(files: ReadonlyMap<string, string>, shimmed: (specifier: string) => boolean): PackageUses;
72
+ /**
73
+ * Ambient declarations that make the listed packages resolve as `any`, with
74
+ * Node's own modules and globals as the generated project declares them.
75
+ * A specifier in `uses` gets a module of its own that exports each name the
76
+ * slice reads as both a value and a type, so `import type { X }` and
77
+ * `React.ComponentProps<"button">` check as they would with the package's
78
+ * types; any other is untyped, which only works as a value. A global in
79
+ * `uses` is a namespace of the types read off it. A `typed` package, whose
80
+ * fetched types the compile reads, is declared by patterns that yield to
81
+ * them.
82
+ */
83
+ export declare function packageShim(packages: readonly string[], { modules: uses, globals }?: PackageUses, typed?: (name: string) => boolean): string;
84
+ /**
85
+ * The part of `packageShim` that declares what the slice reads: a module
86
+ * for each specifier in `uses` and a namespace for each global. A sliced
87
+ * project declares its packages with these too, so it builds against what
88
+ * the slice was checked against; one it `installed` by a pattern, which
89
+ * yields to the package's own types.
90
+ */
91
+ export declare function packageModules({ modules: uses, globals }: PackageUses, installed?: (specifier: string) => boolean): {
92
+ specifiers: string[];
93
+ text: string;
94
+ };
95
+ /**
96
+ * A compiler message with the checkout's path taken out. Messages name
97
+ * files by absolute path (TS6059 names the project's `rootDir`), and they
98
+ * are written into the folder's record, which may leave the machine: a
99
+ * path under the repository becomes repository-relative, and one under the
100
+ * fetched package types becomes `<package types>/...`.
101
+ */
102
+ export declare function withoutRoots(message: string, root: string, types?: string | null): string;
103
+ export declare function compileFixture(input: FixtureInput): CompilationResult;
@@ -0,0 +1,6 @@
1
+ import type { Stats } from "node:fs";
2
+ /** Git's blob id for these bytes: what `git hash-object` would print. */
3
+ export declare function gitBlobId(bytes: Uint8Array): string;
4
+ export declare function sha256(bytes: Uint8Array | string): string;
5
+ /** The two modes Git records for a regular file. */
6
+ export declare function gitFileMode(stats: Pick<Stats, "mode">): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Where a repository says the packages it imports are fetched from: the
3
+ * URLs its lockfiles resolved them at, and the registries its npm, Yarn
4
+ * and pnpm configuration send their scopes to. These are the facts
5
+ * `resolveScope` in core decides by: a package is installed by its name
6
+ * only when they show it comes from the public registry, since a private
7
+ * registry's package name is anyone's to publish on the public one.
8
+ *
9
+ * Only the repository's own files are read, never a registry: the root
10
+ * `package-lock.json` or `npm-shrinkwrap.json`, `yarn.lock` (classic and
11
+ * Berry), `pnpm-lock.yaml`, `.npmrc`, `.yarnrc`, `.yarnrc.yml`, and the
12
+ * `publishConfig` of the root and workspace manifests. Lockfiles in YAML
13
+ * are read by line, as far as their entries' names and resolutions.
14
+ */
15
+ import type { PackageRegistry } from "../core.js";
16
+ /** Whether a registry or tarball URL is the public registry's. */
17
+ export declare function isPublicRegistry(url: string): boolean;
18
+ /**
19
+ * What the repository at `root` says of each package's registry, as a
20
+ * function of its name: `private` when any of its configuration sends the
21
+ * package's scope, or every package, to another registry, or a lockfile
22
+ * resolved it elsewhere; `public` when a lockfile resolved it from the
23
+ * public registry, or pinned it from a registry that the configuration
24
+ * its own package manager reads names as the public one; null when
25
+ * nothing says.
26
+ *
27
+ * One file naming another registry is enough, whichever manager reads it:
28
+ * Berry ignores `.npmrc` and npm ignores `.yarnrc.yml`, so a public
29
+ * registry in one never outweighs a private one in another.
30
+ */
31
+ export declare function packageRegistries(root: string): (name: string) => PackageRegistry | null;
32
+ /**
33
+ * The integrity each `pnpm-lock.yaml` entry with no tarball URL records,
34
+ * by package name and version. Such an entry came from whatever registry
35
+ * the developer's configuration named, so on its own it shows nothing;
36
+ * its `sha512-` integrity, equal to the one the public registry publishes
37
+ * for the same version, shows it is the public package byte for byte (see
38
+ * `publicByIntegrity`).
39
+ */
40
+ export declare function lockIntegrities(root: string): Map<string, Map<string, string>>;
41
+ /**
42
+ * Whether every version of `name` the lockfile records has the integrity
43
+ * the public registry publishes for it: then the repository installs the
44
+ * public package, whatever registry served it. One mismatch, or a version
45
+ * the registry does not have, and it shows nothing.
46
+ */
47
+ export declare function publicByIntegrity(name: string, versions: ReadonlyMap<string, string>, signal: AbortSignal): Promise<boolean>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The declared types of the public packages a slice reads, for the
3
+ * extractor and the fixture compile.
4
+ *
5
+ * A snapshot has no node_modules, so without these every package is
6
+ * untyped: a value whose type the source infers from one (a zod schema)
7
+ * becomes `any` in its stub, and the sandbox, which installs the package,
8
+ * no longer compiles what the source does. Only packages the repository
9
+ * shows come from the public registry are fetched, at the version its
10
+ * lockfile installed (or the range its package.json pins), with install
11
+ * scripts off; the compiler reads their declaration files and nothing of
12
+ * them runs.
13
+ */
14
+ export interface PackageTypesRequest {
15
+ readonly name: string;
16
+ /** What npm installs it by: a version, a range or an `npm:` alias. */
17
+ readonly spec: string;
18
+ }
19
+ export interface PackageTypes {
20
+ /** A directory whose `node_modules` holds the packages. */
21
+ readonly directory: string;
22
+ /** What was installed under each requested name. */
23
+ readonly packages: readonly {
24
+ name: string;
25
+ version: string;
26
+ }[];
27
+ }
28
+ /** The types, or why there are none: the slice then goes on untyped. */
29
+ export type PackageTypesFetcher = (requests: readonly PackageTypesRequest[], signal: AbortSignal) => Promise<PackageTypes | {
30
+ readonly failed: string;
31
+ }>;
32
+ export declare const PUBLIC_REGISTRY = "https://registry.npmjs.org/";
33
+ /** How long one fetch may take before the slice goes on without types. */
34
+ export declare const PACKAGE_TYPES_TIMEOUT_MS = 120000;
35
+ /**
36
+ * What the repository's npm lockfile installed under `name` at its root,
37
+ * as npm would install it again: `1.2.3`, or `npm:other@1.2.3` for an
38
+ * alias. Null when no npm lockfile says.
39
+ */
40
+ export declare function lockedSpec(root: string, name: string): string | null;
41
+ /** `@types/x` for `x`, `@types/scope__x` for `@scope/x`. */
42
+ export declare function typesPackageOf(name: string): string;
43
+ /**
44
+ * Fetches with npm into `root`, one directory per set of requests, kept
45
+ * for the next slice that reads the same packages. Nothing of the worker's
46
+ * npm configuration applies: the registry is the public one, the user
47
+ * config an empty file, and no credential is in the environment.
48
+ */
49
+ export declare function npmPackageTypes(options: {
50
+ readonly root: string;
51
+ readonly timeoutMs?: number;
52
+ }): PackageTypesFetcher;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The public surface's types, held where the slice can check them.
3
+ *
4
+ * The interface test asserts that each export outside code imports is
5
+ * still there; it cannot see a changed signature, and a slice whose
6
+ * importers live in other packages passes a change that breaks them. So
7
+ * the cut writes, under `carveout/surface/`, the declarations the slice's
8
+ * own modules had when it was cut (a mirror of their paths, each import
9
+ * pointing at the mirror, the folder's stub or the package it named), and
10
+ * `check.ts`, which holds every export outside code uses to that type:
11
+ *
12
+ * - a value must still be assignable to its old type, so every call and
13
+ * read outside code makes still compiles;
14
+ * - an interface or type alias must be assignable both ways, since
15
+ * outside code both reads and builds it.
16
+ *
17
+ * `npm test` type-checks `check.ts` with the folder's own compiler (see
18
+ * `carveout/test.js`) and reports each error as the export whose type
19
+ * changed. Enums, and classes with private or protected members, are
20
+ * nominal: an identical copy of one is not assignable to the original, so
21
+ * they are not checked. A check that fails before any edit (a type that
22
+ * holds such a class) fails at the slice's baseline and is not counted.
23
+ */
24
+ import ts from "typescript";
25
+ export interface SurfaceInput {
26
+ readonly program: ts.Program;
27
+ /** Included modules outside code imports, with the names it takes. */
28
+ readonly surface: ReadonlyMap<string, ReadonlySet<string> | "*">;
29
+ readonly included: ReadonlySet<string>;
30
+ /** Cut modules whose stub the folder holds at their declaration path. */
31
+ readonly stubbed: ReadonlySet<string>;
32
+ /** A specifier's module in the repository, or null for a package. */
33
+ readonly resolve: (specifier: string, from: string) => string | null;
34
+ readonly abs: (path: string) => string;
35
+ }
36
+ export interface SurfaceFile {
37
+ readonly path: string;
38
+ readonly text: string;
39
+ }
40
+ /**
41
+ * The mirror and the check for a slice's public surface; empty when there
42
+ * is nothing to check or a module's declarations cannot be emitted.
43
+ */
44
+ export declare function surfaceFiles(input: SurfaceInput): SurfaceFile[];