ushman-ledger 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +79 -0
- package/README.md +144 -5
- package/TROUBLESHOOTING.md +170 -0
- package/dist/blobs.d.ts +3 -0
- package/dist/blobs.d.ts.map +1 -1
- package/dist/blobs.js +41 -15
- package/dist/builders.d.ts +1 -2
- package/dist/builders.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +231 -70
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +3 -2
- package/dist/doctor.d.ts +17 -4
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +225 -58
- package/dist/handle.d.ts +27 -7
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +96 -20
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +23 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/list.d.ts +3 -2
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +24 -12
- package/dist/note.d.ts +7 -0
- package/dist/note.d.ts.map +1 -1
- package/dist/note.js +6 -0
- package/dist/patch-resolver.d.ts +12 -0
- package/dist/patch-resolver.d.ts.map +1 -1
- package/dist/patch-resolver.js +205 -53
- package/dist/read-index.d.ts.map +1 -1
- package/dist/read-index.js +6 -5
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +3 -3
- package/dist/render/migration-log.d.ts +8 -1
- package/dist/render/migration-log.d.ts.map +1 -1
- package/dist/render/migration-log.js +40 -33
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +1 -7
- package/dist/render/workspace-narrative.d.ts +7 -1
- package/dist/render/workspace-narrative.d.ts.map +1 -1
- package/dist/render/workspace-narrative.js +114 -46
- package/dist/runtime-config.d.ts +12 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +83 -0
- package/dist/schema/entry-read.d.ts.map +1 -1
- package/dist/schema/entry-read.js +1 -1
- package/dist/schema/entry-write.d.ts.map +1 -1
- package/dist/schema/entry-write.js +1 -1
- package/dist/schema/entry.d.ts.map +1 -1
- package/dist/storage/filesystem.d.ts +8 -0
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +110 -5
- package/dist/text-lines.d.ts +8 -0
- package/dist/text-lines.d.ts.map +1 -0
- package/dist/text-lines.js +20 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -1
- package/package.json +5 -3
|
@@ -11,11 +11,36 @@ export const LEDGER_ROOT_SEGMENTS = ['.lab', 'ledger'];
|
|
|
11
11
|
export const LEDGER_STALE_TEMP_MS = 60_000;
|
|
12
12
|
const atomicWriteTestHooks = new Map();
|
|
13
13
|
const resolveAtomicWriteKey = (filePath) => path.resolve(filePath);
|
|
14
|
-
const
|
|
14
|
+
const getAtomicWriteTestHook = (filePath) => {
|
|
15
15
|
const key = resolveAtomicWriteKey(filePath);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
return {
|
|
17
|
+
hook: atomicWriteTestHooks.get(key),
|
|
18
|
+
key,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
const runAtomicWriteTestHook = async (filePath) => {
|
|
22
|
+
const { hook, key } = getAtomicWriteTestHook(filePath);
|
|
23
|
+
if (!hook?.beforeRename) {
|
|
24
|
+
atomicWriteTestHooks.delete(key);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await hook.beforeRename();
|
|
29
|
+
atomicWriteTestHooks.delete(key);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (!hook.retainOnThrow) {
|
|
33
|
+
atomicWriteTestHooks.delete(key);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const runAtomicWriteWriteTestHook = async (filePath) => {
|
|
39
|
+
const { hook } = getAtomicWriteTestHook(filePath);
|
|
40
|
+
if (!hook?.beforeWrite) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await hook.beforeWrite();
|
|
19
44
|
};
|
|
20
45
|
export const resolveLedgerPaths = (workspaceRoot) => {
|
|
21
46
|
const root = path.join(workspaceRoot, ...LEDGER_ROOT_SEGMENTS);
|
|
@@ -83,7 +108,7 @@ export const writeAtomicTextFile = async (filePath, text) => {
|
|
|
83
108
|
finally {
|
|
84
109
|
await handle.close();
|
|
85
110
|
}
|
|
86
|
-
await
|
|
111
|
+
await runAtomicWriteTestHook(filePath);
|
|
87
112
|
try {
|
|
88
113
|
await rename(tempPath, filePath);
|
|
89
114
|
}
|
|
@@ -92,6 +117,86 @@ export const writeAtomicTextFile = async (filePath, text) => {
|
|
|
92
117
|
throw error;
|
|
93
118
|
}
|
|
94
119
|
};
|
|
120
|
+
export const createAtomicTextFileWriter = async (filePath) => {
|
|
121
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
122
|
+
const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}.${randomUUID()}`;
|
|
123
|
+
const handle = await open(tempPath, 'w');
|
|
124
|
+
let completed = false;
|
|
125
|
+
let pendingWriteError;
|
|
126
|
+
let writeChain = Promise.resolve();
|
|
127
|
+
const finalizeHandle = async () => {
|
|
128
|
+
if (completed) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
completed = true;
|
|
132
|
+
return true;
|
|
133
|
+
};
|
|
134
|
+
const waitForWrites = async () => {
|
|
135
|
+
await writeChain;
|
|
136
|
+
if (pendingWriteError) {
|
|
137
|
+
throw pendingWriteError;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
abort: async () => {
|
|
142
|
+
if (!(await finalizeHandle())) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let writeError;
|
|
146
|
+
try {
|
|
147
|
+
await waitForWrites();
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
writeError = error;
|
|
151
|
+
}
|
|
152
|
+
await handle.close();
|
|
153
|
+
await rm(tempPath, { force: true });
|
|
154
|
+
if (writeError) {
|
|
155
|
+
throw writeError;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
close: async () => {
|
|
159
|
+
if (!(await finalizeHandle())) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await waitForWrites();
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
await handle.close();
|
|
167
|
+
await rm(tempPath, { force: true });
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await handle.sync();
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
await handle.close();
|
|
175
|
+
}
|
|
176
|
+
await runAtomicWriteTestHook(filePath);
|
|
177
|
+
try {
|
|
178
|
+
await rename(tempPath, filePath);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
await rm(tempPath, { force: true });
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
write: async (chunk) => {
|
|
186
|
+
const nextWrite = writeChain.then(async () => {
|
|
187
|
+
if (completed || chunk.length === 0) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
await runAtomicWriteWriteTestHook(filePath);
|
|
191
|
+
await handle.writeFile(chunk, 'utf8');
|
|
192
|
+
});
|
|
193
|
+
writeChain = nextWrite.catch((error) => {
|
|
194
|
+
pendingWriteError ??= error;
|
|
195
|
+
});
|
|
196
|
+
await nextWrite;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
};
|
|
95
200
|
export const writeAtomicJsonFile = async (filePath, value) => {
|
|
96
201
|
await writeAtomicTextFile(filePath, `${stableStringify(value, true)}\n`);
|
|
97
202
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visit each logical line in `text`, normalizing trailing `\r` from CRLF input.
|
|
3
|
+
*
|
|
4
|
+
* The final unterminated line is still visited, which keeps diff parsing stable for
|
|
5
|
+
* patch content that does not end with a trailing newline.
|
|
6
|
+
*/
|
|
7
|
+
export declare const forEachLine: (text: string, visit: (line: string) => void) => void;
|
|
8
|
+
//# sourceMappingURL=text-lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-lines.d.ts","sourceRoot":"","sources":["../src/text-lines.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,SAatE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visit each logical line in `text`, normalizing trailing `\r` from CRLF input.
|
|
3
|
+
*
|
|
4
|
+
* The final unterminated line is still visited, which keeps diff parsing stable for
|
|
5
|
+
* patch content that does not end with a trailing newline.
|
|
6
|
+
*/
|
|
7
|
+
export const forEachLine = (text, visit) => {
|
|
8
|
+
let lineStart = 0;
|
|
9
|
+
for (let index = 0; index <= text.length; index += 1) {
|
|
10
|
+
if (index !== text.length && text.charCodeAt(index) !== 10) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
let line = text.slice(lineStart, index);
|
|
14
|
+
if (line.endsWith('\r')) {
|
|
15
|
+
line = line.slice(0, -1);
|
|
16
|
+
}
|
|
17
|
+
visit(line);
|
|
18
|
+
lineStart = index + 1;
|
|
19
|
+
}
|
|
20
|
+
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const LEDGER_LIBRARY_VERSION
|
|
1
|
+
export declare const LEDGER_LIBRARY_VERSION: string;
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,sBAAsB,QAAsB,CAAC"}
|
package/dist/version.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import packageJson from '../package.json' with { type: 'json' };
|
|
2
|
+
export const LEDGER_LIBRARY_VERSION = packageJson.version;
|
package/package.json
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"safe-stable-stringify": "^2.5.0",
|
|
14
14
|
"tar": "^7.5.15",
|
|
15
|
-
"valibot": "^1.4.
|
|
15
|
+
"valibot": "^1.4.1"
|
|
16
16
|
},
|
|
17
17
|
"description": "Append-only workspace ledger library and CLI for Ushman v4.",
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@biomejs/biome": "^2.4.
|
|
19
|
+
"@biomejs/biome": "^2.4.16",
|
|
20
20
|
"@types/bun": "^1.3.14",
|
|
21
21
|
"@types/node": "^25.9.1",
|
|
22
22
|
"typescript": "^6.0.3"
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
+
"ARCHITECTURE.md",
|
|
40
41
|
"CHANGELOG.md",
|
|
42
|
+
"TROUBLESHOOTING.md",
|
|
41
43
|
"README.md",
|
|
42
44
|
"AGENTS.md",
|
|
43
45
|
"LICENSE.md"
|
|
@@ -68,5 +70,5 @@
|
|
|
68
70
|
},
|
|
69
71
|
"type": "module",
|
|
70
72
|
"types": "dist/index.d.ts",
|
|
71
|
-
"version": "1.2.
|
|
73
|
+
"version": "1.2.2"
|
|
72
74
|
}
|