ushman-ledger 0.3.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -7
- package/CHANGELOG.md +8 -12
- package/README.md +28 -57
- package/dist/archive-journal.d.ts +29 -18
- package/dist/archive-journal.d.ts.map +1 -1
- package/dist/archive-journal.js +17 -17
- package/dist/blobs.js +3 -3
- package/dist/builders.d.ts +79 -358
- package/dist/builders.d.ts.map +1 -1
- package/dist/builders.js +15 -60
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +227 -52
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +104 -4
- package/dist/handle.d.ts +4 -2
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +20 -15
- package/dist/helpers.d.ts +7 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +38 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/lab-min.d.ts +7 -7
- package/dist/lab-min.d.ts.map +1 -1
- package/dist/lab-min.js +7 -9
- package/dist/list.d.ts +104 -303
- package/dist/list.d.ts.map +1 -1
- package/dist/note.d.ts +20 -0
- package/dist/note.d.ts.map +1 -1
- package/dist/note.js +5 -0
- package/dist/patch-resolver.d.ts +27 -0
- package/dist/patch-resolver.d.ts.map +1 -0
- package/dist/patch-resolver.js +184 -0
- package/dist/read-index.d.ts +45 -57
- package/dist/read-index.d.ts.map +1 -1
- package/dist/read-index.js +16 -34
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +19 -130
- package/dist/recovery.d.ts +19 -8
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +13 -13
- package/dist/render/migration-log.d.ts +3 -0
- package/dist/render/migration-log.d.ts.map +1 -0
- package/dist/render/migration-log.js +72 -0
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +41 -25
- package/dist/render/workspace-narrative.d.ts +6 -0
- package/dist/render/workspace-narrative.d.ts.map +1 -0
- package/dist/render/workspace-narrative.js +69 -0
- package/dist/schema/entry-core.d.ts +110 -0
- package/dist/schema/entry-core.d.ts.map +1 -0
- package/dist/schema/entry-core.js +143 -0
- package/dist/schema/entry-migrations.d.ts +3 -0
- package/dist/schema/entry-migrations.d.ts.map +1 -0
- package/dist/schema/entry-migrations.js +48 -0
- package/dist/schema/entry-read.d.ts +694 -0
- package/dist/schema/entry-read.d.ts.map +1 -0
- package/dist/schema/entry-read.js +92 -0
- package/dist/schema/entry-write.d.ts +865 -0
- package/dist/schema/entry-write.d.ts.map +1 -0
- package/dist/schema/entry-write.js +105 -0
- package/dist/schema/entry.d.ts +6 -3295
- package/dist/schema/entry.d.ts.map +1 -1
- package/dist/schema/entry.js +10 -619
- package/dist/schema/manifest.d.ts +28 -41
- package/dist/schema/manifest.d.ts.map +1 -1
- package/dist/schema/manifest.js +20 -24
- package/dist/schema/note.d.ts +3 -9
- package/dist/schema/note.d.ts.map +1 -1
- package/dist/schema/note.js +13 -2
- package/dist/storage/filesystem.d.ts +2 -1
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +6 -4
- package/dist/storage/lock-reclaimer.d.ts +2 -0
- package/dist/storage/lock-reclaimer.d.ts.map +1 -0
- package/dist/storage/lock-reclaimer.js +45 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -4
package/AGENTS.md
CHANGED
|
@@ -23,13 +23,17 @@ An append-only ledger library and CLI for ushman v4 workspaces. It owns ledger s
|
|
|
23
23
|
## Read order
|
|
24
24
|
|
|
25
25
|
1. `README.md`
|
|
26
|
-
2. `src/schema/entry.ts`
|
|
27
|
-
3. `src/
|
|
28
|
-
4. `src/
|
|
29
|
-
5. `src/
|
|
30
|
-
6. `src/
|
|
31
|
-
7. `src/
|
|
32
|
-
8. `src/
|
|
26
|
+
2. `src/schema/entry-core.ts`
|
|
27
|
+
3. `src/schema/entry-read.ts`
|
|
28
|
+
4. `src/schema/entry-write.ts`
|
|
29
|
+
5. `src/schema/entry-migrations.ts`
|
|
30
|
+
6. `src/schema/entry.ts`
|
|
31
|
+
7. `src/storage/filesystem.ts`
|
|
32
|
+
8. `src/record.ts`
|
|
33
|
+
9. `src/handle.ts`
|
|
34
|
+
10. `src/coverage.ts`
|
|
35
|
+
11. `src/doctor.ts`
|
|
36
|
+
12. `src/cli.ts`
|
|
33
37
|
|
|
34
38
|
## Commands
|
|
35
39
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [1.
|
|
3
|
+
## [1.1.0] - 2026-05-23
|
|
4
4
|
|
|
5
|
-
- Added
|
|
6
|
-
-
|
|
7
|
-
-
|
|
5
|
+
- Added `change-log` records, narrative note subkinds, and `migration-log-md` / `workspace-narrative-md` render targets.
|
|
6
|
+
- Split entry schema parsing into focused read/write/core modules and moved compatibility coercions behind dedicated parse migrations.
|
|
7
|
+
- Extracted patch-source resolution behind a dedicated resolver with focused tests and more actionable hash mismatch failures.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## [1.0.1] - 2026-05-16
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- Stored `validator-result` entries now persist `payload.id`; helper-side synthesis is only a temporary migration convenience.
|
|
13
|
-
- Structured patch writes now enforce the machine-readable payload fields instead of accepting prose-only patch metadata.
|
|
11
|
+
- Tightened the schema surface to the ledger primitives the v4 orchestrator actively uses.
|
|
14
12
|
|
|
15
|
-
## [0.
|
|
13
|
+
## [1.0.0] - 2026-05-14
|
|
16
14
|
|
|
17
|
-
-
|
|
18
|
-
- Made archive creation crash-recoverable with pending archive journals and post-create tar verification.
|
|
19
|
-
- Added archive recovery tests, read-index rebuild tests, and a manual scale benchmark harness.
|
|
15
|
+
- First stable release: structured `operator-decision`, `strip-decision-reverted`, and `validator-result` payloads; durable read-index sidecar with crash-recoverable archive creation.
|
|
20
16
|
|
|
21
17
|
## [0.1.0] - 2026-05-12
|
|
22
18
|
|
package/README.md
CHANGED
|
@@ -5,11 +5,11 @@ Append-only workspace ledger library and CLI for ushman v4 workspaces.
|
|
|
5
5
|
## What it owns
|
|
6
6
|
|
|
7
7
|
- Ledger entry schemas
|
|
8
|
-
- Structured patch / validator / stage payloads for analytics consumers
|
|
9
8
|
- Serialized manifest-safe entry writes
|
|
10
9
|
- Content-hash idempotency
|
|
11
10
|
- Patch blob storage
|
|
12
|
-
- Retro / JSONL / timeline rendering
|
|
11
|
+
- Retro / JSONL / timeline / dependency-graph rendering
|
|
12
|
+
- Change-log / workspace narrative markdown rendering
|
|
13
13
|
- Archive integrity output
|
|
14
14
|
- Doctor and coverage helpers
|
|
15
15
|
|
|
@@ -59,7 +59,7 @@ await ledger.record(
|
|
|
59
59
|
);
|
|
60
60
|
|
|
61
61
|
await ledger.render({ to: 'retro' });
|
|
62
|
-
await ledger.render({ to: '
|
|
62
|
+
await ledger.render({ to: 'migration-log-md' });
|
|
63
63
|
await ledger.archive('/tmp/ledger.tgz');
|
|
64
64
|
```
|
|
65
65
|
|
|
@@ -69,25 +69,37 @@ await ledger.archive('/tmp/ledger.tgz');
|
|
|
69
69
|
ushman-ledger record --workspace=<ws> --kind=tool-invocation --phase=capture --summary="capture started"
|
|
70
70
|
ushman-ledger record --workspace=<ws> --kind=agent-patch --phase=cleanup --summary="capture git diff" --rationale="track working tree change" --diff-from-git=HEAD
|
|
71
71
|
ushman-ledger record --workspace=<ws> --kind=operator-decision --phase=cleanup --summary="manual override" --action=ledger-hand-edit --check-id=manual-review --rationale="operator edited the ledger after audit"
|
|
72
|
+
ushman-ledger record --workspace=<ws> --kind=change-log --subkind=semantic-cleanup --phase=cleanup --summary="split schema modules" --diff=/tmp/change.patch --hypothesis="smaller schema modules keep the public API stable" --commands=$'bun test\nbun run typecheck' --smoke-result=pass --parity-status=green --rollback-plan="revert the schema split"
|
|
72
73
|
ushman-ledger note regression --workspace=<ws> --phase=cleanup --summary="runtime drift" --body=/tmp/note.md
|
|
74
|
+
ushman-ledger note cleanup-wave --workspace=<ws> --phase=cleanup --summary="wave 1" --body=/tmp/narrative.md
|
|
73
75
|
ushman-ledger list --workspace=<ws> --json
|
|
74
76
|
ushman-ledger render --workspace=<ws> --to=retro
|
|
77
|
+
ushman-ledger render --workspace=<ws> --to=migration-log-md
|
|
78
|
+
ushman-ledger render --workspace=<ws> --to=workspace-narrative-md
|
|
75
79
|
ushman-ledger render --workspace=<ws> --to=jsonl --out=/tmp/ledger.jsonl
|
|
76
80
|
ushman-ledger render --workspace=<ws> --to=dependency-graph --out=/tmp/ledger.mmd
|
|
77
|
-
ushman-ledger render --workspace=<ws> --to=analytics-summary
|
|
78
|
-
ushman-ledger render --workspace=<ws> --to=analytics-summary --fresh
|
|
79
|
-
ushman-ledger render --workspace=<ws> --to=analytics-summary --json
|
|
80
81
|
ushman-ledger archive --workspace=<ws> --out=/tmp/ledger.tgz
|
|
81
82
|
ushman-ledger doctor --workspace=<ws>
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
Valid record kinds: `tool-invocation`, `agent-patch`, `operator-patch`, `
|
|
85
|
+
Valid record kinds: `tool-invocation`, `agent-patch`, `operator-patch`, `operator-decision`, `validator-result`, `runtime-event`, `note`, `correction`, `strip-decision-reverted`, `change-log`
|
|
85
86
|
|
|
86
87
|
Valid phases: `capture`, `intake`, `seed`, `vendor-extract`, `cleanup`, `parity`, `characterize`, `equiv`, `analyze`, `recover`, `ship`, `migration`
|
|
87
88
|
|
|
88
|
-
Valid note subkinds: `regression`, `automation`, `retro`, `operator`, `tooling-gap`
|
|
89
|
+
Valid note subkinds: `regression`, `automation`, `retro`, `operator`, `tooling-gap`, `cleanup-wave`, `verified-flow`, `open-issue`, `decomposition-wave`, `semantic-cleanup-summary`
|
|
89
90
|
|
|
90
|
-
Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `
|
|
91
|
+
Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `migration-log-md`, `workspace-narrative-md`
|
|
92
|
+
|
|
93
|
+
## Change-log records
|
|
94
|
+
|
|
95
|
+
`change-log` entries are append-only structured narrative records for migration and cleanup work. They accept:
|
|
96
|
+
|
|
97
|
+
- `subkind`: `pre-change-checkpoint`, `semantic-cleanup`, `vendor-extract`, `decomposition`, `rollback`, `hotfix`, `smoke`
|
|
98
|
+
- `filesChanged`: explicit CSV paths via `--files-changed`, or automatic diffstat-style derivation from `--diff` / `--diff-from-git`
|
|
99
|
+
- optional narrative fields: `hypothesis`, `commandsRun`, `smokeResult`, `smokeNotes`, `parityStatus`, `rollbackPlan`
|
|
100
|
+
- `rollsBack`: required when `subkind=rollback`
|
|
101
|
+
|
|
102
|
+
`migration-log-md` renders only `change-log` entries. `workspace-narrative-md` renders only the dedicated narrative note subkinds.
|
|
91
103
|
|
|
92
104
|
## Workspace prerequisite
|
|
93
105
|
|
|
@@ -99,53 +111,12 @@ Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `an
|
|
|
99
111
|
- With `idempotencyKey`, the key is authoritative within a phase. Reusing the same key with different content in the same phase returns the first recorded entry unchanged.
|
|
100
112
|
- Stored entries copy the idempotency key into `links.idempotencyKey` for auditability.
|
|
101
113
|
|
|
102
|
-
##
|
|
103
|
-
|
|
104
|
-
- Patch entries (`agent-patch`, `operator-patch`) store `payload.touchedPaths`, `payload.fileSha256Before`, `payload.fileSha256After`, `payload.hunks`, `payload.diffSha256`, and `payload.diff`.
|
|
105
|
-
- Stored `validator-result` entries require a UUIDv7 `payload.id`. Callers should provide it in normal operation; the write helpers only synthesize one during the migration window when legacy callers omit it.
|
|
106
|
-
- `stage-transition` entries capture `{ stage, startedAt, endedAt, exitCode, doctorBaselineId? }` explicitly in the ledger.
|
|
107
|
-
- `operator-decision` entries capture `{ action, checkId?, rationale }` in `payload`.
|
|
108
|
-
- `rework.test_retired` is the structured way to retire a seed-emitted test from the pipeline corpus.
|
|
109
|
-
- Legacy on-disk entries still read successfully. The library normalizes them into the current shape and marks them with `_legacy: true`.
|
|
110
|
-
|
|
111
|
-
Example payloads:
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"kind": "stage-transition",
|
|
116
|
-
"payload": {
|
|
117
|
-
"stage": "cleanup",
|
|
118
|
-
"startedAt": "2026-05-14T12:00:00.000Z",
|
|
119
|
-
"endedAt": "2026-05-14T12:00:05.000Z",
|
|
120
|
-
"exitCode": 0
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
```json
|
|
126
|
-
{
|
|
127
|
-
"kind": "rework.test_retired",
|
|
128
|
-
"payload": {
|
|
129
|
-
"removedTestPath": "tests/pure/app.test.ts",
|
|
130
|
-
"removedTestSymbols": ["renderApp"],
|
|
131
|
-
"removedBy": "descope",
|
|
132
|
-
"sourceSymbolDeletedFrom": "src/app.ts"
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Migration guide
|
|
138
|
-
|
|
139
|
-
- Historical entries are not rewritten. Old on-disk entries continue to load through read-side normalization.
|
|
140
|
-
- Normalized legacy entries are marked with `_legacy: true` so analytics can distinguish migrated reads from fully structured writes.
|
|
141
|
-
- New writes are expected to use structured payloads. Temporary compatibility logic only remains for omitted `validator-result.payload.id` values.
|
|
142
|
-
|
|
143
|
-
## Analytics summary
|
|
114
|
+
## Payload shapes
|
|
144
115
|
|
|
145
|
-
- `
|
|
146
|
-
-
|
|
147
|
-
- `
|
|
148
|
-
- `
|
|
116
|
+
- `operator-decision` entries capture `{ action, checkId?, rationale }` in `payload`. `action` is one of `bypass-doctor`, `skip-check`, `override-strip-decision`, `override-ship-state`, `manual-parity-assertion`, `ledger-hand-edit`, `escalation`.
|
|
117
|
+
- `strip-decision-reverted` entries capture `{ stripDecisionId, rationale, invalidatedStages? }`.
|
|
118
|
+
- `correction` entries require `links.correctsLedgerId` pointing at the entry they correct.
|
|
119
|
+
- Patch entries (`agent-patch`, `operator-patch`) store a `diff` blob reference and a `rationale`. Patch text is stored once under `.lab/ledger/blobs/` and shared by hash.
|
|
149
120
|
|
|
150
121
|
## Links and coverage
|
|
151
122
|
|
|
@@ -176,7 +147,6 @@ Example payloads:
|
|
|
176
147
|
|
|
177
148
|
- `.lab/ledger/pending/` contains append journals that let the ledger recover incomplete writes after crashes. Do not edit these files by hand.
|
|
178
149
|
- If startup or `doctor` reports a pending commit mismatch, re-open the ledger or rerun the command first so reconciliation can replay or discard the journal safely.
|
|
179
|
-
- If `analytics-summary.json` is invalid or stale, rerun `ushman-ledger render --to=analytics-summary --fresh` to recompute it from the ledger tip.
|
|
180
150
|
- If `doctor` reports manifest or blob corruption, fix the underlying entry/blob mismatch first and rerun `doctor` before attempting `archive`.
|
|
181
151
|
|
|
182
152
|
## Scan behavior
|
|
@@ -205,14 +175,15 @@ Example payloads:
|
|
|
205
175
|
```text
|
|
206
176
|
<ws>/.lab/ledger/
|
|
207
177
|
.manifest.lock
|
|
208
|
-
analytics-summary.json
|
|
209
178
|
manifest.json
|
|
210
179
|
read-index.json
|
|
211
180
|
pending/
|
|
212
181
|
pending-archives/
|
|
213
182
|
blobs/
|
|
214
183
|
render.md
|
|
184
|
+
render.migration-log.md
|
|
215
185
|
render.timeline.html
|
|
186
|
+
render.workspace-narrative.md
|
|
216
187
|
<phase>/
|
|
217
188
|
<timestamp>-<sequence>-<hash>.json
|
|
218
189
|
```
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
import type { LedgerManifest } from './schema/manifest.ts';
|
|
3
|
-
export declare const ArchiveManifestSchema:
|
|
4
|
-
fileHashes:
|
|
5
|
-
path:
|
|
6
|
-
sha256:
|
|
7
|
-
},
|
|
8
|
-
integrityHash:
|
|
9
|
-
schemaVersion:
|
|
10
|
-
},
|
|
11
|
-
export type ArchiveManifest =
|
|
3
|
+
export declare const ArchiveManifestSchema: v.ObjectSchema<{
|
|
4
|
+
readonly fileHashes: v.ArraySchema<v.ObjectSchema<{
|
|
5
|
+
readonly path: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
6
|
+
readonly sha256: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.LengthAction<string, 64, undefined>]>;
|
|
7
|
+
}, undefined>, undefined>;
|
|
8
|
+
readonly integrityHash: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.LengthAction<string, 64, undefined>]>;
|
|
9
|
+
readonly schemaVersion: v.LiteralSchema<"ushman-ledger-archive-manifest/v1", undefined>;
|
|
10
|
+
}, undefined>;
|
|
11
|
+
export type ArchiveManifest = v.InferOutput<typeof ArchiveManifestSchema>;
|
|
12
12
|
export declare const collectFileHashes: (root: string, prefix?: string) => Promise<Array<{
|
|
13
13
|
path: string;
|
|
14
14
|
sha256: string;
|
|
@@ -40,7 +40,6 @@ export declare const reconcilePendingArchivesUnderLock: ({ manifest, workspaceRo
|
|
|
40
40
|
readonly manifest: LedgerManifest;
|
|
41
41
|
readonly workspaceRoot: string;
|
|
42
42
|
}) => Promise<{
|
|
43
|
-
[x: string]: unknown;
|
|
44
43
|
archives: {
|
|
45
44
|
createdAt: string;
|
|
46
45
|
integrityHash: string;
|
|
@@ -48,16 +47,28 @@ export declare const reconcilePendingArchivesUnderLock: ({ manifest, workspaceRo
|
|
|
48
47
|
}[];
|
|
49
48
|
createdAt: string;
|
|
50
49
|
entryCount: number;
|
|
51
|
-
entryLocations:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
entryLocations: {
|
|
51
|
+
[x: string]: {
|
|
52
|
+
phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
|
|
53
|
+
sequence: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
idempotencyIndex: {
|
|
57
|
+
[x: string]: {
|
|
58
|
+
[x: string]: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
56
61
|
lastSequence: number;
|
|
57
|
-
perPhaseCounts:
|
|
58
|
-
|
|
62
|
+
perPhaseCounts: {
|
|
63
|
+
[x: string]: number;
|
|
64
|
+
};
|
|
65
|
+
perPhaseLatest: {
|
|
66
|
+
[x: string]: string;
|
|
67
|
+
};
|
|
59
68
|
schemaVersion: "ushman-ledger-manifest/v1";
|
|
60
69
|
updatedAt: string;
|
|
61
70
|
workspaceId: string;
|
|
71
|
+
} & {
|
|
72
|
+
[key: string]: unknown;
|
|
62
73
|
}>;
|
|
63
74
|
//# sourceMappingURL=archive-journal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-journal.d.ts","sourceRoot":"","sources":["../src/archive-journal.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"archive-journal.d.ts","sourceRoot":"","sources":["../src/archive-journal.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAG7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAW3D,eAAO,MAAM,qBAAqB;;;;;;;aAIhC,CAAC;AASH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAyC1E,eAAO,MAAM,iBAAiB,GAC1B,MAAM,MAAM,EACZ,eAAW,KACZ,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAcjD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,YAAY,MAAM;;;;;;;EAO7D,CAAC;AA+DF,eAAO,MAAM,iBAAiB,GAAU,2CAGrC;IACC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,uBAAuB,EAAE,eAAe,CAAC;CACrD,kBA2BA,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,mCAG5C;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC,kBAMA,CAAC;AA4CF,eAAO,MAAM,mBAAmB,GAAU,yDAKvC;IACC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,oBAgBA,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,UAAU,MAAM,kBAE1D,CAAC;AAEF,eAAO,MAAM,iCAAiC,GAAU,8BAGrD;IACC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CA,CAAC"}
|
package/dist/archive-journal.js
CHANGED
|
@@ -2,32 +2,32 @@ import { mkdtemp, readdir, readFile, rm, stat, writeFile } from 'node:fs/promise
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { extract as extractTar } from 'tar';
|
|
5
|
-
import
|
|
5
|
+
import * as v from 'valibot';
|
|
6
6
|
import { sha256File, sha256Hex, stableStringify } from "./json.js";
|
|
7
7
|
import { updateManifestForArchive } from "./manifest-update.js";
|
|
8
8
|
import { resolveLedgerPaths, saveManifest, writeAtomicJsonFile } from "./storage/filesystem.js";
|
|
9
9
|
const ARCHIVE_MANIFEST_SCHEMA_VERSION = 'ushman-ledger-archive-manifest/v1';
|
|
10
10
|
const PENDING_ARCHIVE_SCHEMA_VERSION = 'ushman-ledger-pending-archive/v1';
|
|
11
|
-
const ArchiveManifestFileHashSchema =
|
|
12
|
-
path:
|
|
13
|
-
sha256:
|
|
11
|
+
const ArchiveManifestFileHashSchema = v.object({
|
|
12
|
+
path: v.pipe(v.string(), v.minLength(1)),
|
|
13
|
+
sha256: v.pipe(v.string(), v.length(64)),
|
|
14
14
|
});
|
|
15
|
-
export const ArchiveManifestSchema =
|
|
16
|
-
fileHashes:
|
|
17
|
-
integrityHash:
|
|
18
|
-
schemaVersion:
|
|
15
|
+
export const ArchiveManifestSchema = v.object({
|
|
16
|
+
fileHashes: v.array(ArchiveManifestFileHashSchema),
|
|
17
|
+
integrityHash: v.pipe(v.string(), v.length(64)),
|
|
18
|
+
schemaVersion: v.literal(ARCHIVE_MANIFEST_SCHEMA_VERSION),
|
|
19
19
|
});
|
|
20
|
-
const PendingArchiveSchema =
|
|
20
|
+
const PendingArchiveSchema = v.object({
|
|
21
21
|
archiveManifest: ArchiveManifestSchema,
|
|
22
|
-
createdAt:
|
|
23
|
-
outPath:
|
|
24
|
-
schemaVersion:
|
|
22
|
+
createdAt: v.pipe(v.string(), v.isoTimestamp()),
|
|
23
|
+
outPath: v.pipe(v.string(), v.minLength(1)),
|
|
24
|
+
schemaVersion: v.literal(PENDING_ARCHIVE_SCHEMA_VERSION),
|
|
25
25
|
});
|
|
26
26
|
const formatPendingArchiveId = (createdAt, outPath) => `${createdAt.replaceAll(':', '-').replaceAll('.', '-')}-${sha256Hex(outPath).slice(0, 12)}`;
|
|
27
27
|
const buildPendingArchivePath = ({ createdAt, outPath, workspaceRoot, }) => path.join(resolveLedgerPaths(workspaceRoot).pendingArchivesDir, `${formatPendingArchiveId(createdAt, outPath)}.json`);
|
|
28
28
|
const parsePendingArchiveText = (filePath, text) => {
|
|
29
29
|
try {
|
|
30
|
-
return
|
|
30
|
+
return v.parse(PendingArchiveSchema, JSON.parse(text));
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
33
33
|
throw new Error(`Invalid pending archive at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -35,7 +35,7 @@ const parsePendingArchiveText = (filePath, text) => {
|
|
|
35
35
|
};
|
|
36
36
|
const parseArchiveManifestText = (archivePath, text) => {
|
|
37
37
|
try {
|
|
38
|
-
return
|
|
38
|
+
return v.parse(ArchiveManifestSchema, JSON.parse(text));
|
|
39
39
|
}
|
|
40
40
|
catch (error) {
|
|
41
41
|
throw new Error(`Invalid archive manifest inside ${archivePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -58,7 +58,7 @@ export const collectFileHashes = async (root, prefix = '') => {
|
|
|
58
58
|
};
|
|
59
59
|
export const createArchiveManifest = async (ledgerRoot) => {
|
|
60
60
|
const fileHashes = await collectFileHashes(ledgerRoot);
|
|
61
|
-
return
|
|
61
|
+
return v.parse(ArchiveManifestSchema, {
|
|
62
62
|
fileHashes,
|
|
63
63
|
integrityHash: sha256Hex(stableStringify(fileHashes)),
|
|
64
64
|
schemaVersion: ARCHIVE_MANIFEST_SCHEMA_VERSION,
|
|
@@ -125,7 +125,7 @@ export const verifyArchiveFile = async ({ archivePath, expectedArchiveManifest,
|
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
127
|
export const writeArchiveManifestFile = async ({ archiveManifest, stagingRoot, }) => {
|
|
128
|
-
await writeFile(path.join(stagingRoot, 'archive-manifest.json'), `${stableStringify(
|
|
128
|
+
await writeFile(path.join(stagingRoot, 'archive-manifest.json'), `${stableStringify(v.parse(ArchiveManifestSchema, archiveManifest), true)}\n`, 'utf8');
|
|
129
129
|
};
|
|
130
130
|
const readPendingArchive = async (filePath) => parsePendingArchiveText(filePath, await readFile(filePath, 'utf8'));
|
|
131
131
|
const readPendingArchives = async (workspaceRoot) => {
|
|
@@ -163,7 +163,7 @@ export const writePendingArchive = async ({ archiveManifest, createdAt, outPath,
|
|
|
163
163
|
outPath,
|
|
164
164
|
workspaceRoot,
|
|
165
165
|
});
|
|
166
|
-
await writeAtomicJsonFile(filePath,
|
|
166
|
+
await writeAtomicJsonFile(filePath, v.parse(PendingArchiveSchema, {
|
|
167
167
|
archiveManifest,
|
|
168
168
|
createdAt,
|
|
169
169
|
outPath,
|
package/dist/blobs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile
|
|
1
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { sha256File, sha256Hex } from "./json.js";
|
|
4
4
|
import { resolveLedgerPaths, writeAtomicTextFile } from "./storage/filesystem.js";
|
|
@@ -34,11 +34,11 @@ export const storePatchBlob = async (workspaceRoot, patchText) => {
|
|
|
34
34
|
const blobPath = buildBlobPath(workspaceRoot, blobSha256);
|
|
35
35
|
let shouldWrite = true;
|
|
36
36
|
try {
|
|
37
|
-
await stat(blobPath);
|
|
38
37
|
shouldWrite = (await sha256File(blobPath)) !== blobSha256;
|
|
39
38
|
}
|
|
40
39
|
catch (error) {
|
|
41
|
-
|
|
40
|
+
const code = error.code;
|
|
41
|
+
if (code !== 'ENOENT') {
|
|
42
42
|
throw error;
|
|
43
43
|
}
|
|
44
44
|
}
|