@orkestrel/scaffold 0.0.48 → 0.0.50
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 +4 -2
- package/dist/bin/main.js +427 -97
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +35 -3
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -0
- package/dist/host/claude/agents/orkestrel.md +44 -44
- package/dist/host/claude/rules/quality.md +4 -1
- package/dist/host/claude/rules/tests.md +6 -2
- package/dist/host/guides/scaffold.md +478 -161
- package/dist/host/manifest.json +217 -109
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +129 -6
- package/dist/src/core/index.cjs +1315 -82
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +265 -44
- package/dist/src/core/index.d.ts +265 -44
- package/dist/src/core/index.js +1308 -83
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +816 -152
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +723 -391
- package/dist/src/server/index.d.ts +723 -391
- package/dist/src/server/index.js +812 -156
- package/dist/src/server/index.js.map +1 -1
- package/package.json +10 -9
|
@@ -5,19 +5,21 @@ import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
|
5
5
|
import { EmitterHooks } from '@orkestrel/emitter';
|
|
6
6
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
7
7
|
import { Guard } from '@orkestrel/contract';
|
|
8
|
+
import { HostFile } from '@orkestrel/scaffold';
|
|
9
|
+
import { ManifestRegionSet } from '@orkestrel/scaffold';
|
|
8
10
|
import { Mirror } from '@orkestrel/scaffold';
|
|
9
11
|
import { Plan } from '@orkestrel/scaffold';
|
|
10
12
|
import { Release } from '@orkestrel/scaffold';
|
|
11
13
|
import { Snapshot } from '@orkestrel/scaffold';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
* The Git branch syntax the
|
|
16
|
+
* The Git branch syntax the repository endpoint accepts.
|
|
15
17
|
*
|
|
16
18
|
* @remarks
|
|
17
19
|
* A branch is caller-supplied and reaches a URL path, so it is closed to
|
|
18
20
|
* alphanumerics, dot, underscore, hyphen, and the separator, must open with an
|
|
19
21
|
* alphanumeric, and may carry no `..` anywhere. That last refusal is what stops
|
|
20
|
-
* a branch from walking out of the
|
|
22
|
+
* a branch from walking out of the repository path it addresses.
|
|
21
23
|
*/
|
|
22
24
|
export declare const BRANCH_PATTERN: RegExp;
|
|
23
25
|
|
|
@@ -111,6 +113,80 @@ export declare const DIGEST_PATTERN: RegExp;
|
|
|
111
113
|
*/
|
|
112
114
|
export declare const DRIVE_PATTERN: RegExp;
|
|
113
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Assemble a whole vendored host from live files and the installed floor.
|
|
118
|
+
*
|
|
119
|
+
* @param files - The host-owned vendored files read from the repository, one
|
|
120
|
+
* row per path.
|
|
121
|
+
* @param floor - The installed host floor, which fixes the membership a fill
|
|
122
|
+
* may draw from and supplies the bytes owned by another surface.
|
|
123
|
+
* @returns The assembled host, or `undefined` when any row produced no answer or
|
|
124
|
+
* names a path the floor does not declare, or when a host-owned path is absent.
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
127
|
+
* The one place the host-owned all-or-nothing rule is decided, so no verb
|
|
128
|
+
* restates it. The host surface contributes one baseline: a fill carries live
|
|
129
|
+
* bytes for every path that surface writes, or it is nothing. A row that failed,
|
|
130
|
+
* went missing, names an undeclared path, or leaves a host-owned path absent
|
|
131
|
+
* answers `undefined`. Deferred paths are presence-only and retain the installed
|
|
132
|
+
* floor bytes that their catalog or mirror surface owns; repair never writes
|
|
133
|
+
* those floor bytes. One `Host` can therefore carry live host bytes beside floor
|
|
134
|
+
* bytes without mixing baselines within a surface.
|
|
135
|
+
*
|
|
136
|
+
* The emitted entries keep the release's own order and its storage and
|
|
137
|
+
* executable declarations, and carry digests recomputed over the bytes the fill
|
|
138
|
+
* actually holds. That is what lets a reader verify the value against itself,
|
|
139
|
+
* and it is why an undeclared path is refused rather than added: membership
|
|
140
|
+
* moves with a release, never with a fetch.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { filesToHost } from '@orkestrel/scaffold/server'
|
|
145
|
+
*
|
|
146
|
+
* filesToHost([{ path: 'AGENTS.md', lookup: 'found', hex: '23204167656e74730a' }], floor)
|
|
147
|
+
* // { manifest: { entries: [ … ], roots: [ … ], digest: '…' }, bytes: { 'AGENTS.md': '…' } }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare function filesToHost(files: readonly HostFile[], floor: Host): Host | undefined;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Projects exact bytes stated in hexadecimal to their SHA-256 digest.
|
|
154
|
+
*
|
|
155
|
+
* @param hex - The exact lowercase hexadecimal bytes to digest.
|
|
156
|
+
* @returns Sixty-four lowercase hexadecimal digits.
|
|
157
|
+
* @throws `ScaffoldError('INVALID', …)` when `hex` is not exact bounded
|
|
158
|
+
* lowercase hexadecimal text.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* import { hexToDigest } from '@orkestrel/scaffold/server'
|
|
163
|
+
*
|
|
164
|
+
* hexToDigest('68690a') // '98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4'
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare function hexToDigest(hex: string): string;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* A whole vendored host supplied as a value: the membership beside the bytes filling it.
|
|
171
|
+
*
|
|
172
|
+
* @remarks
|
|
173
|
+
* `manifest` carries the membership, the roots, and the executable declarations
|
|
174
|
+
* the installed release fixed, and each entry's `digest` is the digest of the
|
|
175
|
+
* bytes this value actually holds. `bytes` is keyed by each entry's
|
|
176
|
+
* `destination` and covers exactly the entries the fill holds, so an entry with
|
|
177
|
+
* no bytes and a byte string under no entry are each a refusal rather than a
|
|
178
|
+
* gap a reader has to work around.
|
|
179
|
+
*
|
|
180
|
+
* Membership never moves without a release. A fill therefore states which of
|
|
181
|
+
* the release's paths it carries and never introduces one the release did not
|
|
182
|
+
* declare, which is what keeps a value host's reach identical to the installed
|
|
183
|
+
* root's however the bytes were obtained.
|
|
184
|
+
*/
|
|
185
|
+
export declare interface Host {
|
|
186
|
+
readonly manifest: HostManifest;
|
|
187
|
+
readonly bytes: Snapshot;
|
|
188
|
+
}
|
|
189
|
+
|
|
114
190
|
/**
|
|
115
191
|
* The complete vendored-host inventory.
|
|
116
192
|
*
|
|
@@ -139,10 +215,10 @@ export declare interface HostManifest {
|
|
|
139
215
|
export declare const INVALID_SEGMENT_CHARACTER_PATTERN: RegExp;
|
|
140
216
|
|
|
141
217
|
/**
|
|
142
|
-
* Narrow a value to a Git branch the
|
|
218
|
+
* Narrow a value to a Git branch the repository endpoint accepts.
|
|
143
219
|
*
|
|
144
220
|
* @remarks
|
|
145
|
-
* A branch reaches the
|
|
221
|
+
* A branch reaches the repository URL's path, so the syntax is closed rather than
|
|
146
222
|
* merely bounded and no `..` is admitted anywhere in it.
|
|
147
223
|
*
|
|
148
224
|
* @example
|
|
@@ -272,6 +348,30 @@ export declare function isExactCaseFile(path: string): boolean;
|
|
|
272
348
|
*/
|
|
273
349
|
export declare function isFilesystemPath(value: unknown): value is string;
|
|
274
350
|
|
|
351
|
+
/**
|
|
352
|
+
* Narrow a value to one {@link Host}.
|
|
353
|
+
*
|
|
354
|
+
* @remarks
|
|
355
|
+
* A whole vendored host handed in as a value is as untrusted as one read from a
|
|
356
|
+
* directory a caller named, so both halves are guarded: the manifest by the same
|
|
357
|
+
* membership law a read root is held to, and the bytes by the core snapshot law,
|
|
358
|
+
* which bounds the fill and reads every key as a path and every value as exact
|
|
359
|
+
* lowercase hexadecimal. Whether those halves agree with each other is the
|
|
360
|
+
* reader's question rather than this one's, because a guard has only `false` to
|
|
361
|
+
* say and a mismatch has a path to name.
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```ts
|
|
365
|
+
* import { isHost } from '@orkestrel/scaffold/server'
|
|
366
|
+
*
|
|
367
|
+
* const digest = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
|
368
|
+
*
|
|
369
|
+
* isHost({ manifest: { entries: [], roots: [], digest }, bytes: {} }) // true
|
|
370
|
+
* isHost({ manifest: { entries: [], roots: [], digest } }) // false
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
export declare const isHost: Guard<Host>;
|
|
374
|
+
|
|
275
375
|
/**
|
|
276
376
|
* Narrow a value to one {@link HostManifest}.
|
|
277
377
|
*
|
|
@@ -319,11 +419,34 @@ export declare function isInventory(value: unknown): value is readonly unknown[]
|
|
|
319
419
|
* ```ts
|
|
320
420
|
* import { isManifestEntry } from '@orkestrel/scaffold/server'
|
|
321
421
|
*
|
|
322
|
-
* isManifestEntry({
|
|
422
|
+
* isManifestEntry({
|
|
423
|
+
* storage: 'AGENTS.md',
|
|
424
|
+
* destination: 'AGENTS.md',
|
|
425
|
+
* executable: false,
|
|
426
|
+
* digest: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
|
427
|
+
* }) // true
|
|
323
428
|
* ```
|
|
324
429
|
*/
|
|
325
430
|
export declare const isManifestEntry: Guard<ManifestEntry>;
|
|
326
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Narrow a value to one {@link ManifestRegionSet}.
|
|
434
|
+
*
|
|
435
|
+
* @remarks
|
|
436
|
+
* The whole closed record a manifest-writing method accepts, so a caller
|
|
437
|
+
* naming a region the writer does not carry is refused before any byte moves.
|
|
438
|
+
* Each region is bounded by the same collection law its own list guard applies.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* import { isManifestRegionSet } from '@orkestrel/scaffold/server'
|
|
443
|
+
*
|
|
444
|
+
* isManifestRegionSet({ pins: { runtime: [], development: [] }, scripts: [] }) // true
|
|
445
|
+
* isManifestRegionSet({ pins: { runtime: [], development: [] } }) // false
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
export declare const isManifestRegionSet: Guard<ManifestRegionSet>;
|
|
449
|
+
|
|
327
450
|
/**
|
|
328
451
|
* Narrow a value to the materializer's initial listener record.
|
|
329
452
|
*
|
|
@@ -337,6 +460,12 @@ export declare const isMaterializerHooks: Guard<EmitterHooks<MaterializerEventMa
|
|
|
337
460
|
/**
|
|
338
461
|
* Narrow a value to {@link MaterializerOptions}.
|
|
339
462
|
*
|
|
463
|
+
* @remarks
|
|
464
|
+
* `host` admits both representations of one vendored root: a directory path and
|
|
465
|
+
* a whole {@link Host} value. They share a key because they are one setting
|
|
466
|
+
* stated two ways rather than two settings, so nothing downstream has to
|
|
467
|
+
* reconcile a pair that could disagree.
|
|
468
|
+
*
|
|
340
469
|
* @example
|
|
341
470
|
* ```ts
|
|
342
471
|
* import { isMaterializerOptions } from '@orkestrel/scaffold/server'
|
|
@@ -350,6 +479,25 @@ export declare const isMaterializerOptions: Guard<MaterializerOptions>;
|
|
|
350
479
|
/** Narrow a value to a bounded list of fetched guide mirrors. */
|
|
351
480
|
export declare const isMirrors: Guard<readonly Mirror[]>;
|
|
352
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Narrow a value to a bounded list of target-relative paths.
|
|
484
|
+
*
|
|
485
|
+
* @remarks
|
|
486
|
+
* Composed from the core collection and path guards rather than restated, so
|
|
487
|
+
* the containment law that keeps a caller-supplied path inside its target has
|
|
488
|
+
* exactly one home. It bounds what a caller may hand a public method, which is
|
|
489
|
+
* why it is not {@link isInventory}: that one bounds what a checkout may hold.
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```ts
|
|
493
|
+
* import { isPaths } from '@orkestrel/scaffold/server'
|
|
494
|
+
*
|
|
495
|
+
* isPaths(['AGENTS.md']) // true
|
|
496
|
+
* isPaths(['../secrets']) // false
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
export declare const isPaths: Guard<readonly string[]>;
|
|
500
|
+
|
|
353
501
|
/**
|
|
354
502
|
* Test whether a path is a physical directory this package will read or write into.
|
|
355
503
|
*
|
|
@@ -393,25 +541,6 @@ export declare function isPhysicalDirectory(path: string): boolean;
|
|
|
393
541
|
*/
|
|
394
542
|
export declare function isPhysicalFile(path: string): boolean;
|
|
395
543
|
|
|
396
|
-
/**
|
|
397
|
-
* Narrow a value to a {@link Repository}.
|
|
398
|
-
*
|
|
399
|
-
* @remarks
|
|
400
|
-
* Both path lists are target-relative, so both are measured by the core path
|
|
401
|
-
* law: a reported path that is not one this package could have planned is not a
|
|
402
|
-
* path it will delete. The inventory guard bounds the lists, because a checkout
|
|
403
|
-
* is legitimately far larger than any collection a caller hands a method.
|
|
404
|
-
*
|
|
405
|
-
* @example
|
|
406
|
-
* ```ts
|
|
407
|
-
* import { isRepository } from '@orkestrel/scaffold/server'
|
|
408
|
-
*
|
|
409
|
-
* isRepository({ tracked: ['AGENTS.md'], dirty: [] }) // true
|
|
410
|
-
* isRepository({ tracked: ['../secrets'], dirty: [] }) // false
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
export declare const isRepository: Guard<Repository>;
|
|
414
|
-
|
|
415
544
|
/**
|
|
416
545
|
* Narrow a value to a per-request timeout in milliseconds.
|
|
417
546
|
*
|
|
@@ -447,7 +576,7 @@ export declare const isUpstreamHooks: Guard<EmitterHooks<UpstreamEventMap>>;
|
|
|
447
576
|
* ```ts
|
|
448
577
|
* import { isUpstreamOptions } from '@orkestrel/scaffold/server'
|
|
449
578
|
*
|
|
450
|
-
* isUpstreamOptions({
|
|
579
|
+
* isUpstreamOptions({ repository: { branch: 'main' }, concurrency: 4 }) // true
|
|
451
580
|
* isUpstreamOptions({ concurrency: 0 }) // false
|
|
452
581
|
* ```
|
|
453
582
|
*/
|
|
@@ -476,6 +605,25 @@ export declare const isUpstreamOptions: Guard<UpstreamOptions>;
|
|
|
476
605
|
*/
|
|
477
606
|
export declare function isVacant(target: string): boolean;
|
|
478
607
|
|
|
608
|
+
/**
|
|
609
|
+
* Narrow a value to a {@link Worktree}.
|
|
610
|
+
*
|
|
611
|
+
* @remarks
|
|
612
|
+
* Both path lists are target-relative, so both are measured by the core path
|
|
613
|
+
* law: a reported path that is not one this package could have planned is not a
|
|
614
|
+
* path it will delete. The inventory guard bounds the lists, because a checkout
|
|
615
|
+
* is legitimately far larger than any collection a caller hands a method.
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* import { isWorktree } from '@orkestrel/scaffold/server'
|
|
620
|
+
*
|
|
621
|
+
* isWorktree({ tracked: ['AGENTS.md'], dirty: [] }) // true
|
|
622
|
+
* isWorktree({ tracked: ['../secrets'], dirty: [] }) // false
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
export declare const isWorktree: Guard<Worktree>;
|
|
626
|
+
|
|
479
627
|
/**
|
|
480
628
|
* List a directory's descendant directories as sorted root-relative paths.
|
|
481
629
|
*
|
|
@@ -548,11 +696,19 @@ export declare function listFiles(root: string): readonly string[];
|
|
|
548
696
|
*/
|
|
549
697
|
export declare const MANIFEST_NAME = "manifest.json";
|
|
550
698
|
|
|
551
|
-
/**
|
|
699
|
+
/**
|
|
700
|
+
* One file record of the vendored host's manifest.
|
|
701
|
+
*
|
|
702
|
+
* @remarks
|
|
703
|
+
* `digest` is the SHA-256 of the file's exact bytes. A live read compares it
|
|
704
|
+
* against the target's copy, and verifies host-supplied bytes against it before
|
|
705
|
+
* treating them as that entry.
|
|
706
|
+
*/
|
|
552
707
|
export declare interface ManifestEntry {
|
|
553
708
|
readonly storage: string;
|
|
554
709
|
readonly destination: string;
|
|
555
710
|
readonly executable: boolean;
|
|
711
|
+
readonly digest: string;
|
|
556
712
|
}
|
|
557
713
|
|
|
558
714
|
/**
|
|
@@ -708,7 +864,7 @@ export declare function matchesPrecondition(precondition: WritePrecondition): bo
|
|
|
708
864
|
* directories. It is the inversion the contract asks for: the candidate set
|
|
709
865
|
* is re-derived from the plan and narrowed by what git tracks, and the audit
|
|
710
866
|
* must agree with that derivation rather than supply the set itself.
|
|
711
|
-
*
|
|
867
|
+
* Git metadata is protected because losing history is not a repair,
|
|
712
868
|
* and a target's own `src` and `app` trees are protected because a
|
|
713
869
|
* workspace's source is the one thing scaffold never plans and never owns. A
|
|
714
870
|
* plan the compiler emits never maps a protected root, so this guard exists
|
|
@@ -735,7 +891,7 @@ export declare function matchesProtectedPath(path: string): boolean;
|
|
|
735
891
|
* The vendoring deny-list. A host root is staged from a real checkout, so the
|
|
736
892
|
* refusal is stated over the path rather than over the file's content: a
|
|
737
893
|
* credential is recognizable by where it sits and what it is called long before
|
|
738
|
-
* anything reads it.
|
|
894
|
+
* anything reads it. Git metadata is included through
|
|
739
895
|
* {@link matchesGitPath}, so one call answers the whole question and no caller
|
|
740
896
|
* has to remember to ask twice.
|
|
741
897
|
*
|
|
@@ -768,6 +924,12 @@ export declare function matchesSensitivePath(path: string): boolean;
|
|
|
768
924
|
* case, so a manifest naming `agents.md` for a stored `AGENTS.md` is refused on
|
|
769
925
|
* a case-insensitive filesystem rather than silently resolved.
|
|
770
926
|
*
|
|
927
|
+
* That host arrives as a directory path or as a whole {@link Host} value, and
|
|
928
|
+
* every verb reads one immutable host either way. A value is owned, verified
|
|
929
|
+
* against its own membership and digests, and read in memory; a write fills it
|
|
930
|
+
* into a private root and copies from there, so the executable declarations the
|
|
931
|
+
* release fixed reach the target from either representation.
|
|
932
|
+
*
|
|
771
933
|
* What a mutation guarantees is exactly what {@link WriteTransaction}
|
|
772
934
|
* guarantees, and no more: a caught failure part way through a commit rolls the
|
|
773
935
|
* whole commit back, no destination ever receives half-written bytes, and a
|
|
@@ -794,18 +956,26 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
794
956
|
/**
|
|
795
957
|
* Construct a materializer over one vendored host root.
|
|
796
958
|
*
|
|
797
|
-
* @param options - The vendored host
|
|
798
|
-
* listener-error handler.
|
|
959
|
+
* @param options - The vendored host, in either representation, the initial
|
|
960
|
+
* listeners, and the listener-error handler.
|
|
799
961
|
* @throws {@link ScaffoldError} coded `INVALID` when `options` is present but
|
|
800
962
|
* is not an option bag this materializer accepts, and `TARGET` when the host
|
|
801
|
-
* carries a manifest that cannot be read
|
|
963
|
+
* carries a manifest that cannot be read, does not match what it stores, or
|
|
964
|
+
* is a value that does not agree with the bytes beside it.
|
|
802
965
|
*
|
|
803
966
|
* @remarks
|
|
804
|
-
* `host` defaults to this package's own vendored root, resolved from
|
|
805
|
-
* module's own location so it never depends on the caller's working
|
|
806
|
-
* directory. A
|
|
967
|
+
* A `host` path defaults to this package's own vendored root, resolved from
|
|
968
|
+
* this module's own location so it never depends on the caller's working
|
|
969
|
+
* directory. A root carrying no manifest is read as a raw checkout and every
|
|
807
970
|
* artifact maps onto it one to one.
|
|
808
971
|
*
|
|
972
|
+
* A `host` value is owned before it is read and then held immutable, so the
|
|
973
|
+
* bytes this check measured are the bytes every later read returns. It is
|
|
974
|
+
* verified the way a root is, against the same membership law: the manifest
|
|
975
|
+
* digest must cover the membership beside it, no two entries may claim one
|
|
976
|
+
* destination, and the fill must carry exactly one hashing byte string per
|
|
977
|
+
* declared entry.
|
|
978
|
+
*
|
|
809
979
|
* The host is read here rather than on first use, so a broken vendored root
|
|
810
980
|
* fails at construction where the caller can still act on it, and so nothing
|
|
811
981
|
* has to carry a second flag recording whether the read has happened yet.
|
|
@@ -908,30 +1078,36 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
908
1078
|
*/
|
|
909
1079
|
catalog(entries: readonly CatalogEntry[], target: string): MaterializeResult;
|
|
910
1080
|
/**
|
|
911
|
-
* Rewrite the
|
|
1081
|
+
* Rewrite the manifest regions the caller names in the target's manifest.
|
|
912
1082
|
*
|
|
913
|
-
* @param
|
|
1083
|
+
* @param regions - The dependency ranges and script values the manifest must declare.
|
|
914
1084
|
* @param target - The directory to write into.
|
|
915
|
-
* @returns The manifest path, written when a
|
|
1085
|
+
* @returns The manifest path, written when a named region moved and skipped otherwise.
|
|
916
1086
|
* @throws {@link ScaffoldError} coded `INVALID` when an argument is not the
|
|
917
1087
|
* exact shape or names a package the manifest does not declare, `TARGET` when
|
|
918
1088
|
* the manifest is unreadable, `WRITE` when the write cannot be staged or
|
|
919
1089
|
* committed, and `DESTROYED` after teardown.
|
|
920
1090
|
*
|
|
921
1091
|
* @remarks
|
|
922
|
-
* No other part of the manifest is read back out or rewritten
|
|
923
|
-
*
|
|
924
|
-
* range already declared
|
|
925
|
-
*
|
|
926
|
-
*
|
|
1092
|
+
* No other part of the manifest is read back out or rewritten. The method
|
|
1093
|
+
* never reads or writes `peerDependencies` or `peerDependenciesMeta`. Only a
|
|
1094
|
+
* range already declared in its named writable section is rewritten, so an
|
|
1095
|
+
* undeclared name is refused instead of inserted.
|
|
1096
|
+
*
|
|
1097
|
+
* The regions refuse differently because their targets differ. A range
|
|
1098
|
+
* the manifest does not declare is the caller's mistake and throws. A script
|
|
1099
|
+
* holding a value the region does not accept is the workspace author's own
|
|
1100
|
+
* chain, so the script region is skipped without a byte moving and the range
|
|
1101
|
+
* region is still written. The advisory channel reports what the maintainer
|
|
1102
|
+
* must paste.
|
|
927
1103
|
*/
|
|
928
|
-
declare(
|
|
1104
|
+
declare(regions: ManifestRegionSet, target: string): MaterializeResult;
|
|
929
1105
|
/**
|
|
930
1106
|
* Re-derive and delete the tracked files the plan does not own.
|
|
931
1107
|
*
|
|
932
1108
|
* @param plan - The compiled plan that decides which paths are foreign.
|
|
933
1109
|
* @param audit - The preview returned by this materializer's `audit` method; it must agree with the candidate set this call re-derives.
|
|
934
|
-
* @param
|
|
1110
|
+
* @param worktree - The target's git state; only a tracked path is ever deleted.
|
|
935
1111
|
* @param target - The directory to delete from.
|
|
936
1112
|
* @returns The paths removed.
|
|
937
1113
|
* @throws {@link ScaffoldError} coded `INVALID` when an argument is not the
|
|
@@ -952,7 +1128,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
952
1128
|
* any foreign finding, including one the deletion itself would skip, because a
|
|
953
1129
|
* preview stale anywhere is stale evidence.
|
|
954
1130
|
*/
|
|
955
|
-
remove(plan: Plan, audit: Audit,
|
|
1131
|
+
remove(plan: Plan, audit: Audit, worktree: Worktree, target: string): MaterializeResult;
|
|
956
1132
|
/**
|
|
957
1133
|
* Tear the materializer down. Every later call throws, and teardown is idempotent.
|
|
958
1134
|
*
|
|
@@ -1062,23 +1238,25 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1062
1238
|
*/
|
|
1063
1239
|
catalog(entries: readonly CatalogEntry[], target: string): MaterializeResult;
|
|
1064
1240
|
/**
|
|
1065
|
-
* Rewrite the
|
|
1241
|
+
* Rewrite the manifest regions the caller names in the target's manifest.
|
|
1066
1242
|
*
|
|
1067
|
-
* @param
|
|
1243
|
+
* @param regions - The dependency ranges and script values the manifest must declare.
|
|
1068
1244
|
* @param target - The directory to write into.
|
|
1069
|
-
* @returns The manifest path, written when a
|
|
1245
|
+
* @returns The manifest path, written when a named region moved and skipped otherwise.
|
|
1070
1246
|
*
|
|
1071
1247
|
* @remarks
|
|
1072
|
-
* No other part of the manifest is read back out or rewritten
|
|
1073
|
-
*
|
|
1248
|
+
* No other part of the manifest is read back out or rewritten. The method
|
|
1249
|
+
* never reads or writes `peerDependencies` or `peerDependenciesMeta`. A
|
|
1250
|
+
* script region naming a value the manifest does not accept is skipped
|
|
1251
|
+
* without a byte moving, and the range region is written regardless.
|
|
1074
1252
|
*/
|
|
1075
|
-
declare(
|
|
1253
|
+
declare(regions: ManifestRegionSet, target: string): MaterializeResult;
|
|
1076
1254
|
/**
|
|
1077
1255
|
* Re-derive and delete the tracked files the plan does not own.
|
|
1078
1256
|
*
|
|
1079
1257
|
* @param plan - The compiled plan that decides which paths are foreign.
|
|
1080
1258
|
* @param audit - The preview returned by this materializer's `audit` method; it must agree with the candidate set this call re-derives.
|
|
1081
|
-
* @param
|
|
1259
|
+
* @param worktree - The target's git state; only a tracked path is ever deleted.
|
|
1082
1260
|
* @param target - The directory to delete from.
|
|
1083
1261
|
* @returns The paths removed.
|
|
1084
1262
|
*
|
|
@@ -1101,7 +1279,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1101
1279
|
* any foreign finding, including one the deletion itself would skip, because a
|
|
1102
1280
|
* preview stale anywhere is stale evidence.
|
|
1103
1281
|
*/
|
|
1104
|
-
remove(plan: Plan, audit: Audit,
|
|
1282
|
+
remove(plan: Plan, audit: Audit, worktree: Worktree, target: string): MaterializeResult;
|
|
1105
1283
|
/**
|
|
1106
1284
|
* Tear the materializer down. Every later call throws, and teardown is idempotent.
|
|
1107
1285
|
*
|
|
@@ -1114,19 +1292,21 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1114
1292
|
* Options for the materializer.
|
|
1115
1293
|
*
|
|
1116
1294
|
* @remarks
|
|
1117
|
-
* `host` is the vendored data root host-origin artifacts are copied from
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
* through the manifest.
|
|
1295
|
+
* `host` is the vendored data root host-origin artifacts are copied from, in
|
|
1296
|
+
* either representation. A string is a directory: it defaults to this package's
|
|
1297
|
+
* own vendored root, resolved from the installed module's location rather than
|
|
1298
|
+
* the caller's working directory, and a directory that carries no manifest
|
|
1299
|
+
* beside it maps artifact paths one to one instead of through the manifest. A
|
|
1300
|
+
* {@link Host} is the same root supplied as a value, so a caller that already
|
|
1301
|
+
* holds the bytes never stages them to a directory to be read back.
|
|
1122
1302
|
*/
|
|
1123
1303
|
export declare interface MaterializerOptions {
|
|
1124
|
-
readonly host?: string;
|
|
1304
|
+
readonly host?: string | Host;
|
|
1125
1305
|
readonly on?: EmitterHooks<MaterializerEventMap>;
|
|
1126
1306
|
readonly error?: EmitterErrorHandler;
|
|
1127
1307
|
}
|
|
1128
1308
|
|
|
1129
|
-
/** Maximum characters one
|
|
1309
|
+
/** Maximum characters one repository branch may carry. */
|
|
1130
1310
|
export declare const MAX_BRANCH_LENGTH = 255;
|
|
1131
1311
|
|
|
1132
1312
|
/** Maximum characters one caller-supplied upstream endpoint may carry. */
|
|
@@ -1304,10 +1484,38 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1304
1484
|
*/
|
|
1305
1485
|
export declare function readFileText(root: string, path: string, limit?: number): string | undefined;
|
|
1306
1486
|
|
|
1487
|
+
/**
|
|
1488
|
+
* Reads the installed vendored host floor as a value.
|
|
1489
|
+
*
|
|
1490
|
+
* @param root - The vendored host root. Default: the installed package's
|
|
1491
|
+
* vendored root, resolved from this module's location.
|
|
1492
|
+
* @returns The verified manifest and the exact bytes of every declared entry.
|
|
1493
|
+
* @throws `ScaffoldError('TARGET', …)` when the root is not a readable physical
|
|
1494
|
+
* directory, its manifest is absent or unreadable, the manifest does not verify,
|
|
1495
|
+
* or a declared file is unreadable or misses its digest.
|
|
1496
|
+
*
|
|
1497
|
+
* @remarks
|
|
1498
|
+
* Reads the same default floor the {@link Materializer} uses. Each declared
|
|
1499
|
+
* file is addressed through the manifest's storage name and retained under its
|
|
1500
|
+
* destination, so the returned value has the same shape as the installed root.
|
|
1501
|
+
* When this module executes from TypeScript source, the committed inventory is
|
|
1502
|
+
* the manifest and each checkout destination supplies its bytes. The emitted
|
|
1503
|
+
* module reads the staged `manifest.json` file and each storage path instead.
|
|
1504
|
+
*
|
|
1505
|
+
* @example
|
|
1506
|
+
* ```ts
|
|
1507
|
+
* import { readHostFloor } from '@orkestrel/scaffold/server'
|
|
1508
|
+
*
|
|
1509
|
+
* readHostFloor().manifest // the installed floor's verified membership
|
|
1510
|
+
* ```
|
|
1511
|
+
*/
|
|
1512
|
+
export declare function readHostFloor(root?: string): Host;
|
|
1513
|
+
|
|
1307
1514
|
/**
|
|
1308
1515
|
* Read a vendored host's manifest, when it carries one.
|
|
1309
1516
|
*
|
|
1310
1517
|
* @param host - The vendored host root to read.
|
|
1518
|
+
* @param name - The root-relative manifest path. Default: `manifest.json`.
|
|
1311
1519
|
* @returns The manifest, or `undefined` when the host carries none.
|
|
1312
1520
|
* @throws `ScaffoldError('INVALID', …)` when `host` is not a host path.
|
|
1313
1521
|
* @throws `ScaffoldError('TARGET', …)` when the manifest is there but cannot be
|
|
@@ -1333,7 +1541,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1333
1541
|
* readHostManifest('./dist/host') // the manifest, or undefined for a raw root
|
|
1334
1542
|
* ```
|
|
1335
1543
|
*/
|
|
1336
|
-
export declare function readHostManifest(host: string): HostManifest | undefined;
|
|
1544
|
+
export declare function readHostManifest(host: string, name?: string): HostManifest | undefined;
|
|
1337
1545
|
|
|
1338
1546
|
/**
|
|
1339
1547
|
* Derive one vendored-host manifest entry from a file in a checkout.
|
|
@@ -1359,7 +1567,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1359
1567
|
* import { readManifestEntry } from '@orkestrel/scaffold/server'
|
|
1360
1568
|
*
|
|
1361
1569
|
* readManifestEntry('.gitignore', '/tmp/checkout/.gitignore')
|
|
1362
|
-
* // { storage: 'dotfiles/gitignore', destination: '.gitignore', executable: false }
|
|
1570
|
+
* // { storage: 'dotfiles/gitignore', destination: '.gitignore', executable: false, digest: '...' }
|
|
1363
1571
|
* ```
|
|
1364
1572
|
*/
|
|
1365
1573
|
export declare function readManifestEntry(destination: string, source: string): ManifestEntry | undefined;
|
|
@@ -1395,25 +1603,6 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1395
1603
|
*/
|
|
1396
1604
|
export declare function readSnapshot(target: string, paths: readonly string[]): Snapshot;
|
|
1397
1605
|
|
|
1398
|
-
/**
|
|
1399
|
-
* What git reports about a target's working tree.
|
|
1400
|
-
*
|
|
1401
|
-
* @remarks
|
|
1402
|
-
* `tracked` is the only set a deletion may draw from: git does not report the
|
|
1403
|
-
* loss of an untracked path and `git diff` cannot restore it, so an ignored
|
|
1404
|
-
* path such as an installed dependency tree, a build output, or an editor
|
|
1405
|
-
* directory survives every verb. `dirty` is every path carrying an uncommitted
|
|
1406
|
-
* change, taken repo-wide rather than over a write set, because deletion makes
|
|
1407
|
-
* the write set the whole workspace. A clean tree is an empty `dirty`. A target
|
|
1408
|
-
* that is not a git repository yields no `Repository` at all, so the caller
|
|
1409
|
-
* decides what to do about that rather than reading it out of an invented
|
|
1410
|
-
* empty value.
|
|
1411
|
-
*/
|
|
1412
|
-
export declare interface Repository {
|
|
1413
|
-
readonly tracked: readonly string[];
|
|
1414
|
-
readonly dirty: readonly string[];
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
1606
|
/**
|
|
1418
1607
|
* The Windows device names that stay reserved even when an extension follows.
|
|
1419
1608
|
*
|
|
@@ -1514,6 +1703,42 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1514
1703
|
*/
|
|
1515
1704
|
export declare function resolveRealPath(path: string): string | undefined;
|
|
1516
1705
|
|
|
1706
|
+
/**
|
|
1707
|
+
* Stage the named destinations of a value host into a private root.
|
|
1708
|
+
*
|
|
1709
|
+
* @param host - The host whose bytes are written, keyed by destination.
|
|
1710
|
+
* @param root - The private directory to fill; it must already be a directory
|
|
1711
|
+
* this process may write into.
|
|
1712
|
+
* @param destinations - The destinations to stage, each declared by `host`.
|
|
1713
|
+
* @returns The entry staged for each destination, in the order requested.
|
|
1714
|
+
* @throws `ScaffoldError('INVALID', …)` when `root` is not a host path or a
|
|
1715
|
+
* storage name leaves it.
|
|
1716
|
+
* @throws `ScaffoldError('TARGET', …)` when a destination is one the host does
|
|
1717
|
+
* not declare, carries no bytes, or carries bytes that miss its declared digest.
|
|
1718
|
+
* @throws `ScaffoldError('WRITE', …)` when a file cannot be written or does not
|
|
1719
|
+
* read back as the bytes it was given.
|
|
1720
|
+
*
|
|
1721
|
+
* @remarks
|
|
1722
|
+
* Each file lands under the storage name the manifest declares and takes the
|
|
1723
|
+
* executable bit that manifest records, so a root filled from a value is the
|
|
1724
|
+
* same shape as one staged from a checkout and a reader cannot tell them apart.
|
|
1725
|
+
* That is what lets a mutation copy real files with real modes from bytes a
|
|
1726
|
+
* caller supplied, instead of degrading them to plain text writes.
|
|
1727
|
+
*
|
|
1728
|
+
* The bytes are digested before the write and the staged file after it, so a
|
|
1729
|
+
* value that disagrees with its own manifest is told apart from a write that
|
|
1730
|
+
* did not land.
|
|
1731
|
+
*
|
|
1732
|
+
* @example
|
|
1733
|
+
* ```ts
|
|
1734
|
+
* import { stageBytes } from '@orkestrel/scaffold/server'
|
|
1735
|
+
*
|
|
1736
|
+
* stageBytes(host, '/tmp/orkestrel-host-a1b2', ['scripts/codex.sh'])
|
|
1737
|
+
* // [{ storage: 'scripts/codex.sh', destination: 'scripts/codex.sh', executable: true, digest: '…' }]
|
|
1738
|
+
* ```
|
|
1739
|
+
*/
|
|
1740
|
+
export declare function stageBytes(host: Host, root: string, destinations: readonly string[]): readonly ManifestEntry[];
|
|
1741
|
+
|
|
1517
1742
|
/**
|
|
1518
1743
|
* Stage a vendored host root from a real checkout.
|
|
1519
1744
|
*
|
|
@@ -1568,6 +1793,33 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1568
1793
|
*/
|
|
1569
1794
|
export declare function stageHost(checkout: string, host: string): readonly ManifestEntry[];
|
|
1570
1795
|
|
|
1796
|
+
/**
|
|
1797
|
+
* Stages the committed inventory of the files a vendored host carries.
|
|
1798
|
+
*
|
|
1799
|
+
* @param checkout - The checkout whose vendored paths are inventoried.
|
|
1800
|
+
* @param path - The host path where the JSON inventory is written.
|
|
1801
|
+
* @returns The validated manifest written to `path`.
|
|
1802
|
+
* @throws `ScaffoldError('INVALID', …)` when `path` is not a host path.
|
|
1803
|
+
* @throws `ScaffoldError('WRITE', …)` when a temporary host or the inventory
|
|
1804
|
+
* cannot be written or removed.
|
|
1805
|
+
* @throws `ScaffoldError('TARGET', …)` when the staged inventory does not read
|
|
1806
|
+
* back through the manifest validator.
|
|
1807
|
+
*
|
|
1808
|
+
* @remarks
|
|
1809
|
+
* Uses {@link stageHost} as the single vendored-path expansion. The temporary
|
|
1810
|
+
* host supplies the same entries, roots, per-file digests, and membership
|
|
1811
|
+
* digest as the published host while the requested output remains one JSON
|
|
1812
|
+
* file.
|
|
1813
|
+
*
|
|
1814
|
+
* @example
|
|
1815
|
+
* ```ts
|
|
1816
|
+
* import { stageInventory } from '@orkestrel/scaffold/server'
|
|
1817
|
+
*
|
|
1818
|
+
* stageInventory(process.cwd(), 'host.json') // the committed host inventory
|
|
1819
|
+
* ```
|
|
1820
|
+
*/
|
|
1821
|
+
export declare function stageInventory(checkout: string, path: string): HostManifest;
|
|
1822
|
+
|
|
1571
1823
|
/**
|
|
1572
1824
|
* The reading spine: one bounded, unauthenticated, redirect-free request per answer.
|
|
1573
1825
|
*
|
|
@@ -1586,6 +1838,11 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1586
1838
|
* there is no fleet to report, so an unreachable or malformed list is a coded
|
|
1587
1839
|
* `FETCH` failure.
|
|
1588
1840
|
*
|
|
1841
|
+
* The vendored-file inventory is the other read a whole call rests on, and it
|
|
1842
|
+
* fails the other way: it fails every row of its call rather than throwing, so
|
|
1843
|
+
* the caller receives one whole dead answer it can replace with one whole
|
|
1844
|
+
* baseline instead of a mixture it would have to reconcile.
|
|
1845
|
+
*
|
|
1589
1846
|
* Requests are unauthenticated because every fleet repository is public, and
|
|
1590
1847
|
* they follow no redirect, so a misconfigured or hostile endpoint cannot move a
|
|
1591
1848
|
* read to another host. Each one is bounded by its endpoint's timeout and by the
|
|
@@ -1608,7 +1865,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1608
1865
|
export declare class Upstream implements UpstreamInterface {
|
|
1609
1866
|
#private;
|
|
1610
1867
|
/**
|
|
1611
|
-
* Construct a reader over one
|
|
1868
|
+
* Construct a reader over one raw content host and one registry.
|
|
1612
1869
|
*
|
|
1613
1870
|
* @param options - The endpoints, the request bounds, the initial
|
|
1614
1871
|
* listeners, and the listener-error handler.
|
|
@@ -1684,372 +1941,447 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1684
1941
|
*/
|
|
1685
1942
|
fetch(names: readonly string[], current: Snapshot): Promise<readonly Mirror[]>;
|
|
1686
1943
|
/**
|
|
1687
|
-
*
|
|
1944
|
+
* Read each named vendored file from the repository, beside the target bytes it answers for.
|
|
1688
1945
|
*
|
|
1689
|
-
* @
|
|
1690
|
-
* @
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
*
|
|
1946
|
+
* @param paths - The target-relative vendored paths to read.
|
|
1947
|
+
* @param current - The target files as exact bytes, keyed by the same paths.
|
|
1948
|
+
* @returns One file verdict per path, in input order.
|
|
1949
|
+
* @throws {@link ScaffoldError} coded `INVALID` when `paths` is not a bounded
|
|
1950
|
+
* list of target-relative paths or `current` is not a snapshot, and
|
|
1951
|
+
* `DESTROYED` when the reader is torn down before or during the call.
|
|
1694
1952
|
*
|
|
1695
1953
|
* @remarks
|
|
1696
|
-
* The
|
|
1697
|
-
*
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1700
|
-
*
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
1954
|
+
* The committed inventory is read once per call and decides every row, so a
|
|
1955
|
+
* path whose declared digest already matches the target's own bytes is `found`
|
|
1956
|
+
* without a request and the call spends nothing on it. An inventory that
|
|
1957
|
+
* produces no answer fails every row of the call rather than leaving some rows
|
|
1958
|
+
* live and some dead, which is what leaves the caller one whole baseline to
|
|
1959
|
+
* fall back to. A path the inventory does not name is `missing`.
|
|
1960
|
+
*
|
|
1961
|
+
* A fetched response's decoded content is verified against the digest the
|
|
1962
|
+
* inventory declares for that path, before any character decoding. Transport
|
|
1963
|
+
* encoding is transparent and does not enter the comparison, so content that
|
|
1964
|
+
* does not hash to the inventory's claim fails its row rather than reaching a
|
|
1965
|
+
* write. That is integrity against a single committed baseline, not
|
|
1966
|
+
* authenticity: it detects truncated, substituted, or stale content, and it
|
|
1967
|
+
* says nothing about who published the inventory.
|
|
1968
|
+
*
|
|
1969
|
+
* A guide mirror is never answered here whatever the caller asks for and
|
|
1970
|
+
* whatever the target holds, because those bytes belong to `fetch` and to the
|
|
1971
|
+
* mirror verb that writes them.
|
|
1703
1972
|
*
|
|
1704
1973
|
* @example
|
|
1705
1974
|
* ```ts
|
|
1706
1975
|
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
1707
1976
|
*
|
|
1708
1977
|
* const upstream = new Upstream()
|
|
1709
|
-
*
|
|
1978
|
+
* await upstream.read(['AGENTS.md'], { 'AGENTS.md': '2320416745' })
|
|
1710
1979
|
* upstream.destroy()
|
|
1711
1980
|
* ```
|
|
1712
1981
|
*/
|
|
1713
|
-
|
|
1982
|
+
read(paths: readonly string[], current: Snapshot): Promise<readonly HostFile[]>;
|
|
1714
1983
|
/**
|
|
1715
|
-
*
|
|
1984
|
+
* Catalog the published fleet from the registry's organization package list.
|
|
1716
1985
|
*
|
|
1717
|
-
* @returns
|
|
1986
|
+
* @returns One row per published package, sorted by name.
|
|
1987
|
+
* @throws {@link ScaffoldError} coded `FETCH` when the organization package
|
|
1988
|
+
* list is unreachable, malformed, empty, or larger than one bounded
|
|
1989
|
+
* collection, and `DESTROYED` when the reader is torn down before or during
|
|
1990
|
+
* the call.
|
|
1991
|
+
*
|
|
1992
|
+
* @remarks
|
|
1993
|
+
* The organization list is the exact membership the registry publishes, not a
|
|
1994
|
+
* relevance search, and it is the whole reason this answer can claim to be the
|
|
1995
|
+
* fleet. So it fails hard where a per-package lookup fails soft: an empty or
|
|
1996
|
+
* unreadable list would otherwise be written into a target's catalog table as
|
|
1997
|
+
* a fleet with no packages in it. A package whose own version lookup then
|
|
1998
|
+
* fails still keeps its row, carrying the cause instead of a version, because
|
|
1999
|
+
* the list already proved the package is published.
|
|
2000
|
+
*
|
|
2001
|
+
* @example
|
|
2002
|
+
* ```ts
|
|
2003
|
+
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
2004
|
+
*
|
|
2005
|
+
* const upstream = new Upstream()
|
|
2006
|
+
* const entries = await upstream.catalog()
|
|
2007
|
+
* upstream.destroy()
|
|
2008
|
+
* ```
|
|
2009
|
+
*/
|
|
2010
|
+
catalog(): Promise<readonly CatalogEntry[]>;
|
|
2011
|
+
/**
|
|
2012
|
+
* Tear the reader down, aborting every request in flight. Teardown is idempotent.
|
|
2013
|
+
*
|
|
2014
|
+
* @returns Nothing.
|
|
2015
|
+
*
|
|
2016
|
+
* @remarks
|
|
2017
|
+
* A call still in flight rejects with a `DESTROYED` error rather than
|
|
2018
|
+
* resolving to a partial answer, because half a fleet reads exactly like a
|
|
2019
|
+
* whole one.
|
|
2020
|
+
*
|
|
2021
|
+
* @example
|
|
2022
|
+
* ```ts
|
|
2023
|
+
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
2024
|
+
*
|
|
2025
|
+
* const upstream = new Upstream()
|
|
2026
|
+
* upstream.destroy()
|
|
2027
|
+
* upstream.emitter.destroyed // true
|
|
2028
|
+
* ```
|
|
2029
|
+
*/
|
|
2030
|
+
destroy(): void;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
/**
|
|
2034
|
+
* The upstream reader's observation channel.
|
|
1718
2035
|
*
|
|
1719
2036
|
* @remarks
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1722
|
-
*
|
|
2037
|
+
* Each verdict is published whole rather than as a name beside a summary, so a
|
|
2038
|
+
* listener reads the same value the call returns and a failed lookup is told
|
|
2039
|
+
* apart from a successful one by the verdict's own discriminant rather than by
|
|
2040
|
+
* which event carried it.
|
|
2041
|
+
*/
|
|
2042
|
+
export declare type UpstreamEventMap = {
|
|
2043
|
+
readonly release: readonly [release: Release];
|
|
2044
|
+
readonly mirror: readonly [mirror: Mirror];
|
|
2045
|
+
readonly file: readonly [file: HostFile];
|
|
2046
|
+
readonly error: readonly [error: unknown];
|
|
2047
|
+
readonly destroy: readonly [];
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
/**
|
|
2051
|
+
* The upstream contract: the package's only network reader, and it never writes.
|
|
1723
2052
|
*
|
|
1724
|
-
* @
|
|
1725
|
-
*
|
|
1726
|
-
*
|
|
2053
|
+
* @remarks
|
|
2054
|
+
* A per-package failure is collected as a verdict carrying its cause, not
|
|
2055
|
+
* thrown, so one unreachable package never costs the caller the rest of the
|
|
2056
|
+
* answer. The organization list is the exception: without it there is no fleet
|
|
2057
|
+
* to report, so an unreachable or malformed list is a coded failure.
|
|
1727
2058
|
*
|
|
1728
|
-
*
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1731
|
-
*
|
|
2059
|
+
* The vendored-file inventory is the other whole-answer read, and it fails
|
|
2060
|
+
* softly: an inventory that produces no answer fails every row of that call
|
|
2061
|
+
* rather than throwing, which is what leaves the caller one whole baseline to
|
|
2062
|
+
* fall back to instead of a mixture.
|
|
1732
2063
|
*/
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2064
|
+
export declare interface UpstreamInterface {
|
|
2065
|
+
readonly emitter: EmitterInterface<UpstreamEventMap>;
|
|
2066
|
+
/**
|
|
2067
|
+
* Look up the newest release each declared range admits.
|
|
2068
|
+
*
|
|
2069
|
+
* @param dependencies - The declared dependencies to look up.
|
|
2070
|
+
* @returns One release verdict per dependency, in input order.
|
|
2071
|
+
*/
|
|
2072
|
+
lookup(dependencies: readonly Dependency[]): Promise<readonly Release[]>;
|
|
2073
|
+
/**
|
|
2074
|
+
* Fetch each named package's guide, beside the local mirror it answers for.
|
|
2075
|
+
*
|
|
2076
|
+
* @param names - The packages to fetch: the target's declared set, or the whole organization.
|
|
2077
|
+
* @param current - The target's local mirrors as exact bytes, keyed by mirror path.
|
|
2078
|
+
* @returns One mirror verdict per name, in input order.
|
|
2079
|
+
*/
|
|
2080
|
+
fetch(names: readonly string[], current: Snapshot): Promise<readonly Mirror[]>;
|
|
2081
|
+
/**
|
|
2082
|
+
* Read each named vendored file from the repository, beside the target bytes it answers for.
|
|
2083
|
+
*
|
|
2084
|
+
* @param paths - The target-relative vendored paths to read.
|
|
2085
|
+
* @param current - The target files as exact bytes, keyed by the same paths.
|
|
2086
|
+
* @returns One file verdict per path, in input order.
|
|
2087
|
+
*/
|
|
2088
|
+
read(paths: readonly string[], current: Snapshot): Promise<readonly HostFile[]>;
|
|
2089
|
+
/**
|
|
2090
|
+
* Catalog the published fleet from the registry's organization package list.
|
|
2091
|
+
*
|
|
2092
|
+
* @returns One row per published package, sorted by name.
|
|
2093
|
+
*/
|
|
2094
|
+
catalog(): Promise<readonly CatalogEntry[]>;
|
|
2095
|
+
/**
|
|
2096
|
+
* Tear the reader down, aborting every request in flight. Teardown is idempotent.
|
|
2097
|
+
*
|
|
2098
|
+
* @returns Nothing.
|
|
2099
|
+
*/
|
|
2100
|
+
destroy(): void;
|
|
2101
|
+
}
|
|
1751
2102
|
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
/**
|
|
1785
|
-
* Tear the reader down, aborting every request in flight. Teardown is idempotent.
|
|
1786
|
-
*
|
|
1787
|
-
* @returns Nothing.
|
|
1788
|
-
*/
|
|
1789
|
-
destroy(): void;
|
|
1790
|
-
}
|
|
2103
|
+
/**
|
|
2104
|
+
* Options for the upstream reader.
|
|
2105
|
+
*
|
|
2106
|
+
* @remarks
|
|
2107
|
+
* The endpoints are grouped under the entity each configures: `repository`
|
|
2108
|
+
* takes the raw content host's `base`, its `branch`, and its `timeout`;
|
|
2109
|
+
* `registry` takes the registry's `base` and `timeout`. One raw content host
|
|
2110
|
+
* serves both the fleet's guides and this package's own vendored files, so one
|
|
2111
|
+
* group configures both. `concurrency` bounds requests in
|
|
2112
|
+
* flight and `retries` opts into per-request retry on a transport fault.
|
|
2113
|
+
* `limit` bounds the bytes read from one response body and `budget` bounds the
|
|
2114
|
+
* bytes read across a whole call, so neither one oversized answer nor many
|
|
2115
|
+
* small ones can exhaust the caller. Every request is unauthenticated and
|
|
2116
|
+
* follows no redirect.
|
|
2117
|
+
*/
|
|
2118
|
+
export declare interface UpstreamOptions {
|
|
2119
|
+
readonly repository?: {
|
|
2120
|
+
readonly base?: string;
|
|
2121
|
+
readonly branch?: string;
|
|
2122
|
+
readonly timeout?: number;
|
|
2123
|
+
};
|
|
2124
|
+
readonly registry?: {
|
|
2125
|
+
readonly base?: string;
|
|
2126
|
+
readonly timeout?: number;
|
|
2127
|
+
};
|
|
2128
|
+
readonly concurrency?: number;
|
|
2129
|
+
readonly retries?: number;
|
|
2130
|
+
readonly limit?: number;
|
|
2131
|
+
readonly budget?: number;
|
|
2132
|
+
readonly on?: EmitterHooks<UpstreamEventMap>;
|
|
2133
|
+
readonly error?: EmitterErrorHandler;
|
|
2134
|
+
}
|
|
1791
2135
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
};
|
|
1811
|
-
readonly registry?: {
|
|
1812
|
-
readonly base?: string;
|
|
1813
|
-
readonly timeout?: number;
|
|
1814
|
-
};
|
|
1815
|
-
readonly concurrency?: number;
|
|
1816
|
-
readonly retries?: number;
|
|
1817
|
-
readonly limit?: number;
|
|
1818
|
-
readonly budget?: number;
|
|
1819
|
-
readonly on?: EmitterHooks<UpstreamEventMap>;
|
|
1820
|
-
readonly error?: EmitterErrorHandler;
|
|
1821
|
-
}
|
|
2136
|
+
/**
|
|
2137
|
+
* What git reports about a target's working tree.
|
|
2138
|
+
*
|
|
2139
|
+
* @remarks
|
|
2140
|
+
* `tracked` is the only set a deletion may draw from: git does not report the
|
|
2141
|
+
* loss of an untracked path and `git diff` cannot restore it, so an ignored
|
|
2142
|
+
* path such as an installed dependency tree, a build output, or an editor
|
|
2143
|
+
* directory survives every verb. `dirty` is every path carrying an uncommitted
|
|
2144
|
+
* change, taken repo-wide rather than over a write set, because deletion makes
|
|
2145
|
+
* the write set the whole workspace. A clean tree is an empty `dirty`. A target
|
|
2146
|
+
* that is not a git repository yields no `Worktree` at all, so the caller
|
|
2147
|
+
* decides what to do about that rather than reading it out of an invented
|
|
2148
|
+
* empty value.
|
|
2149
|
+
*/
|
|
2150
|
+
export declare interface Worktree {
|
|
2151
|
+
readonly tracked: readonly string[];
|
|
2152
|
+
readonly dirty: readonly string[];
|
|
2153
|
+
}
|
|
1822
2154
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2155
|
+
/**
|
|
2156
|
+
* One physical directory identity captured across a write transaction.
|
|
2157
|
+
*
|
|
2158
|
+
* @remarks
|
|
2159
|
+
* Device and inode locate the directory and do not date it. Two directories
|
|
2160
|
+
* occupying one slot at different times share an anchor, so an anchor proves
|
|
2161
|
+
* where a write lands and never that the directory was left alone.
|
|
2162
|
+
*/
|
|
2163
|
+
export declare interface WriteAnchor {
|
|
2164
|
+
readonly path: string;
|
|
2165
|
+
readonly device: number;
|
|
2166
|
+
readonly inode: number;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/** The final directory anchor of a write transaction and the subset one call created. */
|
|
2170
|
+
export declare interface WriteDirectoryResult {
|
|
2171
|
+
readonly anchor: WriteAnchor;
|
|
2172
|
+
readonly created: readonly WriteAnchor[];
|
|
2173
|
+
}
|
|
1842
2174
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
2175
|
+
/**
|
|
2176
|
+
* One destination snapshot captured before a write and required to survive it.
|
|
2177
|
+
*
|
|
2178
|
+
* @remarks
|
|
2179
|
+
* `device`, `inode`, `modified`, `size`, and `digest` are present only where
|
|
2180
|
+
* the observed shape supplies them.
|
|
2181
|
+
*/
|
|
2182
|
+
export declare interface WriteExpectation {
|
|
2183
|
+
readonly path: string;
|
|
2184
|
+
readonly shape: 'absent' | 'file' | 'directory';
|
|
2185
|
+
readonly device?: number;
|
|
2186
|
+
readonly inode?: number;
|
|
2187
|
+
readonly modified?: number;
|
|
2188
|
+
readonly size?: number;
|
|
2189
|
+
readonly digest?: string;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
/** The narrower caller-observed destination state a write transaction must still match. */
|
|
2193
|
+
export declare interface WritePrecondition {
|
|
2194
|
+
readonly path: string;
|
|
2195
|
+
readonly shape: 'absent' | 'file';
|
|
2196
|
+
readonly digest?: string;
|
|
2197
|
+
}
|
|
1866
2198
|
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
/**
|
|
1935
|
-
* Open a transaction over one target directory.
|
|
1936
|
-
*
|
|
1937
|
-
* @param target - The directory every path is written beneath.
|
|
1938
|
-
* @param paths - Every target-relative path this transaction may touch.
|
|
1939
|
-
* @param preconditions - The caller-observed destination states the whole
|
|
1940
|
-
* transaction is held to, each naming a resolved destination of `paths`.
|
|
1941
|
-
* @throws {@link ScaffoldError} coded `INVALID` when an argument is off
|
|
1942
|
-
* contract, `TARGET` when a destination is a shape this package will not write
|
|
1943
|
-
* over or no longer matches its precondition, and `WRITE` when the private
|
|
1944
|
-
* root cannot be established.
|
|
1945
|
-
*
|
|
1946
|
-
* @remarks
|
|
1947
|
-
* Nothing is created until every destination has been inspected and every
|
|
1948
|
-
* precondition has held, so a refused transaction leaves no residue at all.
|
|
1949
|
-
* The private root is created last and carries a random name, so two
|
|
1950
|
-
* transactions over one target never collide.
|
|
1951
|
-
*/
|
|
1952
|
-
constructor(target: string, paths: readonly string[], preconditions?: readonly WritePrecondition[]);
|
|
1953
|
-
/** The resolved directory every path is written beneath. */
|
|
1954
|
-
get target(): string;
|
|
1955
|
-
/** What each destination held when the transaction opened, in path order. */
|
|
1956
|
-
get expectations(): readonly WriteExpectation[];
|
|
1957
|
-
/** Whether the transaction can still be committed or discarded. */
|
|
1958
|
-
get open(): boolean;
|
|
2199
|
+
/**
|
|
2200
|
+
* One staged, reversible mutation of one target directory.
|
|
2201
|
+
*
|
|
2202
|
+
* @remarks
|
|
2203
|
+
* The transaction owns a private root beside the target — a sibling directory on
|
|
2204
|
+
* the same volume, so every promotion is a rename rather than a copy. Staging
|
|
2205
|
+
* writes go into that root and nothing else, so a call that fails while staging
|
|
2206
|
+
* has not touched the target at all. Commit is the only step that mutates the
|
|
2207
|
+
* target, and it is the only step that can need rolling back.
|
|
2208
|
+
*
|
|
2209
|
+
* A precondition and an expectation hold a destination still. A **precondition** is what the caller
|
|
2210
|
+
* observed earlier and is checked once, at construction, so a target that moved
|
|
2211
|
+
* between the caller's read and this transaction fails before anything is
|
|
2212
|
+
* created. An **expectation** is captured here, at construction, and re-checked
|
|
2213
|
+
* at commit, so a target that moves while the write is being staged fails before
|
|
2214
|
+
* anything is promoted.
|
|
2215
|
+
*
|
|
2216
|
+
* What this provides, exactly:
|
|
2217
|
+
*
|
|
2218
|
+
* - **Across destinations, staged-then-swapped with rollback on a caught
|
|
2219
|
+
* failure.** A failure part way through commit restores every destination it
|
|
2220
|
+
* already promoted, restores every file it already took, and removes every
|
|
2221
|
+
* directory it created, then reports what recovery could not undo. This is
|
|
2222
|
+
* measured: a promotion is driven to fail after an earlier one landed, and the
|
|
2223
|
+
* earlier destination is read back.
|
|
2224
|
+
* - **No partly written destination.** Every file is written whole into the
|
|
2225
|
+
* private root and digested there before commit, so a destination never
|
|
2226
|
+
* receives bytes that were still being produced.
|
|
2227
|
+
* - **Containment, not continuity, of the directories it creates.** Every
|
|
2228
|
+
* ancestor is re-read between `mkdir` calls and again before the first
|
|
2229
|
+
* promotion, so an ancestor that became a file, a symlink, a directory
|
|
2230
|
+
* elsewhere, or nothing is refused. An ancestor deleted and recreated under
|
|
2231
|
+
* the same name can receive its old inode back and is indistinguishable here
|
|
2232
|
+
* from one that never moved.
|
|
2233
|
+
* - **No crash atomicity across destinations.** A process killed between
|
|
2234
|
+
* promotions leaves the target holding some new files and some old ones, and
|
|
2235
|
+
* leaves the private root behind. Nothing here is a journal, and the private
|
|
2236
|
+
* root's name is the only record a later run could read.
|
|
2237
|
+
*
|
|
2238
|
+
* A destination is preserved by hard link and then replaced by a single
|
|
2239
|
+
* `rename`, rather than moved aside and replaced, so the path continues to name
|
|
2240
|
+
* the old file right up to the swap. That is a property of `rename` on the host,
|
|
2241
|
+
* not one this package's tests measure: telling it apart from move-then-replace
|
|
2242
|
+
* needs a reader observing the destination inside the swap, and no test here
|
|
2243
|
+
* does that. Read the claim as the mechanism it describes, not as a proven
|
|
2244
|
+
* guarantee about a concurrent reader.
|
|
2245
|
+
*
|
|
2246
|
+
* A path names a file this transaction writes, takes, or establishes as a
|
|
2247
|
+
* directory; every path is target-relative and is measured by the portable-path
|
|
2248
|
+
* law. An expectation and a precondition both name the resolved destination
|
|
2249
|
+
* instead, because that is the path each is re-read at.
|
|
2250
|
+
*
|
|
2251
|
+
* @example
|
|
2252
|
+
* ```ts
|
|
2253
|
+
* import { WriteTransaction } from '@orkestrel/scaffold/server'
|
|
2254
|
+
*
|
|
2255
|
+
* const transaction = new WriteTransaction('./packages/router', ['AGENTS.md'])
|
|
2256
|
+
* try {
|
|
2257
|
+
* transaction.write('AGENTS.md', '# Agents\n')
|
|
2258
|
+
* transaction.commit() // ['AGENTS.md']
|
|
2259
|
+
* } finally {
|
|
2260
|
+
* transaction.discard()
|
|
2261
|
+
* }
|
|
2262
|
+
* ```
|
|
2263
|
+
*/
|
|
2264
|
+
export declare class WriteTransaction {
|
|
2265
|
+
#private;
|
|
1959
2266
|
/**
|
|
1960
|
-
*
|
|
2267
|
+
* Open a transaction over one target directory.
|
|
1961
2268
|
*
|
|
1962
|
-
* @param
|
|
1963
|
-
* @param
|
|
1964
|
-
* @
|
|
1965
|
-
*
|
|
1966
|
-
|
|
1967
|
-
*
|
|
1968
|
-
*
|
|
2269
|
+
* @param target - The directory every path is written beneath.
|
|
2270
|
+
* @param paths - Every target-relative path this transaction may touch.
|
|
2271
|
+
* @param preconditions - The caller-observed destination states the whole
|
|
2272
|
+
* transaction is held to, each naming a resolved destination of `paths`.
|
|
2273
|
+
* @throws {@link ScaffoldError} coded `INVALID` when an argument is off
|
|
2274
|
+
* contract, `TARGET` when a destination is a shape this package will not write
|
|
2275
|
+
* over or no longer matches its precondition, and `WRITE` when the private
|
|
2276
|
+
* root cannot be established.
|
|
1969
2277
|
*
|
|
1970
2278
|
* @remarks
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
2279
|
+
* Nothing is created until every destination has been inspected and every
|
|
2280
|
+
* precondition has held, so a refused transaction leaves no residue at all.
|
|
2281
|
+
* The private root is created last and carries a random name, so two
|
|
2282
|
+
* transactions over one target never collide.
|
|
1973
2283
|
*/
|
|
1974
|
-
|
|
2284
|
+
constructor(target: string, paths: readonly string[], preconditions?: readonly WritePrecondition[]);
|
|
2285
|
+
/** The resolved directory every path is written beneath. */
|
|
2286
|
+
get target(): string;
|
|
2287
|
+
/** What each destination held when the transaction opened, in path order. */
|
|
2288
|
+
get expectations(): readonly WriteExpectation[];
|
|
2289
|
+
/** Whether the transaction can still be committed or discarded. */
|
|
2290
|
+
get open(): boolean;
|
|
1975
2291
|
/**
|
|
1976
|
-
* Stage one
|
|
2292
|
+
* Stage one text file.
|
|
1977
2293
|
*
|
|
1978
2294
|
* @param path - The target-relative path to write.
|
|
1979
|
-
* @param
|
|
1980
|
-
* @param executable - If `true`, the destination carries the executable bit; if `false`, it does not.
|
|
2295
|
+
* @param content - The exact UTF-8 text the destination must hold.
|
|
1981
2296
|
* @returns Nothing.
|
|
1982
2297
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
1983
2298
|
* transaction opened or is already staged, `TARGET` when the destination holds
|
|
1984
|
-
* a directory
|
|
1985
|
-
*
|
|
2299
|
+
* a directory, and `WRITE` when the staged file cannot be written or does not
|
|
2300
|
+
* carry the bytes it was given.
|
|
1986
2301
|
*
|
|
1987
2302
|
* @remarks
|
|
1988
|
-
* The
|
|
1989
|
-
* a
|
|
2303
|
+
* The staged file is read back and digested against the text it was given, so
|
|
2304
|
+
* a partial or interrupted write is refused here rather than promoted later.
|
|
1990
2305
|
*/
|
|
1991
|
-
|
|
2306
|
+
write(path: string, content: string): void;
|
|
1992
2307
|
/**
|
|
1993
|
-
*
|
|
2308
|
+
* Stage one byte-for-byte copy of a file that already exists on this host.
|
|
1994
2309
|
*
|
|
1995
|
-
* @param path - The target-relative
|
|
1996
|
-
* @
|
|
2310
|
+
* @param path - The target-relative path to write.
|
|
2311
|
+
* @param source - The resolved absolute path to copy the bytes from.
|
|
2312
|
+
* @param executable - If `true`, the destination carries the executable bit; if `false`, it does not.
|
|
2313
|
+
* @returns Nothing.
|
|
1997
2314
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
1998
|
-
* transaction opened, `TARGET` when the destination holds
|
|
1999
|
-
*
|
|
2315
|
+
* transaction opened or is already staged, `TARGET` when the destination holds
|
|
2316
|
+
* a directory or the source is not a physical file, and `WRITE` when the copy
|
|
2317
|
+
* cannot be made or does not digest to the source's bytes.
|
|
2000
2318
|
*
|
|
2001
2319
|
* @remarks
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
2004
|
-
* innermost first, and leaves every segment that was already there. Each
|
|
2005
|
-
* created segment is captured by device and inode, so a segment swapped
|
|
2006
|
-
* underneath the transaction is detected rather than written into.
|
|
2320
|
+
* The source is digested before the copy and the copy is digested after it, so
|
|
2321
|
+
* a source that changed mid-copy is refused instead of staged.
|
|
2007
2322
|
*/
|
|
2008
|
-
|
|
2323
|
+
copy(path: string, source: string, executable: boolean): void;
|
|
2009
2324
|
/**
|
|
2010
|
-
*
|
|
2325
|
+
* Establish one directory inside the target, one segment at a time.
|
|
2011
2326
|
*
|
|
2012
|
-
* @param path - The target-relative
|
|
2013
|
-
* @returns
|
|
2327
|
+
* @param path - The target-relative directory to establish.
|
|
2328
|
+
* @returns The directory's identity and every segment this call created.
|
|
2014
2329
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
2015
|
-
* transaction opened
|
|
2016
|
-
*
|
|
2330
|
+
* transaction opened, `TARGET` when the destination holds a file, and `WRITE`
|
|
2331
|
+
* when a segment cannot be created or changed while it was being created.
|
|
2017
2332
|
*
|
|
2018
2333
|
* @remarks
|
|
2019
|
-
*
|
|
2020
|
-
*
|
|
2334
|
+
* A directory is created immediately rather than staged, because creating one
|
|
2335
|
+
* destroys nothing: rollback removes exactly the segments this call created,
|
|
2336
|
+
* innermost first, and leaves every segment that was already there. Each
|
|
2337
|
+
* created segment is captured by device and inode, so a segment swapped
|
|
2338
|
+
* underneath the transaction is detected rather than written into.
|
|
2021
2339
|
*/
|
|
2022
|
-
|
|
2340
|
+
establish(path: string): WriteDirectoryResult;
|
|
2023
2341
|
/**
|
|
2024
|
-
*
|
|
2342
|
+
* Mark one file for deletion at commit.
|
|
2025
2343
|
*
|
|
2026
|
-
* @
|
|
2027
|
-
*
|
|
2028
|
-
* @throws {@link ScaffoldError} coded `
|
|
2029
|
-
*
|
|
2030
|
-
*
|
|
2344
|
+
* @param path - The target-relative file to delete.
|
|
2345
|
+
* @returns Nothing.
|
|
2346
|
+
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
2347
|
+
* transaction opened or is already claimed, and `TARGET` when the destination
|
|
2348
|
+
* does not hold a file.
|
|
2031
2349
|
*
|
|
2032
2350
|
* @remarks
|
|
2033
|
-
*
|
|
2034
|
-
*
|
|
2035
|
-
* check runs inside the same rollback as the promotions, because a transaction
|
|
2036
|
-
* that refuses before it starts still has a private root and created
|
|
2037
|
-
* directories to clear. The transaction is closed either way: a committed one
|
|
2038
|
-
* has nothing left to undo and a failed one has already been rolled back.
|
|
2351
|
+
* Nothing moves here. Commit renames the file into the private backup rather
|
|
2352
|
+
* than unlinking it, so a later failure in the same commit puts it back.
|
|
2039
2353
|
*/
|
|
2040
|
-
|
|
2354
|
+
remove(path: string): void;
|
|
2041
2355
|
/**
|
|
2042
|
-
*
|
|
2356
|
+
* Promote every staged file and take every marked file, or roll the whole call back.
|
|
2043
2357
|
*
|
|
2044
|
-
* @returns
|
|
2045
|
-
*
|
|
2046
|
-
|
|
2358
|
+
* @returns Every target-relative path whose destination changed: the files
|
|
2359
|
+
* promoted, then the directories established, then the files taken.
|
|
2360
|
+
* @throws {@link ScaffoldError} coded `WRITE` when the transaction is closed,
|
|
2361
|
+
* when a destination moved since the transaction opened, or when the commit
|
|
2362
|
+
* failed; a failure reports what rollback could not undo in its context.
|
|
2047
2363
|
*
|
|
2048
2364
|
* @remarks
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2365
|
+
* Every destination and every directory this transaction created is re-checked
|
|
2366
|
+
* before anything moves, so the common failure moves nothing at all. That
|
|
2367
|
+
* check runs inside the same rollback as the promotions, because a transaction
|
|
2368
|
+
* that refuses before it starts still has a private root and created
|
|
2369
|
+
* directories to clear. The transaction is closed either way: a committed one
|
|
2370
|
+
* has nothing left to undo and a failed one has already been rolled back.
|
|
2051
2371
|
*/
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2372
|
+
commit(): readonly string[];
|
|
2373
|
+
/**
|
|
2374
|
+
* Abandon the transaction and remove everything it created.
|
|
2375
|
+
*
|
|
2376
|
+
* @returns Nothing.
|
|
2377
|
+
* @throws {@link ScaffoldError} coded `WRITE` when residue could not be
|
|
2378
|
+
* removed, naming the private root that still holds it.
|
|
2379
|
+
*
|
|
2380
|
+
* @remarks
|
|
2381
|
+
* Idempotent, and a no-op on a transaction that already committed or already
|
|
2382
|
+
* failed, so a caller can put it in a `finally` beside the work it guards.
|
|
2383
|
+
*/
|
|
2384
|
+
discard(): void;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
export { }
|