@remnic/core 1.1.20 → 1.1.22
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/access-cli.js +2 -2
- package/dist/access-http.js +5 -5
- package/dist/access-mcp.js +4 -4
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +2 -2
- package/dist/{chunk-BFAMI2LQ.js → chunk-26OQECWH.js} +3 -3
- package/dist/{chunk-4KMA7OKQ.js → chunk-FF46Q3SN.js} +76 -14
- package/dist/chunk-FF46Q3SN.js.map +1 -0
- package/dist/{chunk-CRYI3XUQ.js → chunk-LDJANWTK.js} +4 -4
- package/dist/{chunk-DOXBLGVJ.js → chunk-NGPO6S3M.js} +2 -2
- package/dist/{chunk-HZX5IGIC.js → chunk-UA6OCL6S.js} +4 -4
- package/dist/{chunk-YANROFJY.js → chunk-VMQRBXJ5.js} +2 -2
- package/dist/cli.js +6 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/offline-sync.d.ts +9 -1
- package/dist/offline-sync.js +3 -1
- package/package.json +1 -1
- package/src/access-http.test.ts +3 -3
- package/src/index.ts +1 -0
- package/src/offline-sync.test.ts +249 -3
- package/src/offline-sync.ts +87 -12
- package/dist/chunk-4KMA7OKQ.js.map +0 -1
- /package/dist/{chunk-BFAMI2LQ.js.map → chunk-26OQECWH.js.map} +0 -0
- /package/dist/{chunk-CRYI3XUQ.js.map → chunk-LDJANWTK.js.map} +0 -0
- /package/dist/{chunk-DOXBLGVJ.js.map → chunk-NGPO6S3M.js.map} +0 -0
- /package/dist/{chunk-HZX5IGIC.js.map → chunk-UA6OCL6S.js.map} +0 -0
- /package/dist/{chunk-YANROFJY.js.map → chunk-VMQRBXJ5.js.map} +0 -0
package/dist/offline-sync.d.ts
CHANGED
|
@@ -128,6 +128,14 @@ declare function buildOfflineSyncChangeset(options: {
|
|
|
128
128
|
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
129
129
|
}): Promise<OfflineSyncChangeset>;
|
|
130
130
|
declare function summarizeOfflineSyncChangeset(changeset: OfflineSyncChangeset): OfflineSyncChangesetSummary;
|
|
131
|
+
declare function summarizeOfflineSyncPendingChanges(options: {
|
|
132
|
+
root: string;
|
|
133
|
+
sourceId: string;
|
|
134
|
+
baseFiles?: readonly OfflineSyncFileState[];
|
|
135
|
+
includeTranscripts?: boolean;
|
|
136
|
+
now?: Date;
|
|
137
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
138
|
+
}): Promise<OfflineSyncChangesetSummary>;
|
|
131
139
|
declare function applyOfflineSyncSnapshot(options: {
|
|
132
140
|
root: string;
|
|
133
141
|
snapshot: unknown;
|
|
@@ -157,4 +165,4 @@ declare function offlineSyncStateFromSnapshot(options: {
|
|
|
157
165
|
declare function normalizeOfflineSyncState(input: unknown): OfflineSyncState;
|
|
158
166
|
declare function fileStatesFromSnapshot(snapshot: OfflineSyncSnapshot): OfflineSyncFileState[];
|
|
159
167
|
|
|
160
|
-
export { OFFLINE_SYNC_CHANGESET_FORMAT, OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES, OFFLINE_SYNC_SNAPSHOT_FORMAT, OFFLINE_SYNC_STATE_VERSION, type OfflineSyncApplyChangesetResult, type OfflineSyncApplySnapshotResult, type OfflineSyncChange, type OfflineSyncChangeset, type OfflineSyncChangesetSummary, type OfflineSyncConflict, type OfflineSyncFileContentChunk, type OfflineSyncFileRecord, type OfflineSyncFileState, type OfflineSyncFileTarget, type OfflineSyncFileWriteTarget, type OfflineSyncSnapshot, type OfflineSyncState, applyOfflineSyncChangeset, applyOfflineSyncSnapshot, buildOfflineSyncChangeset, buildOfflineSyncSnapshot, buildOfflineSyncSnapshotForPaths, defaultOfflineSyncStatePath, fileStatesFromSnapshot, normalizeOfflineSyncChangeset, normalizeOfflineSyncSnapshot, normalizeOfflineSyncState, offlineSyncStateFromSnapshot, readOfflineSyncFileContentChunk, readOfflineSyncState, summarizeOfflineSyncChangeset, writeOfflineSyncState };
|
|
168
|
+
export { OFFLINE_SYNC_CHANGESET_FORMAT, OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES, OFFLINE_SYNC_SNAPSHOT_FORMAT, OFFLINE_SYNC_STATE_VERSION, type OfflineSyncApplyChangesetResult, type OfflineSyncApplySnapshotResult, type OfflineSyncChange, type OfflineSyncChangeset, type OfflineSyncChangesetSummary, type OfflineSyncConflict, type OfflineSyncFileContentChunk, type OfflineSyncFileRecord, type OfflineSyncFileState, type OfflineSyncFileTarget, type OfflineSyncFileWriteTarget, type OfflineSyncSnapshot, type OfflineSyncState, applyOfflineSyncChangeset, applyOfflineSyncSnapshot, buildOfflineSyncChangeset, buildOfflineSyncSnapshot, buildOfflineSyncSnapshotForPaths, defaultOfflineSyncStatePath, fileStatesFromSnapshot, normalizeOfflineSyncChangeset, normalizeOfflineSyncSnapshot, normalizeOfflineSyncState, offlineSyncStateFromSnapshot, readOfflineSyncFileContentChunk, readOfflineSyncState, summarizeOfflineSyncChangeset, summarizeOfflineSyncPendingChanges, writeOfflineSyncState };
|
package/dist/offline-sync.js
CHANGED
|
@@ -17,8 +17,9 @@ import {
|
|
|
17
17
|
readOfflineSyncFileContentChunk,
|
|
18
18
|
readOfflineSyncState,
|
|
19
19
|
summarizeOfflineSyncChangeset,
|
|
20
|
+
summarizeOfflineSyncPendingChanges,
|
|
20
21
|
writeOfflineSyncState
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-FF46Q3SN.js";
|
|
22
23
|
import "./chunk-I6K5FBRQ.js";
|
|
23
24
|
import "./chunk-AGZQD76C.js";
|
|
24
25
|
import "./chunk-SH5S7XYD.js";
|
|
@@ -44,6 +45,7 @@ export {
|
|
|
44
45
|
readOfflineSyncFileContentChunk,
|
|
45
46
|
readOfflineSyncState,
|
|
46
47
|
summarizeOfflineSyncChangeset,
|
|
48
|
+
summarizeOfflineSyncPendingChanges,
|
|
47
49
|
writeOfflineSyncState
|
|
48
50
|
};
|
|
49
51
|
//# sourceMappingURL=offline-sync.js.map
|
package/package.json
CHANGED
package/src/access-http.test.ts
CHANGED
|
@@ -418,7 +418,7 @@ test("HTTP offline file-content forwards range options and returns binary metada
|
|
|
418
418
|
body: JSON.stringify({
|
|
419
419
|
namespace: "team",
|
|
420
420
|
includeTranscripts: false,
|
|
421
|
-
path: "
|
|
421
|
+
path: "artifacts/large.txt",
|
|
422
422
|
offset: 8,
|
|
423
423
|
length: 5,
|
|
424
424
|
}),
|
|
@@ -430,7 +430,7 @@ test("HTTP offline file-content forwards range options and returns binary metada
|
|
|
430
430
|
assert.equal(body.toString("utf-8"), "hello");
|
|
431
431
|
assert.equal(response.headers.get("content-type"), "application/octet-stream");
|
|
432
432
|
assert.equal(response.headers.get("x-remnic-namespace"), "team");
|
|
433
|
-
assert.equal(response.headers.get("x-remnic-file-path"), "
|
|
433
|
+
assert.equal(response.headers.get("x-remnic-file-path"), "artifacts%2Flarge.txt");
|
|
434
434
|
assert.equal(response.headers.get("x-remnic-file-sha256"), "a".repeat(64));
|
|
435
435
|
assert.equal(response.headers.get("x-remnic-file-bytes"), "12");
|
|
436
436
|
assert.equal(response.headers.get("x-remnic-file-mtime-ms"), "1234");
|
|
@@ -440,7 +440,7 @@ test("HTTP offline file-content forwards range options and returns binary metada
|
|
|
440
440
|
namespace: "team",
|
|
441
441
|
principal: "reader",
|
|
442
442
|
includeTranscripts: false,
|
|
443
|
-
path: "
|
|
443
|
+
path: "artifacts/large.txt",
|
|
444
444
|
offset: 8,
|
|
445
445
|
length: 5,
|
|
446
446
|
}]);
|
package/src/index.ts
CHANGED
|
@@ -696,6 +696,7 @@ export {
|
|
|
696
696
|
readOfflineSyncFileContentChunk,
|
|
697
697
|
readOfflineSyncState,
|
|
698
698
|
summarizeOfflineSyncChangeset,
|
|
699
|
+
summarizeOfflineSyncPendingChanges,
|
|
699
700
|
writeOfflineSyncState,
|
|
700
701
|
type OfflineSyncApplyChangesetResult,
|
|
701
702
|
type OfflineSyncApplySnapshotResult,
|
package/src/offline-sync.test.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
buildOfflineSyncSnapshot,
|
|
13
13
|
buildOfflineSyncSnapshotForPaths,
|
|
14
14
|
readOfflineSyncFileContentChunk,
|
|
15
|
+
summarizeOfflineSyncPendingChanges,
|
|
15
16
|
} from "./offline-sync.js";
|
|
16
17
|
import { isEncryptedFile } from "./secure-store/secure-fs.js";
|
|
17
18
|
import { StorageManager } from "./storage.js";
|
|
@@ -176,19 +177,264 @@ test("offline sync excludes live LCM sqlite artifacts without deleting existing
|
|
|
176
177
|
}
|
|
177
178
|
});
|
|
178
179
|
|
|
180
|
+
test("offline sync excludes runtime-derived state without deleting existing local copies", async () => {
|
|
181
|
+
const root = await tempDir("remnic-offline-runtime-state");
|
|
182
|
+
try {
|
|
183
|
+
await write(root, "facts/a.md", "alpha");
|
|
184
|
+
await write(root, "assets/state/fact-hashes.txt", "durable asset");
|
|
185
|
+
await write(root, "state/buffer-surprise-ledger.jsonl", "surprise");
|
|
186
|
+
await write(root, "state/buffer.json", "buffer");
|
|
187
|
+
await write(root, "state/buffer.json.tmp-123-456", "tmp");
|
|
188
|
+
await write(root, "state/embeddings.json", "embeddings");
|
|
189
|
+
await write(root, "state/entity-mention-index.json", "entities");
|
|
190
|
+
await write(root, "state/index_tags.json", "tags");
|
|
191
|
+
await write(root, "state/index_time.json", "time");
|
|
192
|
+
await write(root, "state/memory-lifecycle-ledger.jsonl", "ledger");
|
|
193
|
+
await write(root, "state/.artifact-write-version.log", "version");
|
|
194
|
+
await write(root, "state/.memory-status-version.log", "version");
|
|
195
|
+
await write(root, "state/memory-projection.sqlite", "projection");
|
|
196
|
+
await write(root, "state/memory-projection.sqlite-shm", "projection-shm");
|
|
197
|
+
await write(root, "state/memory-projection.sqlite-wal", "projection-wal");
|
|
198
|
+
await write(root, "state/recall_impressions.jsonl", "impressions");
|
|
199
|
+
await write(root, "namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json", "intent");
|
|
200
|
+
await write(root, "namespaces/generalist-project-origin-6ebeaa54/state/entity-mention-index.json", "entities");
|
|
201
|
+
await write(root, "namespaces/generalist-project-origin-6ebeaa54/state/.memory-status-version.log", "version");
|
|
202
|
+
|
|
203
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
204
|
+
root,
|
|
205
|
+
sourceId: "remote",
|
|
206
|
+
includeContent: true,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), [
|
|
210
|
+
"assets/state/fact-hashes.txt",
|
|
211
|
+
"facts/a.md",
|
|
212
|
+
]);
|
|
213
|
+
await assert.rejects(
|
|
214
|
+
() =>
|
|
215
|
+
buildOfflineSyncSnapshotForPaths({
|
|
216
|
+
root,
|
|
217
|
+
sourceId: "remote",
|
|
218
|
+
paths: ["state/memory-lifecycle-ledger.jsonl"],
|
|
219
|
+
includeContent: true,
|
|
220
|
+
}),
|
|
221
|
+
/offline sync snapshot path is excluded: state\/memory-lifecycle-ledger\.jsonl/,
|
|
222
|
+
);
|
|
223
|
+
await assert.rejects(
|
|
224
|
+
() =>
|
|
225
|
+
readOfflineSyncFileContentChunk({
|
|
226
|
+
root,
|
|
227
|
+
path: "state/buffer.json.tmp-123-456",
|
|
228
|
+
}),
|
|
229
|
+
/offline sync file content path is excluded: state\/buffer\.json\.tmp-123-456/,
|
|
230
|
+
);
|
|
231
|
+
await assert.rejects(
|
|
232
|
+
() =>
|
|
233
|
+
buildOfflineSyncSnapshotForPaths({
|
|
234
|
+
root,
|
|
235
|
+
sourceId: "remote",
|
|
236
|
+
paths: ["namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json"],
|
|
237
|
+
includeContent: true,
|
|
238
|
+
}),
|
|
239
|
+
/offline sync snapshot path is excluded: namespaces\/generalist-project-origin-6ebeaa54\/state\/last_intent\.json/,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const oldLedger = Buffer.from("old ledger");
|
|
243
|
+
const pull = await applyOfflineSyncSnapshot({
|
|
244
|
+
root,
|
|
245
|
+
snapshot,
|
|
246
|
+
baseFiles: [{
|
|
247
|
+
path: "state/memory-lifecycle-ledger.jsonl",
|
|
248
|
+
sha256: createHash("sha256").update(oldLedger).digest("hex"),
|
|
249
|
+
bytes: oldLedger.byteLength,
|
|
250
|
+
mtimeMs: 0,
|
|
251
|
+
}],
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
assert.equal(pull.deleted, 0);
|
|
255
|
+
assert.equal(await readUtf8(root, "state/memory-lifecycle-ledger.jsonl"), "ledger");
|
|
256
|
+
} finally {
|
|
257
|
+
await rm(root, { recursive: true, force: true });
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("offline sync ignores runtime-derived records from older peers", async () => {
|
|
262
|
+
const root = await tempDir("remnic-offline-legacy-runtime-state");
|
|
263
|
+
try {
|
|
264
|
+
const fact = Buffer.from("alpha");
|
|
265
|
+
const runtime = Buffer.from("legacy runtime");
|
|
266
|
+
const asset = Buffer.from("durable asset");
|
|
267
|
+
const runtimeSha = createHash("sha256").update(runtime).digest("hex");
|
|
268
|
+
const factSha = createHash("sha256").update(fact).digest("hex");
|
|
269
|
+
const assetSha = createHash("sha256").update(asset).digest("hex");
|
|
270
|
+
|
|
271
|
+
const pull = await applyOfflineSyncSnapshot({
|
|
272
|
+
root,
|
|
273
|
+
snapshot: {
|
|
274
|
+
format: "remnic.offline-sync.snapshot.v1",
|
|
275
|
+
schemaVersion: 1,
|
|
276
|
+
createdAt: new Date().toISOString(),
|
|
277
|
+
sourceId: "old-remote",
|
|
278
|
+
includeTranscripts: true,
|
|
279
|
+
files: [
|
|
280
|
+
{
|
|
281
|
+
path: "state/buffer.json",
|
|
282
|
+
sha256: runtimeSha,
|
|
283
|
+
bytes: runtime.byteLength,
|
|
284
|
+
mtimeMs: 0,
|
|
285
|
+
contentBase64: runtime.toString("base64"),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
path: "namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json",
|
|
289
|
+
sha256: runtimeSha,
|
|
290
|
+
bytes: runtime.byteLength,
|
|
291
|
+
mtimeMs: 0,
|
|
292
|
+
contentBase64: runtime.toString("base64"),
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
path: "facts/a.md",
|
|
296
|
+
sha256: factSha,
|
|
297
|
+
bytes: fact.byteLength,
|
|
298
|
+
mtimeMs: 0,
|
|
299
|
+
contentBase64: fact.toString("base64"),
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
path: "assets/state/fact-hashes.txt",
|
|
303
|
+
sha256: assetSha,
|
|
304
|
+
bytes: asset.byteLength,
|
|
305
|
+
mtimeMs: 0,
|
|
306
|
+
contentBase64: asset.toString("base64"),
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
assert.equal(pull.upserted, 2);
|
|
313
|
+
assert.equal(await readUtf8(root, "facts/a.md"), "alpha");
|
|
314
|
+
assert.equal(await readUtf8(root, "assets/state/fact-hashes.txt"), "durable asset");
|
|
315
|
+
await assert.rejects(
|
|
316
|
+
() => readFile(path.join(root, "state", "buffer.json")),
|
|
317
|
+
/ENOENT/,
|
|
318
|
+
);
|
|
319
|
+
await assert.rejects(
|
|
320
|
+
() => readFile(path.join(root, "namespaces", "generalist-project-origin-6ebeaa54", "state", "last_intent.json")),
|
|
321
|
+
/ENOENT/,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const remote = await tempDir("remnic-offline-legacy-runtime-remote");
|
|
325
|
+
try {
|
|
326
|
+
const push = await applyOfflineSyncChangeset({
|
|
327
|
+
root: remote,
|
|
328
|
+
changeset: {
|
|
329
|
+
format: "remnic.offline-sync.changeset.v1",
|
|
330
|
+
schemaVersion: 1,
|
|
331
|
+
createdAt: new Date().toISOString(),
|
|
332
|
+
sourceId: "old-laptop",
|
|
333
|
+
includeTranscripts: true,
|
|
334
|
+
changes: [
|
|
335
|
+
{
|
|
336
|
+
type: "upsert",
|
|
337
|
+
path: "state/memory-lifecycle-ledger.jsonl",
|
|
338
|
+
file: {
|
|
339
|
+
path: "state/memory-lifecycle-ledger.jsonl",
|
|
340
|
+
sha256: runtimeSha,
|
|
341
|
+
bytes: runtime.byteLength,
|
|
342
|
+
mtimeMs: 0,
|
|
343
|
+
contentBase64: runtime.toString("base64"),
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
type: "upsert",
|
|
348
|
+
path: "namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json",
|
|
349
|
+
file: {
|
|
350
|
+
path: "namespaces/generalist-project-origin-6ebeaa54/state/last_intent.json",
|
|
351
|
+
sha256: runtimeSha,
|
|
352
|
+
bytes: runtime.byteLength,
|
|
353
|
+
mtimeMs: 0,
|
|
354
|
+
contentBase64: runtime.toString("base64"),
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: "upsert",
|
|
359
|
+
path: "facts/a.md",
|
|
360
|
+
file: {
|
|
361
|
+
path: "facts/a.md",
|
|
362
|
+
sha256: factSha,
|
|
363
|
+
bytes: fact.byteLength,
|
|
364
|
+
mtimeMs: 0,
|
|
365
|
+
contentBase64: fact.toString("base64"),
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
type: "upsert",
|
|
370
|
+
path: "assets/state/fact-hashes.txt",
|
|
371
|
+
file: {
|
|
372
|
+
path: "assets/state/fact-hashes.txt",
|
|
373
|
+
sha256: assetSha,
|
|
374
|
+
bytes: asset.byteLength,
|
|
375
|
+
mtimeMs: 0,
|
|
376
|
+
contentBase64: asset.toString("base64"),
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
assert.equal(push.appliedUpserts, 2);
|
|
384
|
+
assert.equal(await readUtf8(remote, "facts/a.md"), "alpha");
|
|
385
|
+
assert.equal(await readUtf8(remote, "assets/state/fact-hashes.txt"), "durable asset");
|
|
386
|
+
await assert.rejects(
|
|
387
|
+
() => readFile(path.join(remote, "state", "memory-lifecycle-ledger.jsonl")),
|
|
388
|
+
/ENOENT/,
|
|
389
|
+
);
|
|
390
|
+
await assert.rejects(
|
|
391
|
+
() => readFile(path.join(remote, "namespaces", "generalist-project-origin-6ebeaa54", "state", "last_intent.json")),
|
|
392
|
+
/ENOENT/,
|
|
393
|
+
);
|
|
394
|
+
} finally {
|
|
395
|
+
await rm(remote, { recursive: true, force: true });
|
|
396
|
+
}
|
|
397
|
+
} finally {
|
|
398
|
+
await rm(root, { recursive: true, force: true });
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("offline sync pending summary returns counts without materializing changed content", async () => {
|
|
403
|
+
const root = await tempDir("remnic-offline-pending-summary");
|
|
404
|
+
try {
|
|
405
|
+
await write(root, "facts/a.md", "updated");
|
|
406
|
+
await write(root, "facts/b.md", "new");
|
|
407
|
+
const baseA = createHash("sha256").update("old").digest("hex");
|
|
408
|
+
const baseDeleted = createHash("sha256").update("deleted").digest("hex");
|
|
409
|
+
|
|
410
|
+
const summary = await summarizeOfflineSyncPendingChanges({
|
|
411
|
+
root,
|
|
412
|
+
sourceId: "local",
|
|
413
|
+
baseFiles: [
|
|
414
|
+
{ path: "facts/a.md", sha256: baseA, bytes: 3, mtimeMs: 0 },
|
|
415
|
+
{ path: "facts/deleted.md", sha256: baseDeleted, bytes: 7, mtimeMs: 0 },
|
|
416
|
+
],
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
assert.deepEqual(summary, { upserts: 2, deletes: 1, total: 3 });
|
|
420
|
+
} finally {
|
|
421
|
+
await rm(root, { recursive: true, force: true });
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
179
425
|
test("offline sync reads bounded file content chunks with metadata", async () => {
|
|
180
426
|
const root = await tempDir("remnic-offline-file-content");
|
|
181
427
|
try {
|
|
182
|
-
await write(root, "
|
|
428
|
+
await write(root, "artifacts/large.txt", "alpha\nbeta\ngamma\n");
|
|
183
429
|
|
|
184
430
|
const chunk = await readOfflineSyncFileContentChunk({
|
|
185
431
|
root,
|
|
186
|
-
path: "
|
|
432
|
+
path: "artifacts/large.txt",
|
|
187
433
|
offset: 6,
|
|
188
434
|
length: 5,
|
|
189
435
|
});
|
|
190
436
|
|
|
191
|
-
assert.equal(chunk.path, "
|
|
437
|
+
assert.equal(chunk.path, "artifacts/large.txt");
|
|
192
438
|
assert.equal(chunk.offset, 6);
|
|
193
439
|
assert.equal(chunk.chunkBytes, 5);
|
|
194
440
|
assert.equal(chunk.content.toString("utf-8"), "beta\n");
|
package/src/offline-sync.ts
CHANGED
|
@@ -150,16 +150,29 @@ const EXCLUDED_FILE_NAMES = new Set([
|
|
|
150
150
|
".sync-state.json",
|
|
151
151
|
]);
|
|
152
152
|
|
|
153
|
-
const
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
153
|
+
const DERIVED_RUNTIME_STATE_BASENAMES = new Set([
|
|
154
|
+
".artifact-write-version.log",
|
|
155
|
+
".memory-status-version.log",
|
|
156
|
+
"fact-hashes.ready",
|
|
157
|
+
"fact-hashes.txt",
|
|
158
|
+
"buffer-surprise-ledger.jsonl",
|
|
159
|
+
"buffer.json",
|
|
160
|
+
"embeddings.json",
|
|
161
|
+
"entity-mention-index.json",
|
|
162
|
+
"index_tags.json",
|
|
163
|
+
"index_time.json",
|
|
164
|
+
"last_graph_recall.json",
|
|
165
|
+
"last_intent.json",
|
|
166
|
+
"last_qmd_recall.json",
|
|
167
|
+
"last_recall.json",
|
|
168
|
+
"lcm.sqlite",
|
|
169
|
+
"lcm.sqlite-shm",
|
|
170
|
+
"lcm.sqlite-wal",
|
|
171
|
+
"memory-lifecycle-ledger.jsonl",
|
|
172
|
+
"memory-projection.sqlite",
|
|
173
|
+
"memory-projection.sqlite-shm",
|
|
174
|
+
"memory-projection.sqlite-wal",
|
|
175
|
+
"recall_impressions.jsonl",
|
|
163
176
|
]);
|
|
164
177
|
|
|
165
178
|
const EXCLUDED_FILE_PREFIXES = [
|
|
@@ -284,6 +297,7 @@ export function normalizeOfflineSyncSnapshot(
|
|
|
284
297
|
const files = obj.files
|
|
285
298
|
.map((entry, index) =>
|
|
286
299
|
normalizeFileRecord(entry, `files[${index}]`, options.requireContent === true))
|
|
300
|
+
.filter((file) => !shouldIgnoreIncomingRuntimePath(file.path))
|
|
287
301
|
.sort(compareByPath);
|
|
288
302
|
assertUniquePaths(files, "offline sync snapshot");
|
|
289
303
|
if (!includeTranscripts) {
|
|
@@ -360,7 +374,7 @@ export function normalizeOfflineSyncChangeset(input: unknown): OfflineSyncChange
|
|
|
360
374
|
};
|
|
361
375
|
}
|
|
362
376
|
throw new Error(`changes[${index}].type must be "upsert" or "delete"`);
|
|
363
|
-
});
|
|
377
|
+
}).filter((change) => !shouldIgnoreIncomingRuntimePath(change.path));
|
|
364
378
|
assertUniquePaths(changes, "offline sync changeset");
|
|
365
379
|
if (!includeTranscripts) {
|
|
366
380
|
const transcriptPath = changes.find((change) => change.path.split("/")[0] === "transcripts")?.path;
|
|
@@ -417,13 +431,30 @@ function shouldExcludeRelPath(relPosix: string, includeTranscripts: boolean): bo
|
|
|
417
431
|
const parts = relPosix.split("/");
|
|
418
432
|
if (parts.some((part) => DEFAULT_TRANSFER_EXCLUDE_DIRS.has(part))) return true;
|
|
419
433
|
if (parts.some((part) => part === SYNC_INTERNAL_DIR)) return true;
|
|
420
|
-
if (
|
|
434
|
+
if (isDerivedRuntimeStatePath(parts)) return true;
|
|
421
435
|
if (!includeTranscripts && parts[0] === "transcripts") return true;
|
|
422
436
|
const basename = parts[parts.length - 1] ?? "";
|
|
437
|
+
if (isCanonicalRuntimeStatePath(parts) && basename.includes(".tmp-")) return true;
|
|
423
438
|
if (EXCLUDED_FILE_NAMES.has(basename)) return true;
|
|
424
439
|
return EXCLUDED_FILE_PREFIXES.some((prefix) => basename.startsWith(prefix));
|
|
425
440
|
}
|
|
426
441
|
|
|
442
|
+
function shouldIgnoreIncomingRuntimePath(relPosix: string): boolean {
|
|
443
|
+
const parts = relPosix.split("/");
|
|
444
|
+
const basename = parts[parts.length - 1] ?? "";
|
|
445
|
+
return isDerivedRuntimeStatePath(parts) || (isCanonicalRuntimeStatePath(parts) && basename.includes(".tmp-"));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function isDerivedRuntimeStatePath(parts: string[]): boolean {
|
|
449
|
+
const basename = parts[parts.length - 1] ?? "";
|
|
450
|
+
return isCanonicalRuntimeStatePath(parts) && DERIVED_RUNTIME_STATE_BASENAMES.has(basename);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function isCanonicalRuntimeStatePath(parts: string[]): boolean {
|
|
454
|
+
if (parts[0] === "state") return true;
|
|
455
|
+
return parts[0] === "namespaces" && parts.length >= 4 && parts[2] === "state";
|
|
456
|
+
}
|
|
457
|
+
|
|
427
458
|
function filterBaseFilesForMode(
|
|
428
459
|
files: readonly OfflineSyncFileState[],
|
|
429
460
|
includeTranscripts: boolean,
|
|
@@ -728,6 +759,50 @@ export function summarizeOfflineSyncChangeset(
|
|
|
728
759
|
};
|
|
729
760
|
}
|
|
730
761
|
|
|
762
|
+
export async function summarizeOfflineSyncPendingChanges(options: {
|
|
763
|
+
root: string;
|
|
764
|
+
sourceId: string;
|
|
765
|
+
baseFiles?: readonly OfflineSyncFileState[];
|
|
766
|
+
includeTranscripts?: boolean;
|
|
767
|
+
now?: Date;
|
|
768
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
769
|
+
}): Promise<OfflineSyncChangesetSummary> {
|
|
770
|
+
const includeTranscripts = options.includeTranscripts !== false;
|
|
771
|
+
const base = byPath(filterBaseFilesForMode(
|
|
772
|
+
normalizeFileStates(options.baseFiles),
|
|
773
|
+
includeTranscripts,
|
|
774
|
+
));
|
|
775
|
+
const current = await buildOfflineSyncSnapshot({
|
|
776
|
+
root: options.root,
|
|
777
|
+
sourceId: options.sourceId,
|
|
778
|
+
includeContent: false,
|
|
779
|
+
includeTranscripts,
|
|
780
|
+
now: options.now,
|
|
781
|
+
readFile: options.readFile,
|
|
782
|
+
});
|
|
783
|
+
const currentMap = byPath(current.files);
|
|
784
|
+
let upserts = 0;
|
|
785
|
+
let deletes = 0;
|
|
786
|
+
|
|
787
|
+
for (const relPath of unionPaths(base, currentMap)) {
|
|
788
|
+
const baseEntry = base.get(relPath);
|
|
789
|
+
const currentEntry = currentMap.get(relPath);
|
|
790
|
+
if (currentEntry && currentEntry.sha256 !== baseEntry?.sha256) {
|
|
791
|
+
upserts += 1;
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (!currentEntry && baseEntry) {
|
|
795
|
+
deletes += 1;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return {
|
|
800
|
+
upserts,
|
|
801
|
+
deletes,
|
|
802
|
+
total: upserts + deletes,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
731
806
|
export async function applyOfflineSyncSnapshot(options: {
|
|
732
807
|
root: string;
|
|
733
808
|
snapshot: unknown;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/offline-sync.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n open,\n readdir,\n readFile,\n rename,\n stat,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n DEFAULT_TRANSFER_EXCLUDE_DIRS,\n} from \"./transfer/exclusions.js\";\nimport { isEncryptedFile, MAGIC_HEADER_SIZE } from \"./secure-store/secure-fs.js\";\nimport {\n prepareSafeArchiveRoot,\n resolveSafeArchiveTarget,\n sha256Bytes,\n validateArchiveRelativePath,\n type SafeArchiveRoot,\n} from \"./transfer/fs-utils.js\";\nimport { parseFlexibleIsoTimestamp } from \"./utils/iso-timestamp.js\";\n\nexport const OFFLINE_SYNC_SNAPSHOT_FORMAT = \"remnic.offline-sync.snapshot.v1\";\nexport const OFFLINE_SYNC_CHANGESET_FORMAT = \"remnic.offline-sync.changeset.v1\";\nexport const OFFLINE_SYNC_STATE_VERSION = 1;\nexport const OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES = 64 * 1024 * 1024;\n\nexport interface OfflineSyncFileState {\n path: string;\n sha256: string;\n /** Byte length of the transferable content, after any readFile hook such as secure-store decryption. */\n bytes: number;\n mtimeMs: number;\n}\n\nexport interface OfflineSyncFileRecord extends OfflineSyncFileState {\n contentBase64?: string;\n}\n\nexport interface OfflineSyncSnapshot {\n format: typeof OFFLINE_SYNC_SNAPSHOT_FORMAT;\n schemaVersion: 1;\n createdAt: string;\n sourceId: string;\n includeTranscripts: boolean;\n files: OfflineSyncFileRecord[];\n}\n\nexport type OfflineSyncChange =\n | {\n type: \"upsert\";\n path: string;\n baseSha256?: string;\n file: OfflineSyncFileRecord & { contentBase64: string };\n }\n | {\n type: \"delete\";\n path: string;\n baseSha256: string;\n };\n\nexport interface OfflineSyncChangeset {\n format: typeof OFFLINE_SYNC_CHANGESET_FORMAT;\n schemaVersion: 1;\n createdAt: string;\n sourceId: string;\n includeTranscripts: boolean;\n changes: OfflineSyncChange[];\n}\n\nexport interface OfflineSyncState {\n version: typeof OFFLINE_SYNC_STATE_VERSION;\n remoteId: string;\n namespace?: string;\n includeTranscripts: boolean;\n lastSyncedAt: string;\n baseFiles: OfflineSyncFileState[];\n}\n\nexport interface OfflineSyncConflict {\n path: string;\n reason:\n | \"both_modified\"\n | \"local_deleted_remote_modified\"\n | \"local_modified_remote_deleted\"\n | \"remote_exists_for_local_create\"\n | \"remote_changed_for_local_update\"\n | \"remote_deleted_for_local_update\"\n | \"remote_changed_for_local_delete\";\n baseSha256?: string;\n localSha256?: string;\n incomingSha256?: string;\n conflictPath?: string;\n}\n\nexport interface OfflineSyncApplySnapshotResult {\n upserted: number;\n deleted: number;\n skipped: number;\n pendingLocal: number;\n conflicts: OfflineSyncConflict[];\n nextBaseFiles: OfflineSyncFileState[];\n}\n\nexport interface OfflineSyncApplyChangesetResult {\n appliedUpserts: number;\n appliedDeletes: number;\n skipped: number;\n conflicts: OfflineSyncConflict[];\n currentFiles: OfflineSyncFileState[];\n}\n\nexport interface OfflineSyncChangesetSummary {\n upserts: number;\n deletes: number;\n total: number;\n}\n\nexport interface OfflineSyncFileTarget {\n root: string;\n path: string;\n filePath: string;\n}\n\nexport interface OfflineSyncFileWriteTarget extends OfflineSyncFileTarget {\n content: Buffer;\n}\n\nexport interface OfflineSyncFileContentChunk extends Omit<OfflineSyncFileState, \"sha256\"> {\n sha256?: string;\n offset: number;\n chunkBytes: number;\n content: Buffer;\n}\n\ninterface OfflineSyncFileRecordOptions {\n root: SafeArchiveRoot;\n relPath: string;\n filePath: string;\n includeContent: boolean;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n}\n\nconst SYNC_INTERNAL_DIR = \".offline-sync\";\nconst EXCLUDED_FILE_NAMES = new Set([\n \".sync-state.json\",\n]);\n\nconst EXCLUDED_REL_PATHS = new Set([\n \"state/fact-hashes.ready\",\n \"state/fact-hashes.txt\",\n \"state/last_graph_recall.json\",\n \"state/last_intent.json\",\n \"state/last_qmd_recall.json\",\n \"state/last_recall.json\",\n \"state/lcm.sqlite\",\n \"state/lcm.sqlite-shm\",\n \"state/lcm.sqlite-wal\",\n]);\n\nconst EXCLUDED_FILE_PREFIXES = [\n \".remnic-sync.\",\n \".remnic-sync-state.\",\n];\n\nfunction hashText(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction sha256Buffer(buffer: Buffer): { sha256: string; bytes: number } {\n return sha256Bytes(buffer);\n}\n\nfunction compareByPath<T extends { path: string }>(left: T, right: T): number {\n return left.path.localeCompare(right.path);\n}\n\nfunction assertSha256(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !/^[a-f0-9]{64}$/i.test(value)) {\n throw new Error(`${field} must be a 64-character sha256 hex string`);\n }\n return value.toLowerCase();\n}\n\nfunction assertNonNegativeInteger(value: unknown, field: string): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n !Number.isInteger(value) ||\n value < 0\n ) {\n throw new Error(`${field} must be a non-negative integer`);\n }\n return value;\n}\n\nfunction assertNonNegativeFinite(value: unknown, field: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n throw new Error(`${field} must be a non-negative finite number`);\n }\n return value;\n}\n\nfunction assertBoolean(value: unknown, field: string): boolean {\n if (typeof value !== \"boolean\") {\n throw new Error(`${field} must be a boolean`);\n }\n return value;\n}\n\nfunction normalizeSourceId(value: unknown, field: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0 || value.length > 512) {\n throw new Error(`${field} must be a non-empty string no longer than 512 characters`);\n }\n return value.trim();\n}\n\nfunction normalizeFileState(input: unknown, fieldPrefix: string): OfflineSyncFileState {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\n throw new Error(`${fieldPrefix} must be an object`);\n }\n const obj = input as Record<string, unknown>;\n const relPath = normalizeRelativePath(obj.path, `${fieldPrefix}.path`);\n return {\n path: relPath,\n sha256: assertSha256(obj.sha256, `${fieldPrefix}.sha256`),\n bytes: assertNonNegativeInteger(obj.bytes, `${fieldPrefix}.bytes`),\n mtimeMs: assertNonNegativeFinite(obj.mtimeMs, `${fieldPrefix}.mtimeMs`),\n };\n}\n\nfunction normalizeFileRecord(\n input: unknown,\n fieldPrefix: string,\n requireContent: boolean,\n): OfflineSyncFileRecord {\n const state = normalizeFileState(input, fieldPrefix);\n const obj = input as Record<string, unknown>;\n const contentBase64 = obj.contentBase64;\n if (requireContent && typeof contentBase64 !== \"string\") {\n throw new Error(`${fieldPrefix}.contentBase64 is required`);\n }\n if (contentBase64 !== undefined && typeof contentBase64 !== \"string\") {\n throw new Error(`${fieldPrefix}.contentBase64 must be a base64 string`);\n }\n return {\n ...state,\n ...(contentBase64 !== undefined ? { contentBase64 } : {}),\n };\n}\n\nfunction normalizeFileStates(input: readonly unknown[] | undefined): OfflineSyncFileState[] {\n if (!input) return [];\n if (!Array.isArray(input)) {\n throw new Error(\"baseFiles must be an array\");\n }\n return input.map((entry, index) => normalizeFileState(entry, `baseFiles[${index}]`));\n}\n\nexport function normalizeOfflineSyncSnapshot(\n input: unknown,\n options: { requireContent?: boolean } = {},\n): OfflineSyncSnapshot {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\n throw new Error(\"offline sync snapshot must be an object\");\n }\n const obj = input as Record<string, unknown>;\n if (obj.format !== OFFLINE_SYNC_SNAPSHOT_FORMAT) {\n throw new Error(`offline sync snapshot format must be ${OFFLINE_SYNC_SNAPSHOT_FORMAT}`);\n }\n if (obj.schemaVersion !== 1) {\n throw new Error(\"offline sync snapshot schemaVersion must be 1\");\n }\n const createdAt = normalizeIsoString(obj.createdAt, \"createdAt\");\n const sourceId = normalizeSourceId(obj.sourceId, \"sourceId\");\n const includeTranscripts = assertBoolean(obj.includeTranscripts, \"includeTranscripts\");\n if (!Array.isArray(obj.files)) {\n throw new Error(\"offline sync snapshot files must be an array\");\n }\n const files = obj.files\n .map((entry, index) =>\n normalizeFileRecord(entry, `files[${index}]`, options.requireContent === true))\n .sort(compareByPath);\n assertUniquePaths(files, \"offline sync snapshot\");\n if (!includeTranscripts) {\n const transcriptPath = files.find((file) => file.path.split(\"/\")[0] === \"transcripts\")?.path;\n if (transcriptPath) {\n throw new Error(\n `offline sync snapshot includeTranscripts is false but contains transcript path: ${transcriptPath}`,\n );\n }\n }\n const excludedPath = files.find((file) => shouldExcludeRelPath(file.path, true))?.path;\n if (excludedPath) {\n throw new Error(`offline sync snapshot contains excluded path: ${excludedPath}`);\n }\n return {\n format: OFFLINE_SYNC_SNAPSHOT_FORMAT,\n schemaVersion: 1,\n createdAt,\n sourceId,\n includeTranscripts,\n files,\n };\n}\n\nexport function normalizeOfflineSyncChangeset(input: unknown): OfflineSyncChangeset {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\n throw new Error(\"offline sync changeset must be an object\");\n }\n const obj = input as Record<string, unknown>;\n if (obj.format !== OFFLINE_SYNC_CHANGESET_FORMAT) {\n throw new Error(`offline sync changeset format must be ${OFFLINE_SYNC_CHANGESET_FORMAT}`);\n }\n if (obj.schemaVersion !== 1) {\n throw new Error(\"offline sync changeset schemaVersion must be 1\");\n }\n const createdAt = normalizeIsoString(obj.createdAt, \"createdAt\");\n const sourceId = normalizeSourceId(obj.sourceId, \"sourceId\");\n const includeTranscripts = assertBoolean(obj.includeTranscripts, \"includeTranscripts\");\n if (!Array.isArray(obj.changes)) {\n throw new Error(\"offline sync changeset changes must be an array\");\n }\n const changes = obj.changes.map((entry, index): OfflineSyncChange => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new Error(`changes[${index}] must be an object`);\n }\n const change = entry as Record<string, unknown>;\n const type = change.type;\n const relPath = normalizeRelativePath(change.path, `changes[${index}].path`);\n if (type === \"upsert\") {\n const file = normalizeFileRecord(\n change.file,\n `changes[${index}].file`,\n true,\n ) as OfflineSyncFileRecord & { contentBase64: string };\n if (file.path !== relPath) {\n throw new Error(`changes[${index}].file.path must match changes[${index}].path`);\n }\n const baseSha256 =\n change.baseSha256 === undefined\n ? undefined\n : assertSha256(change.baseSha256, `changes[${index}].baseSha256`);\n return {\n type: \"upsert\",\n path: relPath,\n ...(baseSha256 ? { baseSha256 } : {}),\n file,\n };\n }\n if (type === \"delete\") {\n return {\n type: \"delete\",\n path: relPath,\n baseSha256: assertSha256(change.baseSha256, `changes[${index}].baseSha256`),\n };\n }\n throw new Error(`changes[${index}].type must be \"upsert\" or \"delete\"`);\n });\n assertUniquePaths(changes, \"offline sync changeset\");\n if (!includeTranscripts) {\n const transcriptPath = changes.find((change) => change.path.split(\"/\")[0] === \"transcripts\")?.path;\n if (transcriptPath) {\n throw new Error(\n `offline sync changeset includeTranscripts is false but contains transcript path: ${transcriptPath}`,\n );\n }\n }\n const excludedPath = changes.find((change) => shouldExcludeRelPath(change.path, true))?.path;\n if (excludedPath) {\n throw new Error(`offline sync changeset contains excluded path: ${excludedPath}`);\n }\n return {\n format: OFFLINE_SYNC_CHANGESET_FORMAT,\n schemaVersion: 1,\n createdAt,\n sourceId,\n includeTranscripts,\n changes: changes.sort(compareByPath),\n };\n}\n\nfunction normalizeIsoString(input: unknown, field: string): string {\n if (typeof input !== \"string\" || input.trim().length === 0) {\n throw new Error(`${field} must be an ISO timestamp string`);\n }\n const parsed = parseFlexibleIsoTimestamp(input.trim());\n if (parsed === null) {\n throw new Error(`${field} must be a parseable ISO timestamp`);\n }\n return new Date(parsed).toISOString();\n}\n\nfunction normalizeRelativePath(input: unknown, field: string): string {\n if (typeof input !== \"string\") {\n throw new Error(`${field} must be a POSIX relative path string`);\n }\n return validateArchiveRelativePath(input, field);\n}\n\nfunction assertUniquePaths(entries: readonly { path: string }[], context: string): void {\n const seen = new Set<string>();\n for (const entry of entries) {\n const key = entry.path.toLowerCase();\n if (seen.has(key)) {\n throw new Error(`${context} contains duplicate path: ${entry.path}`);\n }\n seen.add(key);\n }\n}\n\nfunction shouldExcludeRelPath(relPosix: string, includeTranscripts: boolean): boolean {\n const parts = relPosix.split(\"/\");\n if (parts.some((part) => DEFAULT_TRANSFER_EXCLUDE_DIRS.has(part))) return true;\n if (parts.some((part) => part === SYNC_INTERNAL_DIR)) return true;\n if (EXCLUDED_REL_PATHS.has(relPosix)) return true;\n if (!includeTranscripts && parts[0] === \"transcripts\") return true;\n const basename = parts[parts.length - 1] ?? \"\";\n if (EXCLUDED_FILE_NAMES.has(basename)) return true;\n return EXCLUDED_FILE_PREFIXES.some((prefix) => basename.startsWith(prefix));\n}\n\nfunction filterBaseFilesForMode(\n files: readonly OfflineSyncFileState[],\n includeTranscripts: boolean,\n): OfflineSyncFileState[] {\n return files.filter((file) => !shouldExcludeRelPath(file.path, includeTranscripts));\n}\n\nasync function readOfflineSyncFileRecord(\n options: OfflineSyncFileRecordOptions,\n): Promise<OfflineSyncFileRecord> {\n const relPath = validateArchiveRelativePath(options.relPath, \"offlineSyncFile.path\");\n const bytes = options.readFile\n ? await options.readFile({ root: options.root.abs, path: relPath, filePath: options.filePath })\n : await readFile(options.filePath);\n const digest = sha256Buffer(bytes);\n const st = await stat(options.filePath);\n return {\n path: relPath,\n sha256: digest.sha256,\n bytes: digest.bytes,\n mtimeMs: st.mtimeMs,\n ...(options.includeContent ? { contentBase64: bytes.toString(\"base64\") } : {}),\n };\n}\n\nasync function fileIsSecureStoreEncrypted(filePath: string): Promise<boolean> {\n const handle = await open(filePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const { bytesRead } = await handle.read(header, 0, header.length, 0);\n return bytesRead >= MAGIC_HEADER_SIZE && isEncryptedFile(header);\n } finally {\n await handle.close();\n }\n}\n\nasync function readPlainFileContentChunk(options: {\n filePath: string;\n offset: number;\n length: number;\n bytes: number;\n}): Promise<Buffer> {\n const chunkBytes = Math.min(options.length, options.bytes - options.offset);\n const chunk = Buffer.alloc(chunkBytes);\n if (chunkBytes === 0) return chunk;\n const handle = await open(options.filePath, \"r\");\n try {\n const { bytesRead } = await handle.read(chunk, 0, chunk.length, options.offset);\n return bytesRead === chunk.length ? chunk : chunk.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n\nexport async function buildOfflineSyncSnapshot(options: {\n root: string;\n sourceId: string;\n includeContent?: boolean;\n includeTranscripts?: boolean;\n now?: Date;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n}): Promise<OfflineSyncSnapshot> {\n const rootAbs = path.resolve(options.root);\n const root = await prepareSafeArchiveRoot(rootAbs, \"buildOfflineSyncSnapshot\", \"root\");\n const includeTranscripts = options.includeTranscripts !== false;\n const files: OfflineSyncFileRecord[] = [];\n\n async function walk(dirAbs: string): Promise<void> {\n let entries = await readdir(dirAbs, { withFileTypes: true });\n entries = entries.sort((left, right) => left.name.localeCompare(right.name));\n for (const entry of entries) {\n const abs = path.join(dirAbs, entry.name);\n const relPosix = path.relative(root.abs, abs).split(path.sep).join(\"/\");\n if (shouldExcludeRelPath(relPosix, includeTranscripts)) continue;\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) {\n await walk(abs);\n continue;\n }\n if (!entry.isFile()) continue;\n files.push(await readOfflineSyncFileRecord({\n root,\n relPath: relPosix,\n filePath: abs,\n includeContent: options.includeContent === true,\n readFile: options.readFile,\n }));\n }\n }\n\n await walk(root.abs);\n\n return {\n format: OFFLINE_SYNC_SNAPSHOT_FORMAT,\n schemaVersion: 1,\n createdAt: (options.now ?? new Date()).toISOString(),\n sourceId: normalizeSourceId(options.sourceId, \"sourceId\"),\n includeTranscripts,\n files: files.sort(compareByPath),\n };\n}\n\nexport async function buildOfflineSyncSnapshotForPaths(options: {\n root: string;\n sourceId: string;\n paths: readonly string[];\n includeContent?: boolean;\n includeTranscripts?: boolean;\n now?: Date;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n}): Promise<OfflineSyncSnapshot> {\n const rootAbs = path.resolve(options.root);\n const root = await prepareSafeArchiveRoot(rootAbs, \"buildOfflineSyncSnapshotForPaths\", \"root\");\n const includeTranscripts = options.includeTranscripts !== false;\n const files: OfflineSyncFileRecord[] = [];\n const seen = new Set<string>();\n\n for (const rawPath of options.paths) {\n const relPath = normalizeRelativePath(rawPath, \"paths[]\");\n if (seen.has(relPath)) continue;\n seen.add(relPath);\n if (shouldExcludeRelPath(relPath, includeTranscripts)) {\n throw new Error(`offline sync snapshot path is excluded: ${relPath}`);\n }\n const filePath = await resolveSafeArchiveTarget(root, relPath);\n const st = await lstat(filePath).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n });\n if (!st || st.isSymbolicLink() || !st.isFile()) continue;\n files.push(await readOfflineSyncFileRecord({\n root,\n relPath,\n filePath,\n includeContent: options.includeContent === true,\n readFile: options.readFile,\n }));\n }\n\n return {\n format: OFFLINE_SYNC_SNAPSHOT_FORMAT,\n schemaVersion: 1,\n createdAt: (options.now ?? new Date()).toISOString(),\n sourceId: normalizeSourceId(options.sourceId, \"sourceId\"),\n includeTranscripts,\n files: files.sort(compareByPath),\n };\n}\n\nexport async function readOfflineSyncFileContentChunk(options: {\n root: string;\n path: string;\n offset?: number;\n length?: number;\n includeTranscripts?: boolean;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n}): Promise<OfflineSyncFileContentChunk> {\n const rootAbs = path.resolve(options.root);\n const root = await prepareSafeArchiveRoot(rootAbs, \"readOfflineSyncFileContentChunk\", \"root\");\n const includeTranscripts = options.includeTranscripts !== false;\n const relPath = normalizeRelativePath(options.path, \"path\");\n if (shouldExcludeRelPath(relPath, includeTranscripts)) {\n throw new Error(`offline sync file content path is excluded: ${relPath}`);\n }\n const offset = options.offset === undefined\n ? 0\n : assertNonNegativeInteger(options.offset, \"offset\");\n const requestedLength = options.length === undefined\n ? OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES\n : assertNonNegativeInteger(options.length, \"length\");\n if (requestedLength < 1 || requestedLength > OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES) {\n throw new Error(\n `length must be an integer from 1 to ${OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES}`,\n );\n }\n const filePath = await resolveSafeArchiveTarget(root, relPath);\n const st = await lstat(filePath).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n });\n if (!st || st.isSymbolicLink() || !st.isFile()) {\n throw new Error(`offline sync file content path not found: ${relPath}`);\n }\n const encrypted = await fileIsSecureStoreEncrypted(filePath);\n if (!encrypted) {\n if (offset > st.size) {\n throw new Error(`offset must be <= file size for ${relPath}`);\n }\n const chunk = await readPlainFileContentChunk({\n filePath,\n offset,\n length: requestedLength,\n bytes: st.size,\n });\n return {\n path: relPath,\n bytes: st.size,\n mtimeMs: st.mtimeMs,\n offset,\n chunkBytes: chunk.length,\n content: chunk,\n };\n }\n if (!options.readFile) {\n throw new Error(`offline sync file content requires a secure-store read hook: ${relPath}`);\n }\n const content = await options.readFile({ root: root.abs, path: relPath, filePath });\n if (offset > content.length) {\n throw new Error(`offset must be <= file size for ${relPath}`);\n }\n const digest = sha256Buffer(content);\n const end = Math.min(content.length, offset + requestedLength);\n const chunk = content.subarray(offset, end);\n return {\n path: relPath,\n sha256: digest.sha256,\n bytes: digest.bytes,\n mtimeMs: st.mtimeMs,\n offset,\n chunkBytes: chunk.length,\n content: Buffer.from(chunk),\n };\n}\n\nexport async function buildOfflineSyncChangeset(options: {\n root: string;\n sourceId: string;\n baseFiles?: readonly OfflineSyncFileState[];\n includeTranscripts?: boolean;\n now?: Date;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n}): Promise<OfflineSyncChangeset> {\n const includeTranscripts = options.includeTranscripts !== false;\n const base = byPath(filterBaseFilesForMode(\n normalizeFileStates(options.baseFiles),\n includeTranscripts,\n ));\n const current = await buildOfflineSyncSnapshot({\n root: options.root,\n sourceId: options.sourceId,\n includeContent: false,\n includeTranscripts,\n now: options.now,\n readFile: options.readFile,\n });\n const currentMap = byPath(current.files);\n const changes: OfflineSyncChange[] = [];\n\n for (const relPath of unionPaths(base, currentMap)) {\n const baseEntry = base.get(relPath);\n const currentEntry = currentMap.get(relPath);\n if (currentEntry && currentEntry.sha256 !== baseEntry?.sha256) {\n const file = await buildOfflineSyncSnapshotForPaths({\n root: options.root,\n sourceId: options.sourceId,\n paths: [relPath],\n includeContent: true,\n includeTranscripts,\n now: options.now,\n readFile: options.readFile,\n });\n const record = file.files[0];\n if (!record || typeof record.contentBase64 !== \"string\" || record.sha256 !== currentEntry.sha256) {\n throw new Error(`offline sync file changed while building changeset: ${relPath}`);\n }\n changes.push({\n type: \"upsert\",\n path: relPath,\n ...(baseEntry ? { baseSha256: baseEntry.sha256 } : {}),\n file: record as OfflineSyncFileRecord & { contentBase64: string },\n });\n continue;\n }\n if (!currentEntry && baseEntry) {\n changes.push({\n type: \"delete\",\n path: relPath,\n baseSha256: baseEntry.sha256,\n });\n }\n }\n\n return {\n format: OFFLINE_SYNC_CHANGESET_FORMAT,\n schemaVersion: 1,\n createdAt: (options.now ?? new Date()).toISOString(),\n sourceId: normalizeSourceId(options.sourceId, \"sourceId\"),\n includeTranscripts: current.includeTranscripts,\n changes: changes.sort(compareByPath),\n };\n}\n\nexport function summarizeOfflineSyncChangeset(\n changeset: OfflineSyncChangeset,\n): OfflineSyncChangesetSummary {\n const upserts = changeset.changes.filter((change) => change.type === \"upsert\").length;\n const deletes = changeset.changes.filter((change) => change.type === \"delete\").length;\n return {\n upserts,\n deletes,\n total: changeset.changes.length,\n };\n}\n\nexport async function applyOfflineSyncSnapshot(options: {\n root: string;\n snapshot: unknown;\n baseFiles?: readonly OfflineSyncFileState[];\n writeConflictCopies?: boolean;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;\n deleteFile?: (target: OfflineSyncFileTarget) => Promise<void>;\n}): Promise<OfflineSyncApplySnapshotResult> {\n const snapshot = normalizeOfflineSyncSnapshot(options.snapshot);\n const baseMap = byPath(filterBaseFilesForMode(\n normalizeFileStates(options.baseFiles),\n snapshot.includeTranscripts,\n ));\n const incomingMap = byPath(snapshot.files);\n const incomingBuffers = verifyRecordContents(snapshot.files, \"offline sync snapshot\", {\n requireContent: false,\n });\n const root = await ensureSyncRoot(options.root, \"applyOfflineSyncSnapshot\");\n const current = await buildOfflineSyncSnapshot({\n root: root.abs,\n sourceId: \"local\",\n includeContent: false,\n includeTranscripts: snapshot.includeTranscripts,\n readFile: options.readFile,\n });\n const currentMap = byPath(current.files);\n const nextBase = new Map(baseMap);\n const conflicts: OfflineSyncConflict[] = [];\n let upserted = 0;\n let deleted = 0;\n let skipped = 0;\n let pendingLocal = 0;\n\n for (const relPath of unionPaths(baseMap, incomingMap, currentMap)) {\n const base = baseMap.get(relPath);\n const incoming = incomingMap.get(relPath);\n const currentEntry = currentMap.get(relPath);\n\n if (incoming) {\n if (currentEntry?.sha256 === incoming.sha256) {\n nextBase.set(relPath, toFileState(incoming));\n skipped += 1;\n continue;\n }\n if (!currentEntry && base && incoming.sha256 === base.sha256) {\n nextBase.set(relPath, base);\n pendingLocal += 1;\n skipped += 1;\n continue;\n }\n if (!currentEntry && base && incoming.sha256 !== base.sha256) {\n conflicts.push(await recordConflict({\n root,\n relPath,\n reason: \"local_deleted_remote_modified\",\n baseSha256: base.sha256,\n incomingSha256: incoming.sha256,\n incomingBuffer: options.writeConflictCopies === false\n ? incomingBuffers.get(relPath)\n : requiredBuffer(incomingBuffers, relPath),\n writeConflictCopies: options.writeConflictCopies !== false,\n sourceId: snapshot.sourceId,\n writeFile: options.writeFile,\n }));\n nextBase.set(relPath, base);\n continue;\n }\n if (!currentEntry && !base) {\n await writeSafeFile(root, relPath, requiredBuffer(incomingBuffers, relPath), options.writeFile);\n nextBase.set(relPath, toFileState(incoming));\n upserted += 1;\n continue;\n }\n if (base && currentEntry && currentEntry.sha256 === base.sha256) {\n await writeSafeFile(root, relPath, requiredBuffer(incomingBuffers, relPath), options.writeFile);\n nextBase.set(relPath, toFileState(incoming));\n upserted += 1;\n continue;\n }\n if (base && incoming.sha256 === base.sha256) {\n nextBase.set(relPath, base);\n pendingLocal += 1;\n skipped += 1;\n continue;\n }\n conflicts.push(await recordConflict({\n root,\n relPath,\n reason: base ? \"both_modified\" : \"remote_exists_for_local_create\",\n baseSha256: base?.sha256,\n localSha256: currentEntry?.sha256,\n incomingSha256: incoming.sha256,\n incomingBuffer: options.writeConflictCopies === false\n ? incomingBuffers.get(relPath)\n : requiredBuffer(incomingBuffers, relPath),\n writeConflictCopies: options.writeConflictCopies !== false,\n sourceId: snapshot.sourceId,\n writeFile: options.writeFile,\n }));\n if (base) nextBase.set(relPath, base);\n continue;\n }\n\n if (!currentEntry) {\n nextBase.delete(relPath);\n skipped += 1;\n continue;\n }\n if (base && currentEntry.sha256 === base.sha256) {\n await deleteSafeFile(root, relPath, options.deleteFile);\n nextBase.delete(relPath);\n deleted += 1;\n continue;\n }\n if (base) {\n conflicts.push({\n path: relPath,\n reason: \"local_modified_remote_deleted\",\n baseSha256: base.sha256,\n localSha256: currentEntry.sha256,\n });\n nextBase.set(relPath, base);\n continue;\n }\n pendingLocal += 1;\n skipped += 1;\n }\n\n return {\n upserted,\n deleted,\n skipped,\n pendingLocal,\n conflicts,\n nextBaseFiles: [...nextBase.values()].sort(compareByPath),\n };\n}\n\nexport async function applyOfflineSyncChangeset(options: {\n root: string;\n changeset: unknown;\n writeConflictCopies?: boolean;\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;\n deleteFile?: (target: OfflineSyncFileTarget) => Promise<void>;\n}): Promise<OfflineSyncApplyChangesetResult> {\n let changeset: OfflineSyncChangeset;\n try {\n changeset = normalizeOfflineSyncChangeset(options.changeset);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n message.startsWith(\"offline sync\")\n ? message\n : `offline sync changeset invalid: ${message}`,\n );\n }\n const root = await ensureSyncRoot(options.root, \"applyOfflineSyncChangeset\");\n const records = changeset.changes\n .filter((change): change is Extract<OfflineSyncChange, { type: \"upsert\" }> => change.type === \"upsert\")\n .map((change) => change.file);\n const incomingBuffers = verifyRecordContents(records, \"offline sync changeset\");\n const current = await buildOfflineSyncSnapshot({\n root: root.abs,\n sourceId: \"local\",\n includeContent: false,\n includeTranscripts: changeset.includeTranscripts,\n readFile: options.readFile,\n });\n const currentMap = byPath(current.files);\n const conflicts: OfflineSyncConflict[] = [];\n let appliedUpserts = 0;\n let appliedDeletes = 0;\n let skipped = 0;\n\n for (const change of changeset.changes) {\n const currentEntry = currentMap.get(change.path);\n if (change.type === \"upsert\") {\n if (currentEntry?.sha256 === change.file.sha256) {\n skipped += 1;\n continue;\n }\n if (!change.baseSha256) {\n if (!currentEntry) {\n await writeSafeFile(root, change.path, requiredBuffer(incomingBuffers, change.path), options.writeFile);\n currentMap.set(change.path, toFileState(change.file));\n appliedUpserts += 1;\n continue;\n }\n conflicts.push(await recordConflict({\n root,\n relPath: change.path,\n reason: \"remote_exists_for_local_create\",\n localSha256: currentEntry.sha256,\n incomingSha256: change.file.sha256,\n incomingBuffer: incomingBuffers.get(change.path),\n writeConflictCopies: options.writeConflictCopies !== false,\n sourceId: changeset.sourceId,\n writeFile: options.writeFile,\n }));\n continue;\n }\n if (currentEntry?.sha256 === change.baseSha256) {\n await writeSafeFile(root, change.path, requiredBuffer(incomingBuffers, change.path), options.writeFile);\n currentMap.set(change.path, toFileState(change.file));\n appliedUpserts += 1;\n continue;\n }\n conflicts.push(await recordConflict({\n root,\n relPath: change.path,\n reason: currentEntry ? \"remote_changed_for_local_update\" : \"remote_deleted_for_local_update\",\n baseSha256: change.baseSha256,\n localSha256: currentEntry?.sha256,\n incomingSha256: change.file.sha256,\n incomingBuffer: incomingBuffers.get(change.path),\n writeConflictCopies: options.writeConflictCopies !== false,\n sourceId: changeset.sourceId,\n writeFile: options.writeFile,\n }));\n continue;\n }\n\n if (!currentEntry) {\n skipped += 1;\n continue;\n }\n if (currentEntry.sha256 === change.baseSha256) {\n await deleteSafeFile(root, change.path, options.deleteFile);\n currentMap.delete(change.path);\n appliedDeletes += 1;\n continue;\n }\n conflicts.push({\n path: change.path,\n reason: \"remote_changed_for_local_delete\",\n baseSha256: change.baseSha256,\n localSha256: currentEntry.sha256,\n });\n }\n\n return {\n appliedUpserts,\n appliedDeletes,\n skipped,\n conflicts,\n currentFiles: [...currentMap.values()].sort(compareByPath),\n };\n}\n\nfunction verifyRecordContents(\n records: readonly OfflineSyncFileRecord[],\n context: string,\n options: { requireContent?: boolean } = {},\n): Map<string, Buffer> {\n const buffers = new Map<string, Buffer>();\n for (const record of records) {\n if (typeof record.contentBase64 !== \"string\") {\n if (options.requireContent === false) continue;\n throw new Error(`${context}: contentBase64 is required for ${record.path}`);\n }\n const buffer = Buffer.from(record.contentBase64, \"base64\");\n const digest = sha256Buffer(buffer);\n if (digest.sha256 !== record.sha256 || digest.bytes !== record.bytes) {\n throw new Error(\n `${context}: content checksum mismatch for ${record.path}`,\n );\n }\n buffers.set(record.path, buffer);\n }\n return buffers;\n}\n\nfunction requiredBuffer(buffers: Map<string, Buffer>, relPath: string): Buffer {\n const buffer = buffers.get(relPath);\n if (!buffer) {\n throw new Error(`missing decoded content for ${relPath}`);\n }\n return buffer;\n}\n\nasync function ensureSyncRoot(rootPath: string, errorPrefix: string): Promise<SafeArchiveRoot> {\n const rootAbs = path.resolve(rootPath);\n await mkdir(rootAbs, { recursive: true });\n return prepareSafeArchiveRoot(rootAbs, errorPrefix, \"root\");\n}\n\nfunction byPath<T extends OfflineSyncFileState>(files: readonly T[]): Map<string, T> {\n const out = new Map<string, T>();\n for (const file of files) {\n out.set(validateArchiveRelativePath(file.path, \"offlineSync\"), file);\n }\n return out;\n}\n\nfunction unionPaths(...maps: Array<Map<string, unknown>>): string[] {\n const paths = new Set<string>();\n for (const map of maps) {\n for (const key of map.keys()) paths.add(key);\n }\n return [...paths].sort();\n}\n\nfunction toFileState(file: OfflineSyncFileState): OfflineSyncFileState {\n return {\n path: file.path,\n sha256: file.sha256,\n bytes: file.bytes,\n mtimeMs: file.mtimeMs,\n };\n}\n\nasync function writeSafeFile(\n root: SafeArchiveRoot,\n relPath: string,\n content: Buffer,\n writeFileHook?: (target: OfflineSyncFileWriteTarget) => Promise<void>,\n): Promise<void> {\n const target = await resolveSafeArchiveTarget(root, relPath);\n if (writeFileHook) {\n await writeFileHook({ root: root.abs, path: relPath, filePath: target, content });\n return;\n }\n await mkdir(path.dirname(target), { recursive: true });\n const tmp = path.join(\n path.dirname(target),\n `.remnic-sync.${process.pid}.${randomUUID()}.tmp`,\n );\n await writeFile(tmp, content);\n try {\n const targetStat = await lstat(target).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n });\n if (targetStat?.isSymbolicLink()) {\n throw new Error(`offline sync target is a symlink: ${relPath}`);\n }\n await rename(tmp, target);\n } catch (error) {\n await unlink(tmp).catch(() => {});\n throw error;\n }\n}\n\nasync function deleteSafeFile(\n root: SafeArchiveRoot,\n relPath: string,\n deleteFile?: (target: OfflineSyncFileTarget) => Promise<void>,\n): Promise<void> {\n const target = await resolveSafeArchiveTarget(root, relPath);\n if (deleteFile) {\n await deleteFile({ root: root.abs, path: relPath, filePath: target });\n return;\n }\n await unlink(target).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw error;\n });\n}\n\nasync function recordConflict(options: {\n root: SafeArchiveRoot;\n relPath: string;\n reason: OfflineSyncConflict[\"reason\"];\n baseSha256?: string;\n localSha256?: string;\n incomingSha256?: string;\n incomingBuffer?: Buffer;\n writeConflictCopies: boolean;\n sourceId: string;\n writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;\n}): Promise<OfflineSyncConflict> {\n let conflictPath: string | undefined;\n if (options.writeConflictCopies && options.incomingBuffer) {\n const sourceHash = hashText(options.sourceId).slice(0, 12);\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n conflictPath = `${SYNC_INTERNAL_DIR}/conflicts/${stamp}-${sourceHash}/${options.relPath}`;\n await writeSafeFile(options.root, conflictPath, options.incomingBuffer, options.writeFile);\n }\n return {\n path: options.relPath,\n reason: options.reason,\n baseSha256: options.baseSha256,\n localSha256: options.localSha256,\n incomingSha256: options.incomingSha256,\n ...(conflictPath ? { conflictPath } : {}),\n };\n}\n\nexport function defaultOfflineSyncStatePath(\n memoryDir: string,\n remoteId: string,\n namespace?: string,\n): string {\n const key = hashText(`${remoteId}\\0${namespace ?? \"\"}`).slice(0, 16);\n return path.join(path.resolve(memoryDir), SYNC_INTERNAL_DIR, \"state\", `${key}.json`);\n}\n\nexport async function readOfflineSyncState(\n statePath: string,\n): Promise<OfflineSyncState | null> {\n let raw: string;\n try {\n raw = await readFile(path.resolve(statePath), \"utf-8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n const parsed = JSON.parse(raw) as unknown;\n return normalizeOfflineSyncState(parsed);\n}\n\nexport async function writeOfflineSyncState(\n statePath: string,\n state: OfflineSyncState,\n): Promise<void> {\n const normalized = normalizeOfflineSyncState(state);\n const target = path.resolve(statePath);\n await mkdir(path.dirname(target), { recursive: true });\n const tmp = path.join(\n path.dirname(target),\n `.remnic-sync-state.${process.pid}.${randomUUID()}.tmp`,\n );\n await writeFile(tmp, JSON.stringify(normalized, null, 2) + \"\\n\", \"utf-8\");\n try {\n await rename(tmp, target);\n } catch (error) {\n await unlink(tmp).catch(() => {});\n throw error;\n }\n}\n\nexport function offlineSyncStateFromSnapshot(options: {\n remoteId: string;\n namespace?: string;\n snapshot: OfflineSyncSnapshot;\n baseFiles?: readonly OfflineSyncFileState[];\n}): OfflineSyncState {\n const snapshot = normalizeOfflineSyncSnapshot(options.snapshot);\n return normalizeOfflineSyncState({\n version: OFFLINE_SYNC_STATE_VERSION,\n remoteId: options.remoteId,\n namespace: options.namespace,\n includeTranscripts: snapshot.includeTranscripts,\n lastSyncedAt: new Date().toISOString(),\n baseFiles: options.baseFiles ?? snapshot.files.map(toFileState),\n });\n}\n\nexport function normalizeOfflineSyncState(input: unknown): OfflineSyncState {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) {\n throw new Error(\"offline sync state must be an object\");\n }\n const obj = input as Record<string, unknown>;\n if (obj.version !== OFFLINE_SYNC_STATE_VERSION) {\n throw new Error(`offline sync state version must be ${OFFLINE_SYNC_STATE_VERSION}`);\n }\n const namespace =\n typeof obj.namespace === \"string\" && obj.namespace.trim().length > 0\n ? obj.namespace.trim()\n : undefined;\n const baseFiles = normalizeFileStates(obj.baseFiles as readonly unknown[] | undefined)\n .sort(compareByPath);\n assertUniquePaths(baseFiles, \"offline sync state\");\n return {\n version: OFFLINE_SYNC_STATE_VERSION,\n remoteId: normalizeSourceId(obj.remoteId, \"remoteId\"),\n ...(namespace ? { namespace } : {}),\n includeTranscripts: assertBoolean(obj.includeTranscripts, \"includeTranscripts\"),\n lastSyncedAt: normalizeIsoString(obj.lastSyncedAt, \"lastSyncedAt\"),\n baseFiles,\n };\n}\n\nexport function fileStatesFromSnapshot(snapshot: OfflineSyncSnapshot): OfflineSyncFileState[] {\n return normalizeOfflineSyncSnapshot(snapshot).files.map(toFileState).sort(compareByPath);\n}\n"],"mappings":"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","names":["chunk"]}
|
|
File without changes
|