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.
Files changed (63) hide show
  1. package/ARCHITECTURE.md +79 -0
  2. package/README.md +144 -5
  3. package/TROUBLESHOOTING.md +170 -0
  4. package/dist/blobs.d.ts +3 -0
  5. package/dist/blobs.d.ts.map +1 -1
  6. package/dist/blobs.js +41 -15
  7. package/dist/builders.d.ts +1 -2
  8. package/dist/builders.d.ts.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +231 -70
  11. package/dist/coverage.d.ts.map +1 -1
  12. package/dist/coverage.js +3 -2
  13. package/dist/doctor.d.ts +17 -4
  14. package/dist/doctor.d.ts.map +1 -1
  15. package/dist/doctor.js +225 -58
  16. package/dist/handle.d.ts +27 -7
  17. package/dist/handle.d.ts.map +1 -1
  18. package/dist/handle.js +96 -20
  19. package/dist/helpers.d.ts +1 -0
  20. package/dist/helpers.d.ts.map +1 -1
  21. package/dist/helpers.js +23 -0
  22. package/dist/index.d.ts +6 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -1
  25. package/dist/list.d.ts +3 -2
  26. package/dist/list.d.ts.map +1 -1
  27. package/dist/list.js +24 -12
  28. package/dist/note.d.ts +7 -0
  29. package/dist/note.d.ts.map +1 -1
  30. package/dist/note.js +6 -0
  31. package/dist/patch-resolver.d.ts +12 -0
  32. package/dist/patch-resolver.d.ts.map +1 -1
  33. package/dist/patch-resolver.js +205 -53
  34. package/dist/read-index.d.ts.map +1 -1
  35. package/dist/read-index.js +6 -5
  36. package/dist/record.d.ts.map +1 -1
  37. package/dist/record.js +3 -3
  38. package/dist/render/migration-log.d.ts +8 -1
  39. package/dist/render/migration-log.d.ts.map +1 -1
  40. package/dist/render/migration-log.js +40 -33
  41. package/dist/render/retro.d.ts.map +1 -1
  42. package/dist/render/retro.js +1 -7
  43. package/dist/render/workspace-narrative.d.ts +7 -1
  44. package/dist/render/workspace-narrative.d.ts.map +1 -1
  45. package/dist/render/workspace-narrative.js +114 -46
  46. package/dist/runtime-config.d.ts +12 -0
  47. package/dist/runtime-config.d.ts.map +1 -0
  48. package/dist/runtime-config.js +83 -0
  49. package/dist/schema/entry-read.d.ts.map +1 -1
  50. package/dist/schema/entry-read.js +1 -1
  51. package/dist/schema/entry-write.d.ts.map +1 -1
  52. package/dist/schema/entry-write.js +1 -1
  53. package/dist/schema/entry.d.ts.map +1 -1
  54. package/dist/storage/filesystem.d.ts +8 -0
  55. package/dist/storage/filesystem.d.ts.map +1 -1
  56. package/dist/storage/filesystem.js +110 -5
  57. package/dist/text-lines.d.ts +8 -0
  58. package/dist/text-lines.d.ts.map +1 -0
  59. package/dist/text-lines.js +20 -0
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.d.ts.map +1 -1
  62. package/dist/version.js +2 -1
  63. 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 consumeAtomicWriteTestHook = (filePath) => {
14
+ const getAtomicWriteTestHook = (filePath) => {
15
15
  const key = resolveAtomicWriteKey(filePath);
16
- const hook = atomicWriteTestHooks.get(key);
17
- atomicWriteTestHooks.delete(key);
18
- return hook;
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 consumeAtomicWriteTestHook(filePath)?.beforeRename?.();
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.1.0";
1
+ export declare const LEDGER_LIBRARY_VERSION: string;
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,UAAU,CAAC"}
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
- export const LEDGER_LIBRARY_VERSION = '1.1.0';
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.0"
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.15",
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.0"
73
+ "version": "1.2.2"
72
74
  }