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.
- package/AGENTS.md +21 -3
- package/NOTICE.md +2 -1
- package/README.md +248 -473
- package/dist/src/account.d.ts +37 -0
- package/dist/src/account.js +275 -0
- package/dist/src/api.d.ts +148 -0
- package/dist/src/api.js +120 -0
- package/dist/src/carve.d.ts +788 -0
- package/dist/src/carve.js +1721 -125
- package/dist/src/cli.d.ts +10 -0
- package/dist/src/cli.js +674 -47
- package/dist/src/command.d.ts +10 -0
- package/dist/src/core.d.ts +11 -0
- package/dist/src/cut.d.ts +78 -0
- package/dist/src/cut.js +25 -7
- package/dist/src/extract/compiler-config.d.ts +49 -0
- package/dist/src/extract/fixture.d.ts +103 -0
- package/dist/src/extract/hash.d.ts +6 -0
- package/dist/src/extract/package-registry.d.ts +47 -0
- package/dist/src/extract/package-types.d.ts +52 -0
- package/dist/src/extract/surface.d.ts +44 -0
- package/dist/src/extract/typescript.d.ts +180 -0
- package/dist/src/gate.d.ts +55 -0
- package/dist/src/gate.js +1 -0
- package/dist/src/graph.d.ts +142 -0
- package/dist/src/graph.js +4 -3
- package/dist/src/impact.d.ts +113 -0
- package/dist/src/imports.d.ts +67 -0
- package/dist/src/mergeback.d.ts +182 -0
- package/dist/src/mergeback.js +580 -35
- package/dist/src/platform.d.ts +69 -0
- package/dist/src/platform.js +184 -0
- package/dist/src/policy.d.ts +181 -0
- package/dist/src/policy.js +716 -230
- package/dist/src/project/build.d.ts +23 -0
- package/dist/src/project/catalog.d.ts +25 -0
- package/dist/src/project/project.d.ts +287 -0
- package/dist/src/project/project.js +479 -52
- package/dist/src/project/provenance.d.ts +73 -0
- package/dist/src/project/provenance.js +12 -0
- package/dist/src/project/runner.d.ts +19 -0
- package/dist/src/project/secrets.d.ts +64 -0
- package/dist/src/project/vitest-config.d.ts +70 -0
- package/dist/src/project/vitest-config.js +291 -0
- package/dist/src/pseudonyms/aliases.d.ts +107 -0
- package/dist/src/pseudonyms/aliases.js +394 -0
- package/dist/src/pseudonyms/draft.d.ts +64 -0
- package/dist/src/pseudonyms/draft.js +284 -0
- package/dist/src/pseudonyms/folder.d.ts +172 -0
- package/dist/src/pseudonyms/folder.js +527 -0
- package/dist/src/pseudonyms/index.d.ts +11 -0
- package/dist/src/pseudonyms/index.js +11 -0
- package/dist/src/pseudonyms/vocabulary.d.ts +113 -0
- package/dist/src/pseudonyms/vocabulary.js +522 -0
- package/dist/src/readiness.d.ts +71 -0
- package/dist/src/readiness.js +131 -0
- package/dist/src/receipt.d.ts +154 -0
- package/dist/src/receipt.js +296 -0
- package/dist/src/sandbox.d.ts +53 -0
- package/dist/src/sandbox.js +154 -0
- package/dist/src/slice/analysis.d.ts +75 -0
- package/dist/src/slice/externals.d.ts +51 -0
- package/dist/src/slice/graph.d.ts +69 -0
- package/dist/src/slice/manifest.d.ts +262 -0
- package/dist/src/slice/manifest.js +4 -3
- package/dist/src/slice/reach.d.ts +204 -0
- package/dist/src/slice/reach.js +11 -5
- package/dist/src/slice/report.d.ts +49 -0
- package/dist/src/slice/report.js +5 -2
- package/dist/src/specifiers.d.ts +48 -0
- package/dist/src/status.d.ts +46 -0
- package/dist/src/status.js +3 -2
- package/dist/src/test-integrity.d.ts +54 -0
- package/dist/src/test-integrity.js +812 -0
- package/package.json +24 -5
- package/skills/carveout/SKILL.md +517 -0
- package/skills/carveout-gate/SKILL.md +98 -0
- package/SKILL.md +0 -423
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four things carveout does with a checkout: plan a slice, cut it into
|
|
3
|
+
* a folder, check that the folder installs, builds and tests on its own,
|
|
4
|
+
* and turn the edits made there back into a patch for the checkout.
|
|
5
|
+
*
|
|
6
|
+
* The plan and the cut read the checkout and never run its code. The cut
|
|
7
|
+
* may fetch the published types of the packages the slice reads (install
|
|
8
|
+
* scripts off, the public registry only) and resolves the folder's
|
|
9
|
+
* lockfile with npm; `offline` skips both.
|
|
10
|
+
*/
|
|
11
|
+
import { type BoundaryContract, type CoverageGate, type DependencyChoice, type GeneratedFile, type ImportRewrite, type ReachResult, type ResolvedDependency, type SliceBudget, type SliceManifest, type SliceExternals, type SliceExposure, type SliceTestLeft, type TestRunner } from "./core.js";
|
|
12
|
+
import { type Refusal } from "./policy.js";
|
|
13
|
+
import { type SandboxSpec } from "./sandbox.js";
|
|
14
|
+
import { type Readiness } from "./readiness.js";
|
|
15
|
+
import { type AliasRule, type FolderPseudonyms } from "./pseudonyms/index.js";
|
|
16
|
+
import { type Receipt } from "./receipt.js";
|
|
17
|
+
export declare const CARVEOUT_VERSION = "0.1.2";
|
|
18
|
+
export declare const RECORD_PATH = "carveout.json";
|
|
19
|
+
export declare const RECORD_SCHEMA_VERSION = 1;
|
|
20
|
+
export declare const BOUNDARY_PATH = "BOUNDARY.md";
|
|
21
|
+
/** What a coding agent reads first in the folder, loaded without asking. */
|
|
22
|
+
export declare const SLICE_AGENTS_PATH = "AGENTS.md";
|
|
23
|
+
/** Claude Code's name for the same: it imports AGENTS.md. */
|
|
24
|
+
export declare const SLICE_CLAUDE_PATH = "CLAUDE.md";
|
|
25
|
+
/**
|
|
26
|
+
* The folder's AGENTS.md: what an agent needs before its first command.
|
|
27
|
+
* Agents in slices opened BOUNDARY.md and carveout/test.js in most runs,
|
|
28
|
+
* and the README, which says this, in under half (E6c and E8 transcripts):
|
|
29
|
+
* a file the agent's harness loads by itself saves those reads.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderSliceAgents(input: {
|
|
32
|
+
readonly entryPoints: readonly string[];
|
|
33
|
+
readonly testsInFolder: number;
|
|
34
|
+
readonly testsLeft: number;
|
|
35
|
+
/** Whether the folder carries a TASK.md: the work asked for, and when it is accepted. */
|
|
36
|
+
readonly task?: boolean;
|
|
37
|
+
/** Where the package's test script finds its tests, from the folder's root (`packages/core/test/*.test.ts`). */
|
|
38
|
+
readonly testsAt?: string;
|
|
39
|
+
/** The coverage the package's test script holds it to, which the folder's `npm test` holds added files to. */
|
|
40
|
+
readonly coverage?: CoverageGate;
|
|
41
|
+
/** The runner the package's own tests run on, by its test script. */
|
|
42
|
+
readonly runner?: "vitest" | "node:test";
|
|
43
|
+
/** The Prettier the repository formats with, at the version it locks, and the folder's copy of its settings, if any. */
|
|
44
|
+
readonly prettier?: {
|
|
45
|
+
readonly version: string;
|
|
46
|
+
readonly config: string | null;
|
|
47
|
+
};
|
|
48
|
+
/** Whether the package has a lint script, which the folder cannot run. */
|
|
49
|
+
readonly lint?: boolean;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* The packages whose types a file's errors are about: of the packages
|
|
53
|
+
* `candidate` accepts that the file imports, those whose imported names a
|
|
54
|
+
* type names in the declaration around an error's line (`Pick<Stripe.
|
|
55
|
+
* Customer, …>`), or anywhere in the file for an error with no line. A
|
|
56
|
+
* service package imported beside one, for its values alone, is not:
|
|
57
|
+
* installed, it would run against a service the folder has none of.
|
|
58
|
+
*/
|
|
59
|
+
export declare function packagesTypingLines(path: string, text: string, lines: readonly (number | null)[], candidate: (name: string) => boolean): Set<string>;
|
|
60
|
+
/**
|
|
61
|
+
* The runner the entry points' package's tests run on, by its test script:
|
|
62
|
+
* `vitest` (Vitest), `node --test` (node:test), or null when the script
|
|
63
|
+
* says neither or there is none.
|
|
64
|
+
*/
|
|
65
|
+
export declare function packageTestRunner(repo: string, entryPoints: readonly string[]): "vitest" | "node:test" | null;
|
|
66
|
+
/** The folder's TASK.md: the work asked for, in the owner's words, and when it is accepted. */
|
|
67
|
+
export declare function renderTask(input: {
|
|
68
|
+
readonly task: string;
|
|
69
|
+
readonly testsInFolder: readonly string[];
|
|
70
|
+
readonly surface: number;
|
|
71
|
+
/** How many checks of them `npm test` makes, as its last line counts them. */
|
|
72
|
+
readonly surfaceChecks?: number;
|
|
73
|
+
/** The package's own check scripts, by name, for what CI checks. */
|
|
74
|
+
readonly ci?: readonly string[];
|
|
75
|
+
/** Where the package's test script finds its tests, from the folder's root. */
|
|
76
|
+
readonly testsAt?: string;
|
|
77
|
+
/** Whether the owner keeps acceptance tests of their own, which the folder does not hold. */
|
|
78
|
+
readonly ownerTests?: boolean;
|
|
79
|
+
}): string;
|
|
80
|
+
export declare const CONTRACT_PATH = "carveout/contract.json";
|
|
81
|
+
/** The repository owner's settings for every cut of it, at its root. */
|
|
82
|
+
export declare const CONFIG_PATH = "carveout.config.json";
|
|
83
|
+
/**
|
|
84
|
+
* What a repository's owner sets once for every slice cut from it, in
|
|
85
|
+
* `carveout.config.json` at the root: where the cut may follow and what it
|
|
86
|
+
* must keep out. A flag on the command line overrides a boolean here; the
|
|
87
|
+
* lists are joined. Nothing here is read from the slice folder.
|
|
88
|
+
*/
|
|
89
|
+
export interface RepoConfig {
|
|
90
|
+
/** See `SliceRequest.wholePackage`. */
|
|
91
|
+
readonly wholePackage?: boolean;
|
|
92
|
+
/** See `SliceRequest.followWorkspaces`. */
|
|
93
|
+
readonly followWorkspaces?: boolean;
|
|
94
|
+
/** Paths or globs never copied; see `SliceRequest.private`. */
|
|
95
|
+
readonly private?: readonly string[];
|
|
96
|
+
/** Folders copied in whole beside the slice; see `CutRequest.copy`. */
|
|
97
|
+
readonly copy?: readonly string[];
|
|
98
|
+
/** Widen literal types in every stub; see `CutRequest.widenLiterals`. */
|
|
99
|
+
readonly widenLiterals?: boolean;
|
|
100
|
+
/** Every cut is for someone outside; see `CutRequest.public`. */
|
|
101
|
+
readonly public?: boolean;
|
|
102
|
+
/** Copy pure code rather than mock it; see `SliceRequest.copyPure`. */
|
|
103
|
+
readonly copyPure?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Names a public folder must not carry, beyond those carveout finds
|
|
106
|
+
* (the organization's, its products', its customers'): scanned word by
|
|
107
|
+
* word and given pseudonyms. See `pseudonyms/folder.ts`.
|
|
108
|
+
*/
|
|
109
|
+
readonly names?: readonly string[];
|
|
110
|
+
/**
|
|
111
|
+
* Renames the owner chooses, applied to a public folder before the ones
|
|
112
|
+
* carveout drafts: `{ "before": "Acme", "after": "Shop", "kind": "text" }`
|
|
113
|
+
* (kind `identifier`, `text` or `path`).
|
|
114
|
+
*/
|
|
115
|
+
readonly pseudonyms?: readonly AliasRule[];
|
|
116
|
+
/** Names carveout finds that the owner lets a public folder carry (a word the code is about). */
|
|
117
|
+
readonly keepNames?: readonly string[];
|
|
118
|
+
/** The most lines a folder may show; see `SliceRequest.maxExposure`. */
|
|
119
|
+
readonly maxExposureLines?: number;
|
|
120
|
+
/** The most a folder may show, as a percent of the repository's TypeScript lines; see `SliceRequest.maxExposure`. */
|
|
121
|
+
readonly maxExposurePercent?: number;
|
|
122
|
+
}
|
|
123
|
+
/** The owner's settings at the repository's root; empty when there are none. */
|
|
124
|
+
export declare function readRepoConfig(repo: string): RepoConfig;
|
|
125
|
+
/**
|
|
126
|
+
* Whether bytes are text a table may rename: no NUL in their first 8 KiB,
|
|
127
|
+
* and valid UTF-8 throughout. A PDF or an archive in a copied folder is
|
|
128
|
+
* not, whatever its name; decoded and written back it would be corrupted.
|
|
129
|
+
*/
|
|
130
|
+
export declare function isTextContent(bytes: Buffer): boolean;
|
|
131
|
+
/** Assets that are not text: copied and compared as bytes. */
|
|
132
|
+
export declare function isBinaryPath(path: string): boolean;
|
|
133
|
+
/** `~/.carveout`, or `CARVEOUT_HOME`: where fetched package types are kept. */
|
|
134
|
+
export declare function carveoutHome(): string;
|
|
135
|
+
export interface SliceRequest {
|
|
136
|
+
/** The checkout's root: where its lockfile and workspace manifest are. */
|
|
137
|
+
readonly repo: string;
|
|
138
|
+
/** Files or directories, relative to the root. */
|
|
139
|
+
readonly entryPoints: readonly string[];
|
|
140
|
+
readonly budget?: SliceBudget;
|
|
141
|
+
/** Also bring along the tests outside the slice that import it. */
|
|
142
|
+
readonly tests?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Include every file of the entry points' package the walk reaches, at
|
|
145
|
+
* any depth, so the cut falls at package and service boundaries only.
|
|
146
|
+
* Unset: as `carveout.config.json` says, else off.
|
|
147
|
+
*/
|
|
148
|
+
readonly wholePackage?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Follow an import of one of the repository's own packages into its
|
|
151
|
+
* source rather than stubbing the package at the seam. Unset: as the
|
|
152
|
+
* configuration says, else off.
|
|
153
|
+
*/
|
|
154
|
+
readonly followWorkspaces?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Copy the pure code the slice runs (a validator, an error class, a
|
|
157
|
+
* formatter from another module or package) rather than mock it, so its
|
|
158
|
+
* tests run against the real code and do not fail on the mocks before
|
|
159
|
+
* any change. Follows the workspaces unless `followWorkspaces` is false.
|
|
160
|
+
* The copied code is the contributor's to read. Unset: as the
|
|
161
|
+
* configuration says, else off.
|
|
162
|
+
*/
|
|
163
|
+
readonly copyPure?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Paths or globs from the root that are never copied: cut and stubbed
|
|
166
|
+
* wherever reached, their literal values widened. Joined with the
|
|
167
|
+
* configuration's.
|
|
168
|
+
*/
|
|
169
|
+
readonly private?: readonly string[];
|
|
170
|
+
/**
|
|
171
|
+
* Files the cut never reads, as if the checkout had none: the owner's
|
|
172
|
+
* acceptance tests, which no folder may hold nor be shaped by (a test
|
|
173
|
+
* that imports the slice would come along, or make what it imports part
|
|
174
|
+
* of the public surface). Kept in the receipt for later cuts, never in
|
|
175
|
+
* the folder's record.
|
|
176
|
+
*/
|
|
177
|
+
readonly unseen?: readonly string[];
|
|
178
|
+
/**
|
|
179
|
+
* How much of the repository a folder may show: every line it copies
|
|
180
|
+
* (entry points, tests and copied code alike, as `plan`'s "Shows" line
|
|
181
|
+
* counts them), at most `lines`, and at most `percent` of the
|
|
182
|
+
* repository's TypeScript lines (`.d.ts` and `node_modules` left out). A
|
|
183
|
+
* cut past either is written with an `exposure_exceeded` blocker, so a
|
|
184
|
+
* hand-off is not ready. Each unset: as the configuration says
|
|
185
|
+
* (`maxExposureLines`, `maxExposurePercent`), else no bound.
|
|
186
|
+
*/
|
|
187
|
+
readonly maxExposure?: {
|
|
188
|
+
readonly lines?: number;
|
|
189
|
+
readonly percent?: number;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** A folder's lines against the owner's bound, with the repository's TypeScript lines when a percent is asked. */
|
|
193
|
+
export interface ExposureBudget {
|
|
194
|
+
readonly lines?: number;
|
|
195
|
+
readonly percent?: number;
|
|
196
|
+
/** The repository's TypeScript lines, when `percent` is set. */
|
|
197
|
+
readonly repositoryLines?: number;
|
|
198
|
+
/** The folder's lines as a percent of them, when `percent` is set. */
|
|
199
|
+
readonly shownPercent?: number;
|
|
200
|
+
/** What is over, in words; null within the bound. */
|
|
201
|
+
readonly over: string | null;
|
|
202
|
+
}
|
|
203
|
+
/** The repository's TypeScript source lines, as `experiments/hero-exposure.mjs` counts them: tracked `.ts`, `.tsx`, `.mts`, `.cts` files, `.d.ts` and `node_modules` left out. */
|
|
204
|
+
export declare function repositoryTypeScriptLines(repo: string): number;
|
|
205
|
+
/** The folder's exposure against the bound the request settled on; null when there is none. */
|
|
206
|
+
export declare function exposureBudget(repo: string, exposure: SliceExposure, bound: {
|
|
207
|
+
readonly lines?: number;
|
|
208
|
+
readonly percent?: number;
|
|
209
|
+
}): ExposureBudget | null;
|
|
210
|
+
export interface Plan {
|
|
211
|
+
readonly repo: string;
|
|
212
|
+
readonly entryPoints: readonly string[];
|
|
213
|
+
readonly budget: SliceBudget;
|
|
214
|
+
readonly graphFiles: number;
|
|
215
|
+
readonly walk: ReachResult;
|
|
216
|
+
readonly externals: SliceExternals;
|
|
217
|
+
/** Bare specifiers the slice imports, by package name, sorted. */
|
|
218
|
+
readonly packages: readonly string[];
|
|
219
|
+
/** Of those, the repository's own workspace packages, which are stubbed. */
|
|
220
|
+
readonly workspacePackages: readonly string[];
|
|
221
|
+
/** Files a slice never copies: credentials files it reached. */
|
|
222
|
+
readonly secretFiles: readonly string[];
|
|
223
|
+
/** Cut modules, sorted. */
|
|
224
|
+
readonly cutModules: readonly string[];
|
|
225
|
+
/** Of those, the ones the slice imports only types from. */
|
|
226
|
+
readonly typeOnlyCuts: readonly string[];
|
|
227
|
+
/** How the walk was bounded, after the configuration. */
|
|
228
|
+
readonly wholePackage: boolean;
|
|
229
|
+
readonly followWorkspaces: boolean;
|
|
230
|
+
readonly copyPure: boolean;
|
|
231
|
+
readonly private: readonly string[];
|
|
232
|
+
/** The files and lines the cut would copy, by why each is there. */
|
|
233
|
+
readonly exposure: SliceExposure;
|
|
234
|
+
/** The exposure against the owner's bound; absent when none is set. */
|
|
235
|
+
readonly exposureBudget?: ExposureBudget;
|
|
236
|
+
/**
|
|
237
|
+
* The slice's modules that code outside it imports, with the names it
|
|
238
|
+
* takes (`"*"`: the whole module) and the importers, a workspace
|
|
239
|
+
* package's importers by its name included: what a cut's surface check
|
|
240
|
+
* holds to its types. Read from the import graph; the cut's
|
|
241
|
+
* `carveout/contract.json` has the compiler's reading.
|
|
242
|
+
*/
|
|
243
|
+
readonly surface: readonly PlanSurface[];
|
|
244
|
+
/** The runner each test brought along runs on, by file. */
|
|
245
|
+
readonly testRunners: Readonly<Record<string, TestRunner>>;
|
|
246
|
+
}
|
|
247
|
+
/** One module of a plan's public surface. */
|
|
248
|
+
export interface PlanSurface {
|
|
249
|
+
readonly module: string;
|
|
250
|
+
readonly names: readonly string[] | "*";
|
|
251
|
+
readonly importers: readonly string[];
|
|
252
|
+
}
|
|
253
|
+
/** What stops a plan's cut, one line each: entry points that name nothing, imports no slice can carry, credentials files. */
|
|
254
|
+
export declare function planBlockers(plan: Plan): string[];
|
|
255
|
+
/** `purityRules` over the repository's files. */
|
|
256
|
+
export declare function pureRulesFor(repo: string): {
|
|
257
|
+
readonly package: (specifier: string) => boolean;
|
|
258
|
+
readonly sideEffects: (file: string) => boolean;
|
|
259
|
+
};
|
|
260
|
+
/** What a cut of the request would hold, from the import graph alone. */
|
|
261
|
+
export declare function planSlice(request: SliceRequest): Plan;
|
|
262
|
+
export interface CutRequest extends SliceRequest {
|
|
263
|
+
readonly out: string;
|
|
264
|
+
/** Packages to install or to mock, overriding the default. */
|
|
265
|
+
readonly choices?: Readonly<Record<string, DependencyChoice>>;
|
|
266
|
+
/** No network: packages stay untyped and the folder has no lockfile. */
|
|
267
|
+
readonly offline?: boolean;
|
|
268
|
+
/**
|
|
269
|
+
* Widen literal types in stubs (`"live"` to `string`), so no constant's
|
|
270
|
+
* value from a cut module is written; for a folder handed to someone
|
|
271
|
+
* outside. Off by default: the stubs then type-check as the source does.
|
|
272
|
+
*/
|
|
273
|
+
readonly widenLiterals?: boolean;
|
|
274
|
+
/**
|
|
275
|
+
* Service packages kept real rather than mocked (`--live`): installed and
|
|
276
|
+
* typed as the repository has them, and the environment the files that
|
|
277
|
+
* import them read is left out of `carveout.env`, so the shell's values
|
|
278
|
+
* or the code's own defaults apply. For a package whose tests need the
|
|
279
|
+
* service itself: a database driver against a local server.
|
|
280
|
+
*/
|
|
281
|
+
readonly live?: readonly string[];
|
|
282
|
+
/**
|
|
283
|
+
* Folders (or files) copied in whole beside the slice, as paths from the
|
|
284
|
+
* root: what the code reads by a name the walk cannot follow, such as a
|
|
285
|
+
* migrations folder. Their files are neither slice nor generated: the
|
|
286
|
+
* patch carries an edit to one. Joined with the configuration's.
|
|
287
|
+
*/
|
|
288
|
+
readonly copy?: readonly string[];
|
|
289
|
+
/**
|
|
290
|
+
* A folder for someone outside the repository: literals widened unless
|
|
291
|
+
* asked otherwise, and the maintainer's paths left out of what is
|
|
292
|
+
* written, the checkout's location and the files outside the slice that
|
|
293
|
+
* import it (each named by its package folder only). Commands that need
|
|
294
|
+
* the repository then take `--repo`.
|
|
295
|
+
*/
|
|
296
|
+
readonly public?: boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Where to write the maintainer's receipt (`receipt.ts`): a file path,
|
|
299
|
+
* or false for none. By default `CARVEOUT_HOME/receipts/<cut id>.json`.
|
|
300
|
+
* A public folder's own record keeps the repository's names out only
|
|
301
|
+
* when there is a receipt to hold them.
|
|
302
|
+
*/
|
|
303
|
+
readonly receipt?: string | false;
|
|
304
|
+
/**
|
|
305
|
+
* Keep the receipt as data only (`CutResult.receiptData`), written to no
|
|
306
|
+
* file: the library's cut (`api.ts`), whose caller stores it.
|
|
307
|
+
*/
|
|
308
|
+
readonly receiptInMemory?: boolean;
|
|
309
|
+
/** Where fetched package types are kept; by default `CARVEOUT_HOME/types`. */
|
|
310
|
+
readonly typesCache?: string;
|
|
311
|
+
/**
|
|
312
|
+
* More names a public folder must not carry (`--name`), joined with the
|
|
313
|
+
* configuration's. See `pseudonyms/folder.ts`.
|
|
314
|
+
*/
|
|
315
|
+
readonly names?: readonly string[];
|
|
316
|
+
/**
|
|
317
|
+
* False writes a public folder in the repository's own names (the leak
|
|
318
|
+
* scan still runs). On by default for a public cut with a receipt.
|
|
319
|
+
*/
|
|
320
|
+
readonly pseudonyms?: boolean;
|
|
321
|
+
/**
|
|
322
|
+
* The work asked for, in the owner's words: written to the folder's
|
|
323
|
+
* TASK.md with when it is accepted (renamed with the folder in pseudonyms).
|
|
324
|
+
*/
|
|
325
|
+
readonly task?: string;
|
|
326
|
+
/**
|
|
327
|
+
* An earlier cut's pseudonyms (`widen`): a term keeps the placeholder it
|
|
328
|
+
* had where it still fits, so the contributor reads the same names.
|
|
329
|
+
*/
|
|
330
|
+
readonly earlierPseudonyms?: FolderPseudonyms;
|
|
331
|
+
readonly log?: (line: string) => void;
|
|
332
|
+
}
|
|
333
|
+
/** What `carveout.json` holds: the cut, and how to undo its rewrites. */
|
|
334
|
+
export interface CarveoutRecord {
|
|
335
|
+
readonly schemaVersion: typeof RECORD_SCHEMA_VERSION;
|
|
336
|
+
readonly carveoutVersion: string;
|
|
337
|
+
/**
|
|
338
|
+
* The cut's random id: the name of the maintainer's receipt
|
|
339
|
+
* (`receipt.ts`), which holds this record as the cut made it. Absent in
|
|
340
|
+
* records made before receipts.
|
|
341
|
+
*/
|
|
342
|
+
readonly cutId?: string;
|
|
343
|
+
/**
|
|
344
|
+
* A public folder's record, less what names the repository beyond the
|
|
345
|
+
* slice (`publicRecord`): commands that need it read the receipt.
|
|
346
|
+
*/
|
|
347
|
+
readonly publicScrubbed?: boolean;
|
|
348
|
+
/**
|
|
349
|
+
* The checkout the slice was cut from, as given. Absent from a `public`
|
|
350
|
+
* cut, which leaves the maintainer's paths out: commands that need the
|
|
351
|
+
* repository then take `--repo`.
|
|
352
|
+
*/
|
|
353
|
+
readonly repo?: string;
|
|
354
|
+
readonly manifest: SliceManifest;
|
|
355
|
+
readonly dependencies: readonly ResolvedDependency[];
|
|
356
|
+
/**
|
|
357
|
+
* Optional peers of the installed packages the repository installs and
|
|
358
|
+
* the folder does not; `--install <name>` adds one.
|
|
359
|
+
*/
|
|
360
|
+
readonly optionalPeers: readonly {
|
|
361
|
+
name: string;
|
|
362
|
+
version: string;
|
|
363
|
+
of: string;
|
|
364
|
+
}[];
|
|
365
|
+
readonly importRewrites: readonly ImportRewrite[];
|
|
366
|
+
/** Files carveout wrote, by path, with their hashes; never the slice's. */
|
|
367
|
+
readonly generated: readonly {
|
|
368
|
+
path: string;
|
|
369
|
+
sha256: string;
|
|
370
|
+
}[];
|
|
371
|
+
/**
|
|
372
|
+
* The slice's own files as written, imports rewritten, with their hashes:
|
|
373
|
+
* what an edit since the cut is measured against (the coverage gate).
|
|
374
|
+
*/
|
|
375
|
+
/**
|
|
376
|
+
* What the folder holds of each slice file as the cut (or a later apply)
|
|
377
|
+
* wrote it; `applied` marks a file a patch brought in, which no cut of
|
|
378
|
+
* the repository wrote: the folder holding one is edited.
|
|
379
|
+
*/
|
|
380
|
+
readonly copied?: readonly {
|
|
381
|
+
path: string;
|
|
382
|
+
sha256: string;
|
|
383
|
+
applied?: true;
|
|
384
|
+
}[];
|
|
385
|
+
/**
|
|
386
|
+
* The commit a slice file's base is at, where refresh moved it on its
|
|
387
|
+
* own and the record's commit stayed (an overlap elsewhere held it).
|
|
388
|
+
*/
|
|
389
|
+
readonly bases?: Readonly<Record<string, string>>;
|
|
390
|
+
readonly lockfile: boolean;
|
|
391
|
+
readonly blockers: readonly {
|
|
392
|
+
readonly code: string;
|
|
393
|
+
readonly file: string | null;
|
|
394
|
+
readonly detail: string;
|
|
395
|
+
}[];
|
|
396
|
+
readonly ready: boolean;
|
|
397
|
+
/** What the cut was asked, so `status` can ask it again. */
|
|
398
|
+
readonly request: {
|
|
399
|
+
readonly entryPoints: readonly string[];
|
|
400
|
+
readonly budget: SliceBudget;
|
|
401
|
+
readonly choices: Readonly<Record<string, DependencyChoice>>;
|
|
402
|
+
readonly offline: boolean;
|
|
403
|
+
readonly widenLiterals: boolean;
|
|
404
|
+
/** Absent in records made before tests could be brought along. */
|
|
405
|
+
readonly tests?: boolean;
|
|
406
|
+
/** Service packages kept real; absent when none. */
|
|
407
|
+
readonly live?: readonly string[];
|
|
408
|
+
/** The cut's bounds after the configuration; absent when off or none. */
|
|
409
|
+
readonly wholePackage?: boolean;
|
|
410
|
+
readonly followWorkspaces?: boolean;
|
|
411
|
+
readonly copyPure?: boolean;
|
|
412
|
+
readonly private?: readonly string[];
|
|
413
|
+
readonly copy?: readonly string[];
|
|
414
|
+
/** A folder for someone outside; see `CutRequest.public`. */
|
|
415
|
+
readonly public?: boolean;
|
|
416
|
+
/** The work asked for (`--task`), in the repository's names; the receipt's only. */
|
|
417
|
+
readonly task?: string;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Files copied in whole from the `copy` folders, with their hashes: not
|
|
421
|
+
* the slice's, not generated, and in the patch when edited. Absent in
|
|
422
|
+
* records made before folders could be copied.
|
|
423
|
+
*/
|
|
424
|
+
readonly extras?: readonly {
|
|
425
|
+
path: string;
|
|
426
|
+
sha256: string;
|
|
427
|
+
}[];
|
|
428
|
+
/**
|
|
429
|
+
* The repository's files the cut resolved with, beyond the slice: compiler
|
|
430
|
+
* and package configuration, workspace manifests and lockfiles.
|
|
431
|
+
*/
|
|
432
|
+
readonly inputs: readonly {
|
|
433
|
+
path: string;
|
|
434
|
+
sha256: string | null;
|
|
435
|
+
}[];
|
|
436
|
+
/**
|
|
437
|
+
* The tests that run the slice's code: those in the folder, and those
|
|
438
|
+
* left in the repository with the reason (a public cut names each by its
|
|
439
|
+
* package). Absent in records made before the account.
|
|
440
|
+
*/
|
|
441
|
+
readonly tests?: {
|
|
442
|
+
readonly verdict: string;
|
|
443
|
+
readonly carried: readonly string[];
|
|
444
|
+
readonly left: readonly SliceTestLeft[];
|
|
445
|
+
};
|
|
446
|
+
/** What the folder shows of the repository; see `exposureOf`. */
|
|
447
|
+
readonly exposure?: SliceExposure;
|
|
448
|
+
/**
|
|
449
|
+
* What would make the folder's tests run on real code (`readinessOf`).
|
|
450
|
+
* Absent from a public cut, whose advice may name paths the folder does
|
|
451
|
+
* not show, and from records made before it.
|
|
452
|
+
*/
|
|
453
|
+
readonly readiness?: Readiness;
|
|
454
|
+
}
|
|
455
|
+
export interface CutResult {
|
|
456
|
+
readonly out: string;
|
|
457
|
+
readonly record: CarveoutRecord;
|
|
458
|
+
readonly contract: BoundaryContract;
|
|
459
|
+
/** What would make the folder's tests run on real code, public cut or not. */
|
|
460
|
+
readonly readiness: Readiness;
|
|
461
|
+
/** The receipt the cut wrote, or null when asked for none or kept in memory. */
|
|
462
|
+
readonly receipt: string | null;
|
|
463
|
+
/** The receipt as data, written or not; null when asked for none. */
|
|
464
|
+
readonly receiptData: Receipt | null;
|
|
465
|
+
/**
|
|
466
|
+
* A public folder's pseudonyms, as the receipt keeps them (the table and
|
|
467
|
+
* the kept package names); null for a cut in the repository's names.
|
|
468
|
+
*/
|
|
469
|
+
readonly pseudonyms: FolderPseudonyms | null;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* What a cut could change so the folder's tests run on the repository's
|
|
473
|
+
* code instead of mocks (see `readiness.ts`): the pure modules
|
|
474
|
+
* `--copy-pure` would copy, read from a second walk with it on; the mocked
|
|
475
|
+
* database, cache and queue clients `--live` could keep real; the folders
|
|
476
|
+
* the slice's packages read by a configured name, for `--copy`. Only for a
|
|
477
|
+
* cut that carries tests: a folder without them has nothing to turn green.
|
|
478
|
+
*/
|
|
479
|
+
export declare function readinessOf(request: SliceRequest & {
|
|
480
|
+
readonly copy?: readonly string[];
|
|
481
|
+
readonly public?: boolean;
|
|
482
|
+
}, record: Pick<CarveoutRecord, "manifest" | "dependencies" | "tests" | "request">): Readiness;
|
|
483
|
+
export interface LockedPeer {
|
|
484
|
+
readonly name: string;
|
|
485
|
+
readonly version: string;
|
|
486
|
+
/** The installed package whose install tree asks for it. */
|
|
487
|
+
readonly of: string;
|
|
488
|
+
readonly optional: boolean;
|
|
489
|
+
}
|
|
490
|
+
/** The version the repository's npm lockfile installs `name` at, at its root. */
|
|
491
|
+
export declare function lockedVersion(repo: string, name: string): string | null;
|
|
492
|
+
/**
|
|
493
|
+
* A package-lock.json for the folder seeded from the repository's: every
|
|
494
|
+
* package the repository installs, at the version it installs it, the
|
|
495
|
+
* slice's workspaces' own installs over the root's (the first workspace
|
|
496
|
+
* last, so it wins). npm keeps a seeded version that satisfies the range
|
|
497
|
+
* and prunes the rest, so the folder runs what the repository runs rather
|
|
498
|
+
* than what the registry has newest. Null without an npm lockfile.
|
|
499
|
+
*/
|
|
500
|
+
export declare function seededLockfile(repo: string, workspaces: readonly string[], root: Record<string, unknown>): Record<string, unknown> | null;
|
|
501
|
+
/**
|
|
502
|
+
* Takes out of a resolved lock what npm left with nothing to install it
|
|
503
|
+
* under: an entry nested under a package the lock no longer has
|
|
504
|
+
* (`node_modules/vitest/node_modules/magic-string` once `vitest` went),
|
|
505
|
+
* and one npm marked extraneous. npm's `--package-lock-only` marks such
|
|
506
|
+
* entries rather than dropping them, and an npm other than the one that
|
|
507
|
+
* resolved the lock may install them. How many it took out.
|
|
508
|
+
*/
|
|
509
|
+
export declare function pruneLock(lock: {
|
|
510
|
+
packages?: Record<string, Record<string, unknown>>;
|
|
511
|
+
}): number;
|
|
512
|
+
/**
|
|
513
|
+
* Peers of the installed packages, and of what they install, that the
|
|
514
|
+
* repository's npm lockfile installs at its root for production: a
|
|
515
|
+
* package the slice does not import but one it installs needs, at the
|
|
516
|
+
* version the repository runs with. Optional peers are marked; the cut
|
|
517
|
+
* installs only the others unless asked. Empty without a lockfile.
|
|
518
|
+
*/
|
|
519
|
+
export declare function lockedPeers(repo: string, installed: readonly string[]): LockedPeer[];
|
|
520
|
+
/**
|
|
521
|
+
* The version the repository gives `name`: its npm lockfile's, else the
|
|
522
|
+
* pin in the nearest package.json above one of `files`, else the root's.
|
|
523
|
+
*/
|
|
524
|
+
export declare function repositoryVersion(repo: string, name: string, files: readonly string[]): string | null;
|
|
525
|
+
/** A cut held in memory: every file it writes, and its record but the lockfile. */
|
|
526
|
+
export interface ComputedCut {
|
|
527
|
+
readonly files: readonly GeneratedFile[];
|
|
528
|
+
/** Included files copied as bytes. */
|
|
529
|
+
readonly binaries: readonly string[];
|
|
530
|
+
/** The `copy` folders' files, copied as bytes; see `CarveoutRecord.extras`. */
|
|
531
|
+
readonly extras: readonly string[];
|
|
532
|
+
readonly record: Omit<CarveoutRecord, "generated" | "lockfile">;
|
|
533
|
+
readonly contract: BoundaryContract;
|
|
534
|
+
}
|
|
535
|
+
export declare function computeCut(request: Omit<CutRequest, "out">): Promise<ComputedCut>;
|
|
536
|
+
/**
|
|
537
|
+
* The packages whose classes the code tests with `instanceof`, by package
|
|
538
|
+
* name with the class as written: `import Anthropic from
|
|
539
|
+
* "@anthropic-ai/sdk"` and `instanceof Anthropic.APIError` in the same
|
|
540
|
+
* file, or a named or namespace import on the right of one. The
|
|
541
|
+
* repository's own packages and Node's are not among them.
|
|
542
|
+
*/
|
|
543
|
+
export declare function instanceofPackages(sources: readonly {
|
|
544
|
+
path: string;
|
|
545
|
+
text: string;
|
|
546
|
+
}[], workspaces: ReadonlySet<string>): Map<string, string>;
|
|
547
|
+
export declare function cutSlice(request: CutRequest): Promise<CutResult>;
|
|
548
|
+
export declare function readRecord(dir: string): CarveoutRecord;
|
|
549
|
+
/**
|
|
550
|
+
* Throws unless every file the record says the slice holds is one of the
|
|
551
|
+
* repository's, at the bytes the cut read: its hash must be the blob's at
|
|
552
|
+
* the cut's commit, or the checkout's file's (a cut of uncommitted bytes).
|
|
553
|
+
* The record comes back with the folder, and its `included` list decides
|
|
554
|
+
* which files a patch may edit or delete and which packages may gain a new
|
|
555
|
+
* file: an entry added there for a cut module, with any hash, would carry
|
|
556
|
+
* an edit or a deletion of that module past the hand-off policy. Forging
|
|
557
|
+
* one now takes the file's exact bytes, which a slice never shows.
|
|
558
|
+
*/
|
|
559
|
+
export declare function checkRecordFiles(record: CarveoutRecord, repo: string): void;
|
|
560
|
+
export interface CheckStep {
|
|
561
|
+
readonly command: string;
|
|
562
|
+
readonly ok: boolean;
|
|
563
|
+
readonly output: string;
|
|
564
|
+
/** Tests that failed in an unedited folder, recorded as its baseline. */
|
|
565
|
+
readonly baseline?: readonly string[];
|
|
566
|
+
/**
|
|
567
|
+
* The `npm test` step's counts of the folder's tests, read from its TAP:
|
|
568
|
+
* node:test's and Vitest's together, then each runner's, with carveout's
|
|
569
|
+
* own checks of the public surface (exports and types kept) apart.
|
|
570
|
+
*/
|
|
571
|
+
readonly counts?: TestCounts;
|
|
572
|
+
/**
|
|
573
|
+
* Tests the folder's `carveout/baseline.json` names and the receipt's
|
|
574
|
+
* does not: added since the check that recorded it, by whoever had the
|
|
575
|
+
* folder. They were counted.
|
|
576
|
+
*/
|
|
577
|
+
readonly baselineEdited?: readonly string[];
|
|
578
|
+
/** Whether the folder holds an edit since the cut (a folder that came back). */
|
|
579
|
+
readonly edited?: boolean;
|
|
580
|
+
/** The tests `npm test` did not count: the baseline in effect for the run. */
|
|
581
|
+
readonly baselineInEffect?: readonly string[];
|
|
582
|
+
/** Generated files the folder changed, put back from the receipt for the run. */
|
|
583
|
+
readonly restored?: readonly string[];
|
|
584
|
+
/** Packages the folder's package.json added, which the run did not install. */
|
|
585
|
+
readonly addedPackages?: readonly string[];
|
|
586
|
+
/** Whether a receipt vouched for the record and baseline the run read. */
|
|
587
|
+
readonly receiptRead?: boolean;
|
|
588
|
+
}
|
|
589
|
+
/** How many of a folder's tests pass, as `npm test` counted them. */
|
|
590
|
+
export interface TestCounts {
|
|
591
|
+
readonly tests: number;
|
|
592
|
+
readonly pass: number;
|
|
593
|
+
readonly fail: number;
|
|
594
|
+
/** Each runner's: `node:test`, `vitest`. */
|
|
595
|
+
readonly runners?: Readonly<Record<string, {
|
|
596
|
+
readonly tests: number;
|
|
597
|
+
readonly pass: number;
|
|
598
|
+
readonly fail: number;
|
|
599
|
+
}>>;
|
|
600
|
+
/** carveout's own checks that the public surface keeps its exports and their types. */
|
|
601
|
+
readonly surface?: {
|
|
602
|
+
readonly checks: number;
|
|
603
|
+
readonly fail: number;
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
/** The counts the runner wrote as JSON (`COUNTS_PATH`), or undefined when they do not read. */
|
|
607
|
+
export declare function countsFromJson(text: string): TestCounts | undefined;
|
|
608
|
+
/**
|
|
609
|
+
* The counts in a run's TAP: the runner's `# carveout` lines, or, from a
|
|
610
|
+
* folder cut before they were written, node:test's own summary (which
|
|
611
|
+
* counts the interface test's checks among the tests).
|
|
612
|
+
*/
|
|
613
|
+
export declare function countsOf(tap: string): TestCounts | undefined;
|
|
614
|
+
/**
|
|
615
|
+
* Whether a folder holds someone else's work: edited since the cut, by the
|
|
616
|
+
* receipt's hashes. With no receipt it is taken as edited: the folder's own
|
|
617
|
+
* record came back with it and cannot vouch that it is not.
|
|
618
|
+
*/
|
|
619
|
+
export declare function folderCameBack(dir: string, options?: {
|
|
620
|
+
readonly receipt?: string;
|
|
621
|
+
readonly noReceipt?: boolean;
|
|
622
|
+
}): boolean;
|
|
623
|
+
/** The package names the slice's files spell as specifiers, at the cut's commit (or as the checkout holds them). */
|
|
624
|
+
export declare function sliceSpecifierPackages(record: CarveoutRecord, repo: string | null | undefined): string[];
|
|
625
|
+
/**
|
|
626
|
+
* Installs, builds and tests the folder, in a scratch copy so the folder
|
|
627
|
+
* itself is left as it was; stops at the first step that fails.
|
|
628
|
+
*
|
|
629
|
+
* A folder that came back is run as the receipt's cut made it: its
|
|
630
|
+
* generated files (the test runner, the harness, the manifests and the
|
|
631
|
+
* stubs) are put back from the receipt, a file the folder added under
|
|
632
|
+
* `carveout/` is taken out, the baseline read is the receipt's, and a
|
|
633
|
+
* folder with a symbolic link is not run at all. Its code runs, as the
|
|
634
|
+
* caller, with the caller's network: the receipt is put back if the run
|
|
635
|
+
* changed it, and the check fails.
|
|
636
|
+
*/
|
|
637
|
+
export declare function checkProject(dir: string, options?: {
|
|
638
|
+
readonly inPlace?: boolean;
|
|
639
|
+
/**
|
|
640
|
+
* For a folder that came back from someone else: a scratch HOME and npm
|
|
641
|
+
* cache, so the folder's code cannot read the caller's credentials
|
|
642
|
+
* through them, and no install scripts. Not a sandbox: the folder's
|
|
643
|
+
* tests still run with the caller's user, filesystem and network.
|
|
644
|
+
*/
|
|
645
|
+
readonly isolated?: boolean;
|
|
646
|
+
/** The cut's receipt file; else the one `CARVEOUT_HOME` keeps for its id. */
|
|
647
|
+
readonly receipt?: string;
|
|
648
|
+
/** No receipt: the folder's own record and baseline are all there is. */
|
|
649
|
+
readonly noReceipt?: boolean;
|
|
650
|
+
/**
|
|
651
|
+
* The owner's acceptance tests, never sent: each at its repository
|
|
652
|
+
* path, in the repository's names. Put in the check's copy in the
|
|
653
|
+
* folder's names and run with the folder's tests; never recorded in a
|
|
654
|
+
* baseline, and never in the folder itself.
|
|
655
|
+
*/
|
|
656
|
+
readonly accept?: readonly {
|
|
657
|
+
readonly path: string;
|
|
658
|
+
readonly text: string;
|
|
659
|
+
}[];
|
|
660
|
+
/**
|
|
661
|
+
* With `accept`: the slice files patch keeps out, by repository path.
|
|
662
|
+
* The acceptance run puts back the test side's as the cut wrote them
|
|
663
|
+
* and takes out the ones the folder added, so it runs on what patch
|
|
664
|
+
* would carry.
|
|
665
|
+
*/
|
|
666
|
+
readonly held?: readonly string[];
|
|
667
|
+
/**
|
|
668
|
+
* With `held`: when a file patch keeps out would run as the folder
|
|
669
|
+
* holds it (neither a test-side file put back nor an added file taken
|
|
670
|
+
* out), run nothing and say which.
|
|
671
|
+
*/
|
|
672
|
+
readonly refuseHeld?: boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Run every step in a container (`sandbox.ts`): only the scratch copy
|
|
675
|
+
* mounted, the network only for an install with scripts off. For a
|
|
676
|
+
* folder that came back.
|
|
677
|
+
*/
|
|
678
|
+
readonly sandbox?: SandboxSpec;
|
|
679
|
+
}): Promise<readonly CheckStep[]>;
|
|
680
|
+
/** The dependencies one package.json text pins that another does not, as the owner reads them. */
|
|
681
|
+
export declare function packagesAdded(folder: string, cut: string): string[];
|
|
682
|
+
export { undoRewrites } from "./imports.js";
|
|
683
|
+
/** An edit the patch cannot carry: the change reaches past the slice. */
|
|
684
|
+
export interface BoundaryEdit {
|
|
685
|
+
readonly path: string;
|
|
686
|
+
/**
|
|
687
|
+
* `stub`: a cut module's declarations or its mock; `dependency`: the
|
|
688
|
+
* folder's package.json; `config`: its compiler or runtime
|
|
689
|
+
* configuration; `mock`: the mock runtime or a mock package.
|
|
690
|
+
*/
|
|
691
|
+
readonly kind: "stub" | "dependency" | "config" | "mock";
|
|
692
|
+
readonly detail: string;
|
|
693
|
+
/** For `dependency`: the packages the folder added, at the versions it pins. */
|
|
694
|
+
readonly added?: readonly {
|
|
695
|
+
readonly name: string;
|
|
696
|
+
readonly version: string;
|
|
697
|
+
}[];
|
|
698
|
+
}
|
|
699
|
+
export interface Patch {
|
|
700
|
+
/** A unified diff `git apply` takes at the repository's root. */
|
|
701
|
+
readonly diff: string;
|
|
702
|
+
readonly changed: readonly string[];
|
|
703
|
+
readonly added: readonly string[];
|
|
704
|
+
/** Slice files the folder no longer holds, deleted by the patch. */
|
|
705
|
+
readonly deleted: readonly string[];
|
|
706
|
+
/** Generated files that were edited, which the patch leaves out. */
|
|
707
|
+
readonly generatedEdits: readonly string[];
|
|
708
|
+
/** Of those, the edits that mean the change needs more than the slice. */
|
|
709
|
+
readonly boundary: readonly BoundaryEdit[];
|
|
710
|
+
/**
|
|
711
|
+
* New or changed files that import the folder's harness (`carveout/`),
|
|
712
|
+
* which the repository does not have; the patch leaves them out.
|
|
713
|
+
*/
|
|
714
|
+
readonly sliceOnly: readonly string[];
|
|
715
|
+
/** New imports restored as relative paths across packages: no importer names the target. */
|
|
716
|
+
readonly crossPackage: readonly {
|
|
717
|
+
file: string;
|
|
718
|
+
specifier: string;
|
|
719
|
+
}[];
|
|
720
|
+
/** Whether the repository's copy moved since the cut. */
|
|
721
|
+
readonly drifted: readonly string[];
|
|
722
|
+
/**
|
|
723
|
+
* Changed files whose base is no blob of the cut's commit (the cut read
|
|
724
|
+
* uncommitted bytes), so `git apply --3way` cannot merge them.
|
|
725
|
+
*/
|
|
726
|
+
readonly noThreeWay: readonly string[];
|
|
727
|
+
/**
|
|
728
|
+
* What the hand-off policy keeps out of the patch: new files outside the
|
|
729
|
+
* slice's packages, protected paths, deleted tests. `allow` lets them in.
|
|
730
|
+
*/
|
|
731
|
+
readonly refused: readonly Refusal[];
|
|
732
|
+
/**
|
|
733
|
+
* Where the record the patch was judged by came from: the maintainer's
|
|
734
|
+
* receipt, or the folder's own (no receipt found), checked against the
|
|
735
|
+
* repository.
|
|
736
|
+
*/
|
|
737
|
+
readonly recordFrom: "receipt" | "folder";
|
|
738
|
+
/**
|
|
739
|
+
* New files the patch leaves out without judging them: under `carveout/`
|
|
740
|
+
* (the folder's harness, which never goes back) or hidden by the
|
|
741
|
+
* folder's `.gitignore`. Named, since the folder's own tests run them.
|
|
742
|
+
*/
|
|
743
|
+
readonly skipped: readonly string[];
|
|
744
|
+
/**
|
|
745
|
+
* New test files the folder added at a path the repository already holds
|
|
746
|
+
* outside the slice (the owner's acceptance test, kept out of the cut),
|
|
747
|
+
* carried at a free path beside it instead (`contributedPath`), so the
|
|
748
|
+
* patch applies without a conflict and both tests run.
|
|
749
|
+
*/
|
|
750
|
+
readonly renamed?: readonly {
|
|
751
|
+
readonly from: string;
|
|
752
|
+
readonly to: string;
|
|
753
|
+
}[];
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* A free path beside `path` for a contributor's new file that would land on
|
|
757
|
+
* one the repository already holds (the owner's acceptance test, kept out of
|
|
758
|
+
* the cut): `-contributed` before the test suffix, or before the extension
|
|
759
|
+
* (`quote.test.ts` becomes `quote-contributed.test.ts`), numbered past any
|
|
760
|
+
* name `taken` says is in use. Same folder, so its relative imports hold,
|
|
761
|
+
* and the same suffix, so the test script still runs it.
|
|
762
|
+
*/
|
|
763
|
+
export declare function contributedPath(path: string, taken: (candidate: string) => boolean): string;
|
|
764
|
+
export interface PatchOptions {
|
|
765
|
+
/** The repository's checkout; else the one the record names. */
|
|
766
|
+
readonly repo?: string;
|
|
767
|
+
/** Paths and globs the hand-off policy lets through. */
|
|
768
|
+
readonly allow?: readonly string[];
|
|
769
|
+
/** The cut's receipt file; else the one `CARVEOUT_HOME` keeps for its id. */
|
|
770
|
+
readonly receipt?: string;
|
|
771
|
+
/**
|
|
772
|
+
* The cut's receipt as data (`CutResult.receiptData`), in place of a
|
|
773
|
+
* file: no receipt is looked for under `CARVEOUT_HOME` (`api.ts`).
|
|
774
|
+
*/
|
|
775
|
+
readonly receiptData?: Receipt;
|
|
776
|
+
/** No receipt: judge the folder by its own record, checked against the repository. */
|
|
777
|
+
readonly noReceipt?: boolean;
|
|
778
|
+
/** Internal: the folder is a copy already put back in the repository's names. */
|
|
779
|
+
readonly unaliased?: boolean;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* The edits made in the folder as a patch for the repository: each slice
|
|
783
|
+
* file that changed or is gone, and each new file outside `carveout/`, with
|
|
784
|
+
* their imports as the repository writes them (`restoreImports`). Built
|
|
785
|
+
* outputs, the generated files and the lockfile are not part of it; edits
|
|
786
|
+
* to generated files that the change needed are reported as `boundary`.
|
|
787
|
+
*/
|
|
788
|
+
export declare function patchProject(dir: string, options?: PatchOptions): Promise<Patch>;
|