@orkestrel/scaffold 0.0.48 → 0.0.49
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/dist/bin/main.js +271 -53
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +7 -0
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -0
- package/dist/host/claude/rules/quality.md +4 -1
- package/dist/host/guides/scaffold.md +150 -65
- package/dist/host/manifest.json +217 -109
- package/dist/host/tests/config.test.ts +103 -1
- package/dist/src/core/index.cjs +32 -4
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +65 -8
- package/dist/src/core/index.d.ts +65 -8
- package/dist/src/core/index.js +31 -5
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +769 -137
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +681 -376
- package/dist/src/server/index.d.ts +681 -376
- package/dist/src/server/index.js +766 -141
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -5,19 +5,20 @@ 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';
|
|
8
9
|
import { Mirror } from '@orkestrel/scaffold';
|
|
9
10
|
import { Plan } from '@orkestrel/scaffold';
|
|
10
11
|
import { Release } from '@orkestrel/scaffold';
|
|
11
12
|
import { Snapshot } from '@orkestrel/scaffold';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* The Git branch syntax the
|
|
15
|
+
* The Git branch syntax the repository endpoint accepts.
|
|
15
16
|
*
|
|
16
17
|
* @remarks
|
|
17
18
|
* A branch is caller-supplied and reaches a URL path, so it is closed to
|
|
18
19
|
* alphanumerics, dot, underscore, hyphen, and the separator, must open with an
|
|
19
20
|
* alphanumeric, and may carry no `..` anywhere. That last refusal is what stops
|
|
20
|
-
* a branch from walking out of the
|
|
21
|
+
* a branch from walking out of the repository path it addresses.
|
|
21
22
|
*/
|
|
22
23
|
export declare const BRANCH_PATTERN: RegExp;
|
|
23
24
|
|
|
@@ -111,6 +112,80 @@ export declare const DIGEST_PATTERN: RegExp;
|
|
|
111
112
|
*/
|
|
112
113
|
export declare const DRIVE_PATTERN: RegExp;
|
|
113
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Assemble a whole vendored host from live files and the installed floor.
|
|
117
|
+
*
|
|
118
|
+
* @param files - The host-owned vendored files read from the repository, one
|
|
119
|
+
* row per path.
|
|
120
|
+
* @param floor - The installed host floor, which fixes the membership a fill
|
|
121
|
+
* may draw from and supplies the bytes owned by another surface.
|
|
122
|
+
* @returns The assembled host, or `undefined` when any row produced no answer or
|
|
123
|
+
* names a path the floor does not declare, or when a host-owned path is absent.
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* The one place the host-owned all-or-nothing rule is decided, so no verb
|
|
127
|
+
* restates it. The host surface contributes one baseline: a fill carries live
|
|
128
|
+
* bytes for every path that surface writes, or it is nothing. A row that failed,
|
|
129
|
+
* went missing, names an undeclared path, or leaves a host-owned path absent
|
|
130
|
+
* answers `undefined`. Deferred paths are presence-only and retain the installed
|
|
131
|
+
* floor bytes that their catalog or mirror surface owns; repair never writes
|
|
132
|
+
* those floor bytes. One `Host` can therefore carry live host bytes beside floor
|
|
133
|
+
* bytes without mixing baselines within a surface.
|
|
134
|
+
*
|
|
135
|
+
* The emitted entries keep the release's own order and its storage and
|
|
136
|
+
* executable declarations, and carry digests recomputed over the bytes the fill
|
|
137
|
+
* actually holds. That is what lets a reader verify the value against itself,
|
|
138
|
+
* and it is why an undeclared path is refused rather than added: membership
|
|
139
|
+
* moves with a release, never with a fetch.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* import { filesToHost } from '@orkestrel/scaffold/server'
|
|
144
|
+
*
|
|
145
|
+
* filesToHost([{ path: 'AGENTS.md', lookup: 'found', hex: '23204167656e74730a' }], floor)
|
|
146
|
+
* // { manifest: { entries: [ … ], roots: [ … ], digest: '…' }, bytes: { 'AGENTS.md': '…' } }
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function filesToHost(files: readonly HostFile[], floor: Host): Host | undefined;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Projects exact bytes stated in hexadecimal to their SHA-256 digest.
|
|
153
|
+
*
|
|
154
|
+
* @param hex - The exact lowercase hexadecimal bytes to digest.
|
|
155
|
+
* @returns Sixty-four lowercase hexadecimal digits.
|
|
156
|
+
* @throws `ScaffoldError('INVALID', …)` when `hex` is not exact bounded
|
|
157
|
+
* lowercase hexadecimal text.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* import { hexToDigest } from '@orkestrel/scaffold/server'
|
|
162
|
+
*
|
|
163
|
+
* hexToDigest('68690a') // '98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4'
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function hexToDigest(hex: string): string;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A whole vendored host supplied as a value: the membership beside the bytes filling it.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* `manifest` carries the membership, the roots, and the executable declarations
|
|
173
|
+
* the installed release fixed, and each entry's `digest` is the digest of the
|
|
174
|
+
* bytes this value actually holds. `bytes` is keyed by each entry's
|
|
175
|
+
* `destination` and covers exactly the entries the fill holds, so an entry with
|
|
176
|
+
* no bytes and a byte string under no entry are each a refusal rather than a
|
|
177
|
+
* gap a reader has to work around.
|
|
178
|
+
*
|
|
179
|
+
* Membership never moves without a release. A fill therefore states which of
|
|
180
|
+
* the release's paths it carries and never introduces one the release did not
|
|
181
|
+
* declare, which is what keeps a value host's reach identical to the installed
|
|
182
|
+
* root's however the bytes were obtained.
|
|
183
|
+
*/
|
|
184
|
+
export declare interface Host {
|
|
185
|
+
readonly manifest: HostManifest;
|
|
186
|
+
readonly bytes: Snapshot;
|
|
187
|
+
}
|
|
188
|
+
|
|
114
189
|
/**
|
|
115
190
|
* The complete vendored-host inventory.
|
|
116
191
|
*
|
|
@@ -139,10 +214,10 @@ export declare interface HostManifest {
|
|
|
139
214
|
export declare const INVALID_SEGMENT_CHARACTER_PATTERN: RegExp;
|
|
140
215
|
|
|
141
216
|
/**
|
|
142
|
-
* Narrow a value to a Git branch the
|
|
217
|
+
* Narrow a value to a Git branch the repository endpoint accepts.
|
|
143
218
|
*
|
|
144
219
|
* @remarks
|
|
145
|
-
* A branch reaches the
|
|
220
|
+
* A branch reaches the repository URL's path, so the syntax is closed rather than
|
|
146
221
|
* merely bounded and no `..` is admitted anywhere in it.
|
|
147
222
|
*
|
|
148
223
|
* @example
|
|
@@ -272,6 +347,30 @@ export declare function isExactCaseFile(path: string): boolean;
|
|
|
272
347
|
*/
|
|
273
348
|
export declare function isFilesystemPath(value: unknown): value is string;
|
|
274
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Narrow a value to one {@link Host}.
|
|
352
|
+
*
|
|
353
|
+
* @remarks
|
|
354
|
+
* A whole vendored host handed in as a value is as untrusted as one read from a
|
|
355
|
+
* directory a caller named, so both halves are guarded: the manifest by the same
|
|
356
|
+
* membership law a read root is held to, and the bytes by the core snapshot law,
|
|
357
|
+
* which bounds the fill and reads every key as a path and every value as exact
|
|
358
|
+
* lowercase hexadecimal. Whether those halves agree with each other is the
|
|
359
|
+
* reader's question rather than this one's, because a guard has only `false` to
|
|
360
|
+
* say and a mismatch has a path to name.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* import { isHost } from '@orkestrel/scaffold/server'
|
|
365
|
+
*
|
|
366
|
+
* const digest = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
|
|
367
|
+
*
|
|
368
|
+
* isHost({ manifest: { entries: [], roots: [], digest }, bytes: {} }) // true
|
|
369
|
+
* isHost({ manifest: { entries: [], roots: [], digest } }) // false
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
export declare const isHost: Guard<Host>;
|
|
373
|
+
|
|
275
374
|
/**
|
|
276
375
|
* Narrow a value to one {@link HostManifest}.
|
|
277
376
|
*
|
|
@@ -319,7 +418,12 @@ export declare function isInventory(value: unknown): value is readonly unknown[]
|
|
|
319
418
|
* ```ts
|
|
320
419
|
* import { isManifestEntry } from '@orkestrel/scaffold/server'
|
|
321
420
|
*
|
|
322
|
-
* isManifestEntry({
|
|
421
|
+
* isManifestEntry({
|
|
422
|
+
* storage: 'AGENTS.md',
|
|
423
|
+
* destination: 'AGENTS.md',
|
|
424
|
+
* executable: false,
|
|
425
|
+
* digest: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
|
426
|
+
* }) // true
|
|
323
427
|
* ```
|
|
324
428
|
*/
|
|
325
429
|
export declare const isManifestEntry: Guard<ManifestEntry>;
|
|
@@ -337,6 +441,12 @@ export declare const isMaterializerHooks: Guard<EmitterHooks<MaterializerEventMa
|
|
|
337
441
|
/**
|
|
338
442
|
* Narrow a value to {@link MaterializerOptions}.
|
|
339
443
|
*
|
|
444
|
+
* @remarks
|
|
445
|
+
* `host` admits both representations of one vendored root: a directory path and
|
|
446
|
+
* a whole {@link Host} value. They share a key because they are one setting
|
|
447
|
+
* stated two ways rather than two settings, so nothing downstream has to
|
|
448
|
+
* reconcile a pair that could disagree.
|
|
449
|
+
*
|
|
340
450
|
* @example
|
|
341
451
|
* ```ts
|
|
342
452
|
* import { isMaterializerOptions } from '@orkestrel/scaffold/server'
|
|
@@ -350,6 +460,25 @@ export declare const isMaterializerOptions: Guard<MaterializerOptions>;
|
|
|
350
460
|
/** Narrow a value to a bounded list of fetched guide mirrors. */
|
|
351
461
|
export declare const isMirrors: Guard<readonly Mirror[]>;
|
|
352
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Narrow a value to a bounded list of target-relative paths.
|
|
465
|
+
*
|
|
466
|
+
* @remarks
|
|
467
|
+
* Composed from the core collection and path guards rather than restated, so
|
|
468
|
+
* the containment law that keeps a caller-supplied path inside its target has
|
|
469
|
+
* exactly one home. It bounds what a caller may hand a public method, which is
|
|
470
|
+
* why it is not {@link isInventory}: that one bounds what a checkout may hold.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```ts
|
|
474
|
+
* import { isPaths } from '@orkestrel/scaffold/server'
|
|
475
|
+
*
|
|
476
|
+
* isPaths(['AGENTS.md']) // true
|
|
477
|
+
* isPaths(['../secrets']) // false
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
export declare const isPaths: Guard<readonly string[]>;
|
|
481
|
+
|
|
353
482
|
/**
|
|
354
483
|
* Test whether a path is a physical directory this package will read or write into.
|
|
355
484
|
*
|
|
@@ -393,25 +522,6 @@ export declare function isPhysicalDirectory(path: string): boolean;
|
|
|
393
522
|
*/
|
|
394
523
|
export declare function isPhysicalFile(path: string): boolean;
|
|
395
524
|
|
|
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
525
|
/**
|
|
416
526
|
* Narrow a value to a per-request timeout in milliseconds.
|
|
417
527
|
*
|
|
@@ -447,7 +557,7 @@ export declare const isUpstreamHooks: Guard<EmitterHooks<UpstreamEventMap>>;
|
|
|
447
557
|
* ```ts
|
|
448
558
|
* import { isUpstreamOptions } from '@orkestrel/scaffold/server'
|
|
449
559
|
*
|
|
450
|
-
* isUpstreamOptions({
|
|
560
|
+
* isUpstreamOptions({ repository: { branch: 'main' }, concurrency: 4 }) // true
|
|
451
561
|
* isUpstreamOptions({ concurrency: 0 }) // false
|
|
452
562
|
* ```
|
|
453
563
|
*/
|
|
@@ -476,6 +586,25 @@ export declare const isUpstreamOptions: Guard<UpstreamOptions>;
|
|
|
476
586
|
*/
|
|
477
587
|
export declare function isVacant(target: string): boolean;
|
|
478
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Narrow a value to a {@link Worktree}.
|
|
591
|
+
*
|
|
592
|
+
* @remarks
|
|
593
|
+
* Both path lists are target-relative, so both are measured by the core path
|
|
594
|
+
* law: a reported path that is not one this package could have planned is not a
|
|
595
|
+
* path it will delete. The inventory guard bounds the lists, because a checkout
|
|
596
|
+
* is legitimately far larger than any collection a caller hands a method.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* import { isWorktree } from '@orkestrel/scaffold/server'
|
|
601
|
+
*
|
|
602
|
+
* isWorktree({ tracked: ['AGENTS.md'], dirty: [] }) // true
|
|
603
|
+
* isWorktree({ tracked: ['../secrets'], dirty: [] }) // false
|
|
604
|
+
* ```
|
|
605
|
+
*/
|
|
606
|
+
export declare const isWorktree: Guard<Worktree>;
|
|
607
|
+
|
|
479
608
|
/**
|
|
480
609
|
* List a directory's descendant directories as sorted root-relative paths.
|
|
481
610
|
*
|
|
@@ -548,11 +677,19 @@ export declare function listFiles(root: string): readonly string[];
|
|
|
548
677
|
*/
|
|
549
678
|
export declare const MANIFEST_NAME = "manifest.json";
|
|
550
679
|
|
|
551
|
-
/**
|
|
680
|
+
/**
|
|
681
|
+
* One file record of the vendored host's manifest.
|
|
682
|
+
*
|
|
683
|
+
* @remarks
|
|
684
|
+
* `digest` is the SHA-256 of the file's exact bytes. A live read compares it
|
|
685
|
+
* against the target's copy, and verifies host-supplied bytes against it before
|
|
686
|
+
* treating them as that entry.
|
|
687
|
+
*/
|
|
552
688
|
export declare interface ManifestEntry {
|
|
553
689
|
readonly storage: string;
|
|
554
690
|
readonly destination: string;
|
|
555
691
|
readonly executable: boolean;
|
|
692
|
+
readonly digest: string;
|
|
556
693
|
}
|
|
557
694
|
|
|
558
695
|
/**
|
|
@@ -708,7 +845,7 @@ export declare function matchesPrecondition(precondition: WritePrecondition): bo
|
|
|
708
845
|
* directories. It is the inversion the contract asks for: the candidate set
|
|
709
846
|
* is re-derived from the plan and narrowed by what git tracks, and the audit
|
|
710
847
|
* must agree with that derivation rather than supply the set itself.
|
|
711
|
-
*
|
|
848
|
+
* Git metadata is protected because losing history is not a repair,
|
|
712
849
|
* and a target's own `src` and `app` trees are protected because a
|
|
713
850
|
* workspace's source is the one thing scaffold never plans and never owns. A
|
|
714
851
|
* plan the compiler emits never maps a protected root, so this guard exists
|
|
@@ -735,7 +872,7 @@ export declare function matchesProtectedPath(path: string): boolean;
|
|
|
735
872
|
* The vendoring deny-list. A host root is staged from a real checkout, so the
|
|
736
873
|
* refusal is stated over the path rather than over the file's content: a
|
|
737
874
|
* credential is recognizable by where it sits and what it is called long before
|
|
738
|
-
* anything reads it.
|
|
875
|
+
* anything reads it. Git metadata is included through
|
|
739
876
|
* {@link matchesGitPath}, so one call answers the whole question and no caller
|
|
740
877
|
* has to remember to ask twice.
|
|
741
878
|
*
|
|
@@ -768,6 +905,12 @@ export declare function matchesSensitivePath(path: string): boolean;
|
|
|
768
905
|
* case, so a manifest naming `agents.md` for a stored `AGENTS.md` is refused on
|
|
769
906
|
* a case-insensitive filesystem rather than silently resolved.
|
|
770
907
|
*
|
|
908
|
+
* That host arrives as a directory path or as a whole {@link Host} value, and
|
|
909
|
+
* every verb reads one immutable host either way. A value is owned, verified
|
|
910
|
+
* against its own membership and digests, and read in memory; a write fills it
|
|
911
|
+
* into a private root and copies from there, so the executable declarations the
|
|
912
|
+
* release fixed reach the target from either representation.
|
|
913
|
+
*
|
|
771
914
|
* What a mutation guarantees is exactly what {@link WriteTransaction}
|
|
772
915
|
* guarantees, and no more: a caught failure part way through a commit rolls the
|
|
773
916
|
* whole commit back, no destination ever receives half-written bytes, and a
|
|
@@ -794,18 +937,26 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
794
937
|
/**
|
|
795
938
|
* Construct a materializer over one vendored host root.
|
|
796
939
|
*
|
|
797
|
-
* @param options - The vendored host
|
|
798
|
-
* listener-error handler.
|
|
940
|
+
* @param options - The vendored host, in either representation, the initial
|
|
941
|
+
* listeners, and the listener-error handler.
|
|
799
942
|
* @throws {@link ScaffoldError} coded `INVALID` when `options` is present but
|
|
800
943
|
* is not an option bag this materializer accepts, and `TARGET` when the host
|
|
801
|
-
* carries a manifest that cannot be read
|
|
944
|
+
* carries a manifest that cannot be read, does not match what it stores, or
|
|
945
|
+
* is a value that does not agree with the bytes beside it.
|
|
802
946
|
*
|
|
803
947
|
* @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
|
|
948
|
+
* A `host` path defaults to this package's own vendored root, resolved from
|
|
949
|
+
* this module's own location so it never depends on the caller's working
|
|
950
|
+
* directory. A root carrying no manifest is read as a raw checkout and every
|
|
807
951
|
* artifact maps onto it one to one.
|
|
808
952
|
*
|
|
953
|
+
* A `host` value is owned before it is read and then held immutable, so the
|
|
954
|
+
* bytes this check measured are the bytes every later read returns. It is
|
|
955
|
+
* verified the way a root is, against the same membership law: the manifest
|
|
956
|
+
* digest must cover the membership beside it, no two entries may claim one
|
|
957
|
+
* destination, and the fill must carry exactly one hashing byte string per
|
|
958
|
+
* declared entry.
|
|
959
|
+
*
|
|
809
960
|
* The host is read here rather than on first use, so a broken vendored root
|
|
810
961
|
* fails at construction where the caller can still act on it, and so nothing
|
|
811
962
|
* has to carry a second flag recording whether the read has happened yet.
|
|
@@ -931,7 +1082,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
931
1082
|
*
|
|
932
1083
|
* @param plan - The compiled plan that decides which paths are foreign.
|
|
933
1084
|
* @param audit - The preview returned by this materializer's `audit` method; it must agree with the candidate set this call re-derives.
|
|
934
|
-
* @param
|
|
1085
|
+
* @param worktree - The target's git state; only a tracked path is ever deleted.
|
|
935
1086
|
* @param target - The directory to delete from.
|
|
936
1087
|
* @returns The paths removed.
|
|
937
1088
|
* @throws {@link ScaffoldError} coded `INVALID` when an argument is not the
|
|
@@ -952,7 +1103,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
952
1103
|
* any foreign finding, including one the deletion itself would skip, because a
|
|
953
1104
|
* preview stale anywhere is stale evidence.
|
|
954
1105
|
*/
|
|
955
|
-
remove(plan: Plan, audit: Audit,
|
|
1106
|
+
remove(plan: Plan, audit: Audit, worktree: Worktree, target: string): MaterializeResult;
|
|
956
1107
|
/**
|
|
957
1108
|
* Tear the materializer down. Every later call throws, and teardown is idempotent.
|
|
958
1109
|
*
|
|
@@ -1078,7 +1229,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1078
1229
|
*
|
|
1079
1230
|
* @param plan - The compiled plan that decides which paths are foreign.
|
|
1080
1231
|
* @param audit - The preview returned by this materializer's `audit` method; it must agree with the candidate set this call re-derives.
|
|
1081
|
-
* @param
|
|
1232
|
+
* @param worktree - The target's git state; only a tracked path is ever deleted.
|
|
1082
1233
|
* @param target - The directory to delete from.
|
|
1083
1234
|
* @returns The paths removed.
|
|
1084
1235
|
*
|
|
@@ -1101,7 +1252,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1101
1252
|
* any foreign finding, including one the deletion itself would skip, because a
|
|
1102
1253
|
* preview stale anywhere is stale evidence.
|
|
1103
1254
|
*/
|
|
1104
|
-
remove(plan: Plan, audit: Audit,
|
|
1255
|
+
remove(plan: Plan, audit: Audit, worktree: Worktree, target: string): MaterializeResult;
|
|
1105
1256
|
/**
|
|
1106
1257
|
* Tear the materializer down. Every later call throws, and teardown is idempotent.
|
|
1107
1258
|
*
|
|
@@ -1114,19 +1265,21 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1114
1265
|
* Options for the materializer.
|
|
1115
1266
|
*
|
|
1116
1267
|
* @remarks
|
|
1117
|
-
* `host` is the vendored data root host-origin artifacts are copied from
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
* through the manifest.
|
|
1268
|
+
* `host` is the vendored data root host-origin artifacts are copied from, in
|
|
1269
|
+
* either representation. A string is a directory: it defaults to this package's
|
|
1270
|
+
* own vendored root, resolved from the installed module's location rather than
|
|
1271
|
+
* the caller's working directory, and a directory that carries no manifest
|
|
1272
|
+
* beside it maps artifact paths one to one instead of through the manifest. A
|
|
1273
|
+
* {@link Host} is the same root supplied as a value, so a caller that already
|
|
1274
|
+
* holds the bytes never stages them to a directory to be read back.
|
|
1122
1275
|
*/
|
|
1123
1276
|
export declare interface MaterializerOptions {
|
|
1124
|
-
readonly host?: string;
|
|
1277
|
+
readonly host?: string | Host;
|
|
1125
1278
|
readonly on?: EmitterHooks<MaterializerEventMap>;
|
|
1126
1279
|
readonly error?: EmitterErrorHandler;
|
|
1127
1280
|
}
|
|
1128
1281
|
|
|
1129
|
-
/** Maximum characters one
|
|
1282
|
+
/** Maximum characters one repository branch may carry. */
|
|
1130
1283
|
export declare const MAX_BRANCH_LENGTH = 255;
|
|
1131
1284
|
|
|
1132
1285
|
/** Maximum characters one caller-supplied upstream endpoint may carry. */
|
|
@@ -1304,10 +1457,38 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1304
1457
|
*/
|
|
1305
1458
|
export declare function readFileText(root: string, path: string, limit?: number): string | undefined;
|
|
1306
1459
|
|
|
1460
|
+
/**
|
|
1461
|
+
* Reads the installed vendored host floor as a value.
|
|
1462
|
+
*
|
|
1463
|
+
* @param root - The vendored host root. Default: the installed package's
|
|
1464
|
+
* vendored root, resolved from this module's location.
|
|
1465
|
+
* @returns The verified manifest and the exact bytes of every declared entry.
|
|
1466
|
+
* @throws `ScaffoldError('TARGET', …)` when the root is not a readable physical
|
|
1467
|
+
* directory, its manifest is absent or unreadable, the manifest does not verify,
|
|
1468
|
+
* or a declared file is unreadable or misses its digest.
|
|
1469
|
+
*
|
|
1470
|
+
* @remarks
|
|
1471
|
+
* Reads the same default floor the {@link Materializer} uses. Each declared
|
|
1472
|
+
* file is addressed through the manifest's storage name and retained under its
|
|
1473
|
+
* destination, so the returned value has the same shape as the installed root.
|
|
1474
|
+
* When this module executes from TypeScript source, the committed inventory is
|
|
1475
|
+
* the manifest and each checkout destination supplies its bytes. The emitted
|
|
1476
|
+
* module reads the staged `manifest.json` file and each storage path instead.
|
|
1477
|
+
*
|
|
1478
|
+
* @example
|
|
1479
|
+
* ```ts
|
|
1480
|
+
* import { readHostFloor } from '@orkestrel/scaffold/server'
|
|
1481
|
+
*
|
|
1482
|
+
* readHostFloor().manifest // the installed floor's verified membership
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
export declare function readHostFloor(root?: string): Host;
|
|
1486
|
+
|
|
1307
1487
|
/**
|
|
1308
1488
|
* Read a vendored host's manifest, when it carries one.
|
|
1309
1489
|
*
|
|
1310
1490
|
* @param host - The vendored host root to read.
|
|
1491
|
+
* @param name - The root-relative manifest path. Default: `manifest.json`.
|
|
1311
1492
|
* @returns The manifest, or `undefined` when the host carries none.
|
|
1312
1493
|
* @throws `ScaffoldError('INVALID', …)` when `host` is not a host path.
|
|
1313
1494
|
* @throws `ScaffoldError('TARGET', …)` when the manifest is there but cannot be
|
|
@@ -1333,7 +1514,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1333
1514
|
* readHostManifest('./dist/host') // the manifest, or undefined for a raw root
|
|
1334
1515
|
* ```
|
|
1335
1516
|
*/
|
|
1336
|
-
export declare function readHostManifest(host: string): HostManifest | undefined;
|
|
1517
|
+
export declare function readHostManifest(host: string, name?: string): HostManifest | undefined;
|
|
1337
1518
|
|
|
1338
1519
|
/**
|
|
1339
1520
|
* Derive one vendored-host manifest entry from a file in a checkout.
|
|
@@ -1359,7 +1540,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1359
1540
|
* import { readManifestEntry } from '@orkestrel/scaffold/server'
|
|
1360
1541
|
*
|
|
1361
1542
|
* readManifestEntry('.gitignore', '/tmp/checkout/.gitignore')
|
|
1362
|
-
* // { storage: 'dotfiles/gitignore', destination: '.gitignore', executable: false }
|
|
1543
|
+
* // { storage: 'dotfiles/gitignore', destination: '.gitignore', executable: false, digest: '...' }
|
|
1363
1544
|
* ```
|
|
1364
1545
|
*/
|
|
1365
1546
|
export declare function readManifestEntry(destination: string, source: string): ManifestEntry | undefined;
|
|
@@ -1395,25 +1576,6 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1395
1576
|
*/
|
|
1396
1577
|
export declare function readSnapshot(target: string, paths: readonly string[]): Snapshot;
|
|
1397
1578
|
|
|
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
1579
|
/**
|
|
1418
1580
|
* The Windows device names that stay reserved even when an extension follows.
|
|
1419
1581
|
*
|
|
@@ -1514,6 +1676,42 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1514
1676
|
*/
|
|
1515
1677
|
export declare function resolveRealPath(path: string): string | undefined;
|
|
1516
1678
|
|
|
1679
|
+
/**
|
|
1680
|
+
* Stage the named destinations of a value host into a private root.
|
|
1681
|
+
*
|
|
1682
|
+
* @param host - The host whose bytes are written, keyed by destination.
|
|
1683
|
+
* @param root - The private directory to fill; it must already be a directory
|
|
1684
|
+
* this process may write into.
|
|
1685
|
+
* @param destinations - The destinations to stage, each declared by `host`.
|
|
1686
|
+
* @returns The entry staged for each destination, in the order requested.
|
|
1687
|
+
* @throws `ScaffoldError('INVALID', …)` when `root` is not a host path or a
|
|
1688
|
+
* storage name leaves it.
|
|
1689
|
+
* @throws `ScaffoldError('TARGET', …)` when a destination is one the host does
|
|
1690
|
+
* not declare, carries no bytes, or carries bytes that miss its declared digest.
|
|
1691
|
+
* @throws `ScaffoldError('WRITE', …)` when a file cannot be written or does not
|
|
1692
|
+
* read back as the bytes it was given.
|
|
1693
|
+
*
|
|
1694
|
+
* @remarks
|
|
1695
|
+
* Each file lands under the storage name the manifest declares and takes the
|
|
1696
|
+
* executable bit that manifest records, so a root filled from a value is the
|
|
1697
|
+
* same shape as one staged from a checkout and a reader cannot tell them apart.
|
|
1698
|
+
* That is what lets a mutation copy real files with real modes from bytes a
|
|
1699
|
+
* caller supplied, instead of degrading them to plain text writes.
|
|
1700
|
+
*
|
|
1701
|
+
* The bytes are digested before the write and the staged file after it, so a
|
|
1702
|
+
* value that disagrees with its own manifest is told apart from a write that
|
|
1703
|
+
* did not land.
|
|
1704
|
+
*
|
|
1705
|
+
* @example
|
|
1706
|
+
* ```ts
|
|
1707
|
+
* import { stageBytes } from '@orkestrel/scaffold/server'
|
|
1708
|
+
*
|
|
1709
|
+
* stageBytes(host, '/tmp/orkestrel-host-a1b2', ['scripts/codex.sh'])
|
|
1710
|
+
* // [{ storage: 'scripts/codex.sh', destination: 'scripts/codex.sh', executable: true, digest: '…' }]
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1713
|
+
export declare function stageBytes(host: Host, root: string, destinations: readonly string[]): readonly ManifestEntry[];
|
|
1714
|
+
|
|
1517
1715
|
/**
|
|
1518
1716
|
* Stage a vendored host root from a real checkout.
|
|
1519
1717
|
*
|
|
@@ -1568,6 +1766,33 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1568
1766
|
*/
|
|
1569
1767
|
export declare function stageHost(checkout: string, host: string): readonly ManifestEntry[];
|
|
1570
1768
|
|
|
1769
|
+
/**
|
|
1770
|
+
* Stages the committed inventory of the files a vendored host carries.
|
|
1771
|
+
*
|
|
1772
|
+
* @param checkout - The checkout whose vendored paths are inventoried.
|
|
1773
|
+
* @param path - The host path where the JSON inventory is written.
|
|
1774
|
+
* @returns The validated manifest written to `path`.
|
|
1775
|
+
* @throws `ScaffoldError('INVALID', …)` when `path` is not a host path.
|
|
1776
|
+
* @throws `ScaffoldError('WRITE', …)` when a temporary host or the inventory
|
|
1777
|
+
* cannot be written or removed.
|
|
1778
|
+
* @throws `ScaffoldError('TARGET', …)` when the staged inventory does not read
|
|
1779
|
+
* back through the manifest validator.
|
|
1780
|
+
*
|
|
1781
|
+
* @remarks
|
|
1782
|
+
* Uses {@link stageHost} as the single vendored-path expansion. The temporary
|
|
1783
|
+
* host supplies the same entries, roots, per-file digests, and membership
|
|
1784
|
+
* digest as the published host while the requested output remains one JSON
|
|
1785
|
+
* file.
|
|
1786
|
+
*
|
|
1787
|
+
* @example
|
|
1788
|
+
* ```ts
|
|
1789
|
+
* import { stageInventory } from '@orkestrel/scaffold/server'
|
|
1790
|
+
*
|
|
1791
|
+
* stageInventory(process.cwd(), 'host.json') // the committed host inventory
|
|
1792
|
+
* ```
|
|
1793
|
+
*/
|
|
1794
|
+
export declare function stageInventory(checkout: string, path: string): HostManifest;
|
|
1795
|
+
|
|
1571
1796
|
/**
|
|
1572
1797
|
* The reading spine: one bounded, unauthenticated, redirect-free request per answer.
|
|
1573
1798
|
*
|
|
@@ -1586,6 +1811,11 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1586
1811
|
* there is no fleet to report, so an unreachable or malformed list is a coded
|
|
1587
1812
|
* `FETCH` failure.
|
|
1588
1813
|
*
|
|
1814
|
+
* The vendored-file inventory is the other read a whole call rests on, and it
|
|
1815
|
+
* fails the other way: it fails every row of its call rather than throwing, so
|
|
1816
|
+
* the caller receives one whole dead answer it can replace with one whole
|
|
1817
|
+
* baseline instead of a mixture it would have to reconcile.
|
|
1818
|
+
*
|
|
1589
1819
|
* Requests are unauthenticated because every fleet repository is public, and
|
|
1590
1820
|
* they follow no redirect, so a misconfigured or hostile endpoint cannot move a
|
|
1591
1821
|
* read to another host. Each one is bounded by its endpoint's timeout and by the
|
|
@@ -1608,7 +1838,7 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1608
1838
|
export declare class Upstream implements UpstreamInterface {
|
|
1609
1839
|
#private;
|
|
1610
1840
|
/**
|
|
1611
|
-
* Construct a reader over one
|
|
1841
|
+
* Construct a reader over one raw content host and one registry.
|
|
1612
1842
|
*
|
|
1613
1843
|
* @param options - The endpoints, the request bounds, the initial
|
|
1614
1844
|
* listeners, and the listener-error handler.
|
|
@@ -1684,372 +1914,447 @@ export declare class Materializer implements MaterializerInterface {
|
|
|
1684
1914
|
*/
|
|
1685
1915
|
fetch(names: readonly string[], current: Snapshot): Promise<readonly Mirror[]>;
|
|
1686
1916
|
/**
|
|
1687
|
-
*
|
|
1917
|
+
* Read each named vendored file from the repository, beside the target bytes it answers for.
|
|
1688
1918
|
*
|
|
1689
|
-
* @
|
|
1690
|
-
* @
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
*
|
|
1919
|
+
* @param paths - The target-relative vendored paths to read.
|
|
1920
|
+
* @param current - The target files as exact bytes, keyed by the same paths.
|
|
1921
|
+
* @returns One file verdict per path, in input order.
|
|
1922
|
+
* @throws {@link ScaffoldError} coded `INVALID` when `paths` is not a bounded
|
|
1923
|
+
* list of target-relative paths or `current` is not a snapshot, and
|
|
1924
|
+
* `DESTROYED` when the reader is torn down before or during the call.
|
|
1694
1925
|
*
|
|
1695
1926
|
* @remarks
|
|
1696
|
-
* The
|
|
1697
|
-
*
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1700
|
-
*
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
1927
|
+
* The committed inventory is read once per call and decides every row, so a
|
|
1928
|
+
* path whose declared digest already matches the target's own bytes is `found`
|
|
1929
|
+
* without a request and the call spends nothing on it. An inventory that
|
|
1930
|
+
* produces no answer fails every row of the call rather than leaving some rows
|
|
1931
|
+
* live and some dead, which is what leaves the caller one whole baseline to
|
|
1932
|
+
* fall back to. A path the inventory does not name is `missing`.
|
|
1933
|
+
*
|
|
1934
|
+
* A fetched response's decoded content is verified against the digest the
|
|
1935
|
+
* inventory declares for that path, before any character decoding. Transport
|
|
1936
|
+
* encoding is transparent and does not enter the comparison, so content that
|
|
1937
|
+
* does not hash to the inventory's claim fails its row rather than reaching a
|
|
1938
|
+
* write. That is integrity against a single committed baseline, not
|
|
1939
|
+
* authenticity: it detects truncated, substituted, or stale content, and it
|
|
1940
|
+
* says nothing about who published the inventory.
|
|
1941
|
+
*
|
|
1942
|
+
* A guide mirror is never answered here whatever the caller asks for and
|
|
1943
|
+
* whatever the target holds, because those bytes belong to `fetch` and to the
|
|
1944
|
+
* mirror verb that writes them.
|
|
1703
1945
|
*
|
|
1704
1946
|
* @example
|
|
1705
1947
|
* ```ts
|
|
1706
1948
|
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
1707
1949
|
*
|
|
1708
1950
|
* const upstream = new Upstream()
|
|
1709
|
-
*
|
|
1951
|
+
* await upstream.read(['AGENTS.md'], { 'AGENTS.md': '2320416745' })
|
|
1710
1952
|
* upstream.destroy()
|
|
1711
1953
|
* ```
|
|
1712
1954
|
*/
|
|
1713
|
-
|
|
1955
|
+
read(paths: readonly string[], current: Snapshot): Promise<readonly HostFile[]>;
|
|
1714
1956
|
/**
|
|
1715
|
-
*
|
|
1957
|
+
* Catalog the published fleet from the registry's organization package list.
|
|
1716
1958
|
*
|
|
1717
|
-
* @returns
|
|
1959
|
+
* @returns One row per published package, sorted by name.
|
|
1960
|
+
* @throws {@link ScaffoldError} coded `FETCH` when the organization package
|
|
1961
|
+
* list is unreachable, malformed, empty, or larger than one bounded
|
|
1962
|
+
* collection, and `DESTROYED` when the reader is torn down before or during
|
|
1963
|
+
* the call.
|
|
1964
|
+
*
|
|
1965
|
+
* @remarks
|
|
1966
|
+
* The organization list is the exact membership the registry publishes, not a
|
|
1967
|
+
* relevance search, and it is the whole reason this answer can claim to be the
|
|
1968
|
+
* fleet. So it fails hard where a per-package lookup fails soft: an empty or
|
|
1969
|
+
* unreadable list would otherwise be written into a target's catalog table as
|
|
1970
|
+
* a fleet with no packages in it. A package whose own version lookup then
|
|
1971
|
+
* fails still keeps its row, carrying the cause instead of a version, because
|
|
1972
|
+
* the list already proved the package is published.
|
|
1973
|
+
*
|
|
1974
|
+
* @example
|
|
1975
|
+
* ```ts
|
|
1976
|
+
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
1977
|
+
*
|
|
1978
|
+
* const upstream = new Upstream()
|
|
1979
|
+
* const entries = await upstream.catalog()
|
|
1980
|
+
* upstream.destroy()
|
|
1981
|
+
* ```
|
|
1982
|
+
*/
|
|
1983
|
+
catalog(): Promise<readonly CatalogEntry[]>;
|
|
1984
|
+
/**
|
|
1985
|
+
* Tear the reader down, aborting every request in flight. Teardown is idempotent.
|
|
1986
|
+
*
|
|
1987
|
+
* @returns Nothing.
|
|
1988
|
+
*
|
|
1989
|
+
* @remarks
|
|
1990
|
+
* A call still in flight rejects with a `DESTROYED` error rather than
|
|
1991
|
+
* resolving to a partial answer, because half a fleet reads exactly like a
|
|
1992
|
+
* whole one.
|
|
1993
|
+
*
|
|
1994
|
+
* @example
|
|
1995
|
+
* ```ts
|
|
1996
|
+
* import { Upstream } from '@orkestrel/scaffold/server'
|
|
1997
|
+
*
|
|
1998
|
+
* const upstream = new Upstream()
|
|
1999
|
+
* upstream.destroy()
|
|
2000
|
+
* upstream.emitter.destroyed // true
|
|
2001
|
+
* ```
|
|
2002
|
+
*/
|
|
2003
|
+
destroy(): void;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
/**
|
|
2007
|
+
* The upstream reader's observation channel.
|
|
1718
2008
|
*
|
|
1719
2009
|
* @remarks
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1722
|
-
*
|
|
2010
|
+
* Each verdict is published whole rather than as a name beside a summary, so a
|
|
2011
|
+
* listener reads the same value the call returns and a failed lookup is told
|
|
2012
|
+
* apart from a successful one by the verdict's own discriminant rather than by
|
|
2013
|
+
* which event carried it.
|
|
2014
|
+
*/
|
|
2015
|
+
export declare type UpstreamEventMap = {
|
|
2016
|
+
readonly release: readonly [release: Release];
|
|
2017
|
+
readonly mirror: readonly [mirror: Mirror];
|
|
2018
|
+
readonly file: readonly [file: HostFile];
|
|
2019
|
+
readonly error: readonly [error: unknown];
|
|
2020
|
+
readonly destroy: readonly [];
|
|
2021
|
+
};
|
|
2022
|
+
|
|
2023
|
+
/**
|
|
2024
|
+
* The upstream contract: the package's only network reader, and it never writes.
|
|
1723
2025
|
*
|
|
1724
|
-
* @
|
|
1725
|
-
*
|
|
1726
|
-
*
|
|
2026
|
+
* @remarks
|
|
2027
|
+
* A per-package failure is collected as a verdict carrying its cause, not
|
|
2028
|
+
* thrown, so one unreachable package never costs the caller the rest of the
|
|
2029
|
+
* answer. The organization list is the exception: without it there is no fleet
|
|
2030
|
+
* to report, so an unreachable or malformed list is a coded failure.
|
|
1727
2031
|
*
|
|
1728
|
-
*
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1731
|
-
*
|
|
2032
|
+
* The vendored-file inventory is the other whole-answer read, and it fails
|
|
2033
|
+
* softly: an inventory that produces no answer fails every row of that call
|
|
2034
|
+
* rather than throwing, which is what leaves the caller one whole baseline to
|
|
2035
|
+
* fall back to instead of a mixture.
|
|
1732
2036
|
*/
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2037
|
+
export declare interface UpstreamInterface {
|
|
2038
|
+
readonly emitter: EmitterInterface<UpstreamEventMap>;
|
|
2039
|
+
/**
|
|
2040
|
+
* Look up the newest release each declared range admits.
|
|
2041
|
+
*
|
|
2042
|
+
* @param dependencies - The declared dependencies to look up.
|
|
2043
|
+
* @returns One release verdict per dependency, in input order.
|
|
2044
|
+
*/
|
|
2045
|
+
lookup(dependencies: readonly Dependency[]): Promise<readonly Release[]>;
|
|
2046
|
+
/**
|
|
2047
|
+
* Fetch each named package's guide, beside the local mirror it answers for.
|
|
2048
|
+
*
|
|
2049
|
+
* @param names - The packages to fetch: the target's declared set, or the whole organization.
|
|
2050
|
+
* @param current - The target's local mirrors as exact bytes, keyed by mirror path.
|
|
2051
|
+
* @returns One mirror verdict per name, in input order.
|
|
2052
|
+
*/
|
|
2053
|
+
fetch(names: readonly string[], current: Snapshot): Promise<readonly Mirror[]>;
|
|
2054
|
+
/**
|
|
2055
|
+
* Read each named vendored file from the repository, beside the target bytes it answers for.
|
|
2056
|
+
*
|
|
2057
|
+
* @param paths - The target-relative vendored paths to read.
|
|
2058
|
+
* @param current - The target files as exact bytes, keyed by the same paths.
|
|
2059
|
+
* @returns One file verdict per path, in input order.
|
|
2060
|
+
*/
|
|
2061
|
+
read(paths: readonly string[], current: Snapshot): Promise<readonly HostFile[]>;
|
|
2062
|
+
/**
|
|
2063
|
+
* Catalog the published fleet from the registry's organization package list.
|
|
2064
|
+
*
|
|
2065
|
+
* @returns One row per published package, sorted by name.
|
|
2066
|
+
*/
|
|
2067
|
+
catalog(): Promise<readonly CatalogEntry[]>;
|
|
2068
|
+
/**
|
|
2069
|
+
* Tear the reader down, aborting every request in flight. Teardown is idempotent.
|
|
2070
|
+
*
|
|
2071
|
+
* @returns Nothing.
|
|
2072
|
+
*/
|
|
2073
|
+
destroy(): void;
|
|
2074
|
+
}
|
|
1751
2075
|
|
|
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
|
-
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Options for the upstream reader.
|
|
2078
|
+
*
|
|
2079
|
+
* @remarks
|
|
2080
|
+
* The endpoints are grouped under the entity each configures: `repository`
|
|
2081
|
+
* takes the raw content host's `base`, its `branch`, and its `timeout`;
|
|
2082
|
+
* `registry` takes the registry's `base` and `timeout`. One raw content host
|
|
2083
|
+
* serves both the fleet's guides and this package's own vendored files, so one
|
|
2084
|
+
* group configures both. `concurrency` bounds requests in
|
|
2085
|
+
* flight and `retries` opts into per-request retry on a transport fault.
|
|
2086
|
+
* `limit` bounds the bytes read from one response body and `budget` bounds the
|
|
2087
|
+
* bytes read across a whole call, so neither one oversized answer nor many
|
|
2088
|
+
* small ones can exhaust the caller. Every request is unauthenticated and
|
|
2089
|
+
* follows no redirect.
|
|
2090
|
+
*/
|
|
2091
|
+
export declare interface UpstreamOptions {
|
|
2092
|
+
readonly repository?: {
|
|
2093
|
+
readonly base?: string;
|
|
2094
|
+
readonly branch?: string;
|
|
2095
|
+
readonly timeout?: number;
|
|
2096
|
+
};
|
|
2097
|
+
readonly registry?: {
|
|
2098
|
+
readonly base?: string;
|
|
2099
|
+
readonly timeout?: number;
|
|
2100
|
+
};
|
|
2101
|
+
readonly concurrency?: number;
|
|
2102
|
+
readonly retries?: number;
|
|
2103
|
+
readonly limit?: number;
|
|
2104
|
+
readonly budget?: number;
|
|
2105
|
+
readonly on?: EmitterHooks<UpstreamEventMap>;
|
|
2106
|
+
readonly error?: EmitterErrorHandler;
|
|
2107
|
+
}
|
|
1791
2108
|
|
|
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
|
-
}
|
|
2109
|
+
/**
|
|
2110
|
+
* What git reports about a target's working tree.
|
|
2111
|
+
*
|
|
2112
|
+
* @remarks
|
|
2113
|
+
* `tracked` is the only set a deletion may draw from: git does not report the
|
|
2114
|
+
* loss of an untracked path and `git diff` cannot restore it, so an ignored
|
|
2115
|
+
* path such as an installed dependency tree, a build output, or an editor
|
|
2116
|
+
* directory survives every verb. `dirty` is every path carrying an uncommitted
|
|
2117
|
+
* change, taken repo-wide rather than over a write set, because deletion makes
|
|
2118
|
+
* the write set the whole workspace. A clean tree is an empty `dirty`. A target
|
|
2119
|
+
* that is not a git repository yields no `Worktree` at all, so the caller
|
|
2120
|
+
* decides what to do about that rather than reading it out of an invented
|
|
2121
|
+
* empty value.
|
|
2122
|
+
*/
|
|
2123
|
+
export declare interface Worktree {
|
|
2124
|
+
readonly tracked: readonly string[];
|
|
2125
|
+
readonly dirty: readonly string[];
|
|
2126
|
+
}
|
|
1822
2127
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2128
|
+
/**
|
|
2129
|
+
* One physical directory identity captured across a write transaction.
|
|
2130
|
+
*
|
|
2131
|
+
* @remarks
|
|
2132
|
+
* Device and inode locate the directory and do not date it. Two directories
|
|
2133
|
+
* occupying one slot at different times share an anchor, so an anchor proves
|
|
2134
|
+
* where a write lands and never that the directory was left alone.
|
|
2135
|
+
*/
|
|
2136
|
+
export declare interface WriteAnchor {
|
|
2137
|
+
readonly path: string;
|
|
2138
|
+
readonly device: number;
|
|
2139
|
+
readonly inode: number;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
/** The final directory anchor of a write transaction and the subset one call created. */
|
|
2143
|
+
export declare interface WriteDirectoryResult {
|
|
2144
|
+
readonly anchor: WriteAnchor;
|
|
2145
|
+
readonly created: readonly WriteAnchor[];
|
|
2146
|
+
}
|
|
1842
2147
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
2148
|
+
/**
|
|
2149
|
+
* One destination snapshot captured before a write and required to survive it.
|
|
2150
|
+
*
|
|
2151
|
+
* @remarks
|
|
2152
|
+
* `device`, `inode`, `modified`, `size`, and `digest` are present only where
|
|
2153
|
+
* the observed shape supplies them.
|
|
2154
|
+
*/
|
|
2155
|
+
export declare interface WriteExpectation {
|
|
2156
|
+
readonly path: string;
|
|
2157
|
+
readonly shape: 'absent' | 'file' | 'directory';
|
|
2158
|
+
readonly device?: number;
|
|
2159
|
+
readonly inode?: number;
|
|
2160
|
+
readonly modified?: number;
|
|
2161
|
+
readonly size?: number;
|
|
2162
|
+
readonly digest?: string;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/** The narrower caller-observed destination state a write transaction must still match. */
|
|
2166
|
+
export declare interface WritePrecondition {
|
|
2167
|
+
readonly path: string;
|
|
2168
|
+
readonly shape: 'absent' | 'file';
|
|
2169
|
+
readonly digest?: string;
|
|
2170
|
+
}
|
|
1866
2171
|
|
|
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;
|
|
2172
|
+
/**
|
|
2173
|
+
* One staged, reversible mutation of one target directory.
|
|
2174
|
+
*
|
|
2175
|
+
* @remarks
|
|
2176
|
+
* The transaction owns a private root beside the target — a sibling directory on
|
|
2177
|
+
* the same volume, so every promotion is a rename rather than a copy. Staging
|
|
2178
|
+
* writes go into that root and nothing else, so a call that fails while staging
|
|
2179
|
+
* has not touched the target at all. Commit is the only step that mutates the
|
|
2180
|
+
* target, and it is the only step that can need rolling back.
|
|
2181
|
+
*
|
|
2182
|
+
* A precondition and an expectation hold a destination still. A **precondition** is what the caller
|
|
2183
|
+
* observed earlier and is checked once, at construction, so a target that moved
|
|
2184
|
+
* between the caller's read and this transaction fails before anything is
|
|
2185
|
+
* created. An **expectation** is captured here, at construction, and re-checked
|
|
2186
|
+
* at commit, so a target that moves while the write is being staged fails before
|
|
2187
|
+
* anything is promoted.
|
|
2188
|
+
*
|
|
2189
|
+
* What this provides, exactly:
|
|
2190
|
+
*
|
|
2191
|
+
* - **Across destinations, staged-then-swapped with rollback on a caught
|
|
2192
|
+
* failure.** A failure part way through commit restores every destination it
|
|
2193
|
+
* already promoted, restores every file it already took, and removes every
|
|
2194
|
+
* directory it created, then reports what recovery could not undo. This is
|
|
2195
|
+
* measured: a promotion is driven to fail after an earlier one landed, and the
|
|
2196
|
+
* earlier destination is read back.
|
|
2197
|
+
* - **No partly written destination.** Every file is written whole into the
|
|
2198
|
+
* private root and digested there before commit, so a destination never
|
|
2199
|
+
* receives bytes that were still being produced.
|
|
2200
|
+
* - **Containment, not continuity, of the directories it creates.** Every
|
|
2201
|
+
* ancestor is re-read between `mkdir` calls and again before the first
|
|
2202
|
+
* promotion, so an ancestor that became a file, a symlink, a directory
|
|
2203
|
+
* elsewhere, or nothing is refused. An ancestor deleted and recreated under
|
|
2204
|
+
* the same name can receive its old inode back and is indistinguishable here
|
|
2205
|
+
* from one that never moved.
|
|
2206
|
+
* - **No crash atomicity across destinations.** A process killed between
|
|
2207
|
+
* promotions leaves the target holding some new files and some old ones, and
|
|
2208
|
+
* leaves the private root behind. Nothing here is a journal, and the private
|
|
2209
|
+
* root's name is the only record a later run could read.
|
|
2210
|
+
*
|
|
2211
|
+
* A destination is preserved by hard link and then replaced by a single
|
|
2212
|
+
* `rename`, rather than moved aside and replaced, so the path continues to name
|
|
2213
|
+
* the old file right up to the swap. That is a property of `rename` on the host,
|
|
2214
|
+
* not one this package's tests measure: telling it apart from move-then-replace
|
|
2215
|
+
* needs a reader observing the destination inside the swap, and no test here
|
|
2216
|
+
* does that. Read the claim as the mechanism it describes, not as a proven
|
|
2217
|
+
* guarantee about a concurrent reader.
|
|
2218
|
+
*
|
|
2219
|
+
* A path names a file this transaction writes, takes, or establishes as a
|
|
2220
|
+
* directory; every path is target-relative and is measured by the portable-path
|
|
2221
|
+
* law. An expectation and a precondition both name the resolved destination
|
|
2222
|
+
* instead, because that is the path each is re-read at.
|
|
2223
|
+
*
|
|
2224
|
+
* @example
|
|
2225
|
+
* ```ts
|
|
2226
|
+
* import { WriteTransaction } from '@orkestrel/scaffold/server'
|
|
2227
|
+
*
|
|
2228
|
+
* const transaction = new WriteTransaction('./packages/router', ['AGENTS.md'])
|
|
2229
|
+
* try {
|
|
2230
|
+
* transaction.write('AGENTS.md', '# Agents\n')
|
|
2231
|
+
* transaction.commit() // ['AGENTS.md']
|
|
2232
|
+
* } finally {
|
|
2233
|
+
* transaction.discard()
|
|
2234
|
+
* }
|
|
2235
|
+
* ```
|
|
2236
|
+
*/
|
|
2237
|
+
export declare class WriteTransaction {
|
|
2238
|
+
#private;
|
|
1959
2239
|
/**
|
|
1960
|
-
*
|
|
2240
|
+
* Open a transaction over one target directory.
|
|
1961
2241
|
*
|
|
1962
|
-
* @param
|
|
1963
|
-
* @param
|
|
1964
|
-
* @
|
|
1965
|
-
*
|
|
1966
|
-
|
|
1967
|
-
*
|
|
1968
|
-
*
|
|
2242
|
+
* @param target - The directory every path is written beneath.
|
|
2243
|
+
* @param paths - Every target-relative path this transaction may touch.
|
|
2244
|
+
* @param preconditions - The caller-observed destination states the whole
|
|
2245
|
+
* transaction is held to, each naming a resolved destination of `paths`.
|
|
2246
|
+
* @throws {@link ScaffoldError} coded `INVALID` when an argument is off
|
|
2247
|
+
* contract, `TARGET` when a destination is a shape this package will not write
|
|
2248
|
+
* over or no longer matches its precondition, and `WRITE` when the private
|
|
2249
|
+
* root cannot be established.
|
|
1969
2250
|
*
|
|
1970
2251
|
* @remarks
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
2252
|
+
* Nothing is created until every destination has been inspected and every
|
|
2253
|
+
* precondition has held, so a refused transaction leaves no residue at all.
|
|
2254
|
+
* The private root is created last and carries a random name, so two
|
|
2255
|
+
* transactions over one target never collide.
|
|
1973
2256
|
*/
|
|
1974
|
-
|
|
2257
|
+
constructor(target: string, paths: readonly string[], preconditions?: readonly WritePrecondition[]);
|
|
2258
|
+
/** The resolved directory every path is written beneath. */
|
|
2259
|
+
get target(): string;
|
|
2260
|
+
/** What each destination held when the transaction opened, in path order. */
|
|
2261
|
+
get expectations(): readonly WriteExpectation[];
|
|
2262
|
+
/** Whether the transaction can still be committed or discarded. */
|
|
2263
|
+
get open(): boolean;
|
|
1975
2264
|
/**
|
|
1976
|
-
* Stage one
|
|
2265
|
+
* Stage one text file.
|
|
1977
2266
|
*
|
|
1978
2267
|
* @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.
|
|
2268
|
+
* @param content - The exact UTF-8 text the destination must hold.
|
|
1981
2269
|
* @returns Nothing.
|
|
1982
2270
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
1983
2271
|
* transaction opened or is already staged, `TARGET` when the destination holds
|
|
1984
|
-
* a directory
|
|
1985
|
-
*
|
|
2272
|
+
* a directory, and `WRITE` when the staged file cannot be written or does not
|
|
2273
|
+
* carry the bytes it was given.
|
|
1986
2274
|
*
|
|
1987
2275
|
* @remarks
|
|
1988
|
-
* The
|
|
1989
|
-
* a
|
|
2276
|
+
* The staged file is read back and digested against the text it was given, so
|
|
2277
|
+
* a partial or interrupted write is refused here rather than promoted later.
|
|
1990
2278
|
*/
|
|
1991
|
-
|
|
2279
|
+
write(path: string, content: string): void;
|
|
1992
2280
|
/**
|
|
1993
|
-
*
|
|
2281
|
+
* Stage one byte-for-byte copy of a file that already exists on this host.
|
|
1994
2282
|
*
|
|
1995
|
-
* @param path - The target-relative
|
|
1996
|
-
* @
|
|
2283
|
+
* @param path - The target-relative path to write.
|
|
2284
|
+
* @param source - The resolved absolute path to copy the bytes from.
|
|
2285
|
+
* @param executable - If `true`, the destination carries the executable bit; if `false`, it does not.
|
|
2286
|
+
* @returns Nothing.
|
|
1997
2287
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
1998
|
-
* transaction opened, `TARGET` when the destination holds
|
|
1999
|
-
*
|
|
2288
|
+
* transaction opened or is already staged, `TARGET` when the destination holds
|
|
2289
|
+
* a directory or the source is not a physical file, and `WRITE` when the copy
|
|
2290
|
+
* cannot be made or does not digest to the source's bytes.
|
|
2000
2291
|
*
|
|
2001
2292
|
* @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.
|
|
2293
|
+
* The source is digested before the copy and the copy is digested after it, so
|
|
2294
|
+
* a source that changed mid-copy is refused instead of staged.
|
|
2007
2295
|
*/
|
|
2008
|
-
|
|
2296
|
+
copy(path: string, source: string, executable: boolean): void;
|
|
2009
2297
|
/**
|
|
2010
|
-
*
|
|
2298
|
+
* Establish one directory inside the target, one segment at a time.
|
|
2011
2299
|
*
|
|
2012
|
-
* @param path - The target-relative
|
|
2013
|
-
* @returns
|
|
2300
|
+
* @param path - The target-relative directory to establish.
|
|
2301
|
+
* @returns The directory's identity and every segment this call created.
|
|
2014
2302
|
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
2015
|
-
* transaction opened
|
|
2016
|
-
*
|
|
2303
|
+
* transaction opened, `TARGET` when the destination holds a file, and `WRITE`
|
|
2304
|
+
* when a segment cannot be created or changed while it was being created.
|
|
2017
2305
|
*
|
|
2018
2306
|
* @remarks
|
|
2019
|
-
*
|
|
2020
|
-
*
|
|
2307
|
+
* A directory is created immediately rather than staged, because creating one
|
|
2308
|
+
* destroys nothing: rollback removes exactly the segments this call created,
|
|
2309
|
+
* innermost first, and leaves every segment that was already there. Each
|
|
2310
|
+
* created segment is captured by device and inode, so a segment swapped
|
|
2311
|
+
* underneath the transaction is detected rather than written into.
|
|
2021
2312
|
*/
|
|
2022
|
-
|
|
2313
|
+
establish(path: string): WriteDirectoryResult;
|
|
2023
2314
|
/**
|
|
2024
|
-
*
|
|
2315
|
+
* Mark one file for deletion at commit.
|
|
2025
2316
|
*
|
|
2026
|
-
* @
|
|
2027
|
-
*
|
|
2028
|
-
* @throws {@link ScaffoldError} coded `
|
|
2029
|
-
*
|
|
2030
|
-
*
|
|
2317
|
+
* @param path - The target-relative file to delete.
|
|
2318
|
+
* @returns Nothing.
|
|
2319
|
+
* @throws {@link ScaffoldError} coded `INVALID` when the path is not one this
|
|
2320
|
+
* transaction opened or is already claimed, and `TARGET` when the destination
|
|
2321
|
+
* does not hold a file.
|
|
2031
2322
|
*
|
|
2032
2323
|
* @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.
|
|
2324
|
+
* Nothing moves here. Commit renames the file into the private backup rather
|
|
2325
|
+
* than unlinking it, so a later failure in the same commit puts it back.
|
|
2039
2326
|
*/
|
|
2040
|
-
|
|
2327
|
+
remove(path: string): void;
|
|
2041
2328
|
/**
|
|
2042
|
-
*
|
|
2329
|
+
* Promote every staged file and take every marked file, or roll the whole call back.
|
|
2043
2330
|
*
|
|
2044
|
-
* @returns
|
|
2045
|
-
*
|
|
2046
|
-
|
|
2331
|
+
* @returns Every target-relative path whose destination changed: the files
|
|
2332
|
+
* promoted, then the directories established, then the files taken.
|
|
2333
|
+
* @throws {@link ScaffoldError} coded `WRITE` when the transaction is closed,
|
|
2334
|
+
* when a destination moved since the transaction opened, or when the commit
|
|
2335
|
+
* failed; a failure reports what rollback could not undo in its context.
|
|
2047
2336
|
*
|
|
2048
2337
|
* @remarks
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2338
|
+
* Every destination and every directory this transaction created is re-checked
|
|
2339
|
+
* before anything moves, so the common failure moves nothing at all. That
|
|
2340
|
+
* check runs inside the same rollback as the promotions, because a transaction
|
|
2341
|
+
* that refuses before it starts still has a private root and created
|
|
2342
|
+
* directories to clear. The transaction is closed either way: a committed one
|
|
2343
|
+
* has nothing left to undo and a failed one has already been rolled back.
|
|
2051
2344
|
*/
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2345
|
+
commit(): readonly string[];
|
|
2346
|
+
/**
|
|
2347
|
+
* Abandon the transaction and remove everything it created.
|
|
2348
|
+
*
|
|
2349
|
+
* @returns Nothing.
|
|
2350
|
+
* @throws {@link ScaffoldError} coded `WRITE` when residue could not be
|
|
2351
|
+
* removed, naming the private root that still holds it.
|
|
2352
|
+
*
|
|
2353
|
+
* @remarks
|
|
2354
|
+
* Idempotent, and a no-op on a transaction that already committed or already
|
|
2355
|
+
* failed, so a caller can put it in a `finally` beside the work it guards.
|
|
2356
|
+
*/
|
|
2357
|
+
discard(): void;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
export { }
|