selfstore 1.8.34 → 1.8.35
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/advanced.d.ts +9 -9
- package/dist/advanced.js +2 -2
- package/dist/backups.d.ts +4 -4
- package/dist/{cache-D8Z4u5Jz.d.ts → cache-C1edLncd.d.ts} +3 -1
- package/dist/{chunk-A2RRJLIF.js → chunk-3P2W3XQD.js} +36 -23
- package/dist/chunk-3P2W3XQD.js.map +1 -0
- package/dist/{chunk-HQSK7HK3.js → chunk-SHX36MIE.js} +28 -17
- package/dist/chunk-SHX36MIE.js.map +1 -0
- package/dist/{connect-B9f58dFZ.d.ts → connect-D7Yzdi9N.d.ts} +4 -4
- package/dist/{drive-D5YLXmp_.d.ts → drive-3K6pBAmG.d.ts} +2 -2
- package/dist/flows.d.ts +4 -4
- package/dist/groups.d.ts +2 -2
- package/dist/index.d.ts +9 -9
- package/dist/index.js +3 -3
- package/dist/{local-CmS0xFkK.d.ts → local-Cr5cCaQ2.d.ts} +2 -2
- package/dist/{manager-DCV_7B6M.d.ts → manager-Bl16FR-e.d.ts} +2 -2
- package/dist/proof.d.ts +6 -6
- package/dist/{s3-mHLrEywJ.d.ts → s3-C2PrgMpD.d.ts} +2 -2
- package/dist/{simple-Bnh2j78j.d.ts → simple-Dp1tbrwo.d.ts} +6 -6
- package/dist/{store-DSvZwwr1.d.ts → store-B4Zo0f6G.d.ts} +1 -1
- package/dist/sync.js +1 -1
- package/dist/widgets.d.ts +5 -5
- package/package.json +1 -1
- package/dist/chunk-A2RRJLIF.js.map +0 -1
- package/dist/chunk-HQSK7HK3.js.map +0 -1
package/dist/advanced.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { B as BackupTarget } from './store-
|
|
2
|
-
export { A as AuthExpiredError, f as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, g as LocalStoreOptions, e as LocalStoreState, M as MirrorState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, P as PeerSource, k as PeerState, R as RESERVED_COLLECTION_PREFIX, l as RESERVED_STORE_MODES, m as RecipientStanza, n as ReplicaState, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, a as Snapshot, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, s as StatusInput, t as StorageState, S as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-
|
|
3
|
-
import { K as KV } from './cache-
|
|
4
|
-
export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-
|
|
5
|
-
export { G as GisDriveAuthOptions, a as GisTokenPersistence, d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, b as inspect, c as isEncrypted, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-
|
|
6
|
-
import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-
|
|
7
|
-
export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-
|
|
8
|
-
import { c as connect$1, f as fromSession$1, p as preview, b as adopt, d as findOrCreateOwnFile, l as listBackups, e as createBackup, g as deleteBackup, r as renameBackup, h as account, i as createCompanion, s as share, u as unshare, o as owner, j as secondary } from './drive-
|
|
9
|
-
export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive-
|
|
1
|
+
import { B as BackupTarget } from './store-B4Zo0f6G.js';
|
|
2
|
+
export { A as AuthExpiredError, f as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, g as LocalStoreOptions, e as LocalStoreState, M as MirrorState, h as Mode, i as PasswordCheck, b as PasswordPolicy, j as PasswordRequirement, P as PeerSource, k as PeerState, R as RESERVED_COLLECTION_PREFIX, l as RESERVED_STORE_MODES, m as RecipientStanza, n as ReplicaState, o as SelfstoreError, p as SelfstoreErrorCode, q as Severity, a as Snapshot, c as SnapshotFile, r as StatusAction, d as StatusDescriptor, s as StatusInput, t as StorageState, S as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-B4Zo0f6G.js';
|
|
3
|
+
import { K as KV } from './cache-C1edLncd.js';
|
|
4
|
+
export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-C1edLncd.js';
|
|
5
|
+
export { G as GisDriveAuthOptions, a as GisTokenPersistence, d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, b as inspect, c as isEncrypted, p as pickFromDisk, f as preloadGoogleIdentity, s as saveToDisk } from './local-Cr5cCaQ2.js';
|
|
6
|
+
import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-C2PrgMpD.js';
|
|
7
|
+
export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-C2PrgMpD.js';
|
|
8
|
+
import { c as connect$1, f as fromSession$1, p as preview, b as adopt, d as findOrCreateOwnFile, l as listBackups, e as createBackup, g as deleteBackup, r as renameBackup, h as account, i as createCompanion, s as share, u as unshare, o as owner, j as secondary } from './drive-3K6pBAmG.js';
|
|
9
|
+
export { k as DriveAccount, a as DriveAuth, m as DriveBackupInfo, D as DriveOptions } from './drive-3K6pBAmG.js';
|
|
10
10
|
import './sync.js';
|
|
11
11
|
|
|
12
12
|
/**
|
package/dist/advanced.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { peer, fromSession as fromSession$3, connect as connect$3 } from './chunk-GHSHIDF3.js';
|
|
2
2
|
import { fromSession as fromSession$2, connect as connect$2 } from './chunk-IDW3WQKF.js';
|
|
3
|
-
export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity } from './chunk-
|
|
4
|
-
import './chunk-
|
|
3
|
+
export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity } from './chunk-SHX36MIE.js';
|
|
4
|
+
import './chunk-3P2W3XQD.js';
|
|
5
5
|
export { datedName, pickFromDisk, saveToDisk } from './chunk-XAONTUEC.js';
|
|
6
6
|
import './chunk-FJGWTBAL.js';
|
|
7
7
|
import { openExisting, isOpenSupported, isSupported, fromSession as fromSession$1, connect as connect$1 } from './chunk-HVHQLPP6.js';
|
package/dist/backups.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { B as BackupsHost } from './manager-
|
|
2
|
-
export { a as BackupFileInfo, b as BackupOwner, c as BackupRow, d as BackupsErrorCode, e as BackupsKeys, f as BackupsManager, g as BackupsNaming, h as BackupsSnapshot, K as KnownBackups, i as createBackupsManager } from './manager-
|
|
1
|
+
import { B as BackupsHost } from './manager-Bl16FR-e.js';
|
|
2
|
+
export { a as BackupFileInfo, b as BackupOwner, c as BackupRow, d as BackupsErrorCode, e as BackupsKeys, f as BackupsManager, g as BackupsNaming, h as BackupsSnapshot, K as KnownBackups, i as createBackupsManager } from './manager-Bl16FR-e.js';
|
|
3
3
|
import { DriveOptions } from './advanced.js';
|
|
4
|
-
import './cache-
|
|
5
|
-
import './store-
|
|
4
|
+
import './cache-C1edLncd.js';
|
|
5
|
+
import './store-B4Zo0f6G.js';
|
|
6
6
|
import './sync.js';
|
|
7
7
|
import './machine-Dhbx3Eut.js';
|
|
8
8
|
|
|
@@ -88,7 +88,9 @@ interface LocalCache {
|
|
|
88
88
|
} | null>;
|
|
89
89
|
/** Overwrite the collections snapshot (small; written on every change). */
|
|
90
90
|
saveCollections(collections: Record<string, unknown[]>): Promise<void>;
|
|
91
|
-
/** Reconcile the file store: add new blobs, drop removed ones
|
|
91
|
+
/** Reconcile the file store: add new blobs, drop removed ones. A known id
|
|
92
|
+
* is rewritten when its Blob is not the one this cache last loaded or
|
|
93
|
+
* wrote for it, which is how a replaced body reaches the disk. */
|
|
92
94
|
saveFiles(files: CachedFile[]): Promise<void>;
|
|
93
95
|
/** Wipe every locally-stored byte (the "forget me on this device" action). */
|
|
94
96
|
clear(): Promise<void>;
|
|
@@ -64,22 +64,30 @@ function hash(v) {
|
|
|
64
64
|
}
|
|
65
65
|
return h >>> 0;
|
|
66
66
|
}
|
|
67
|
+
function dict(from) {
|
|
68
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
69
|
+
if (from) for (const [k, v] of Object.entries(from)) out[k] = v;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
67
72
|
function emptyCol() {
|
|
68
|
-
return { clocks:
|
|
73
|
+
return { clocks: dict(), deleted: dict(), hashes: dict() };
|
|
69
74
|
}
|
|
70
75
|
function cloneCol(c) {
|
|
71
76
|
if (!c) return emptyCol();
|
|
72
77
|
const out = {
|
|
73
|
-
clocks:
|
|
74
|
-
deleted:
|
|
75
|
-
hashes:
|
|
78
|
+
clocks: dict(c.clocks),
|
|
79
|
+
deleted: dict(c.deleted),
|
|
80
|
+
hashes: dict(c.hashes)
|
|
76
81
|
};
|
|
77
82
|
if (c.fields) {
|
|
78
|
-
out.fields =
|
|
79
|
-
for (const [id, f] of Object.entries(c.fields)) out.fields[id] =
|
|
83
|
+
out.fields = dict();
|
|
84
|
+
for (const [id, f] of Object.entries(c.fields)) out.fields[id] = dict(f);
|
|
80
85
|
}
|
|
81
86
|
return out;
|
|
82
87
|
}
|
|
88
|
+
function baseColOf(base, name) {
|
|
89
|
+
return base ? cloneCol(base.cols[name]) : void 0;
|
|
90
|
+
}
|
|
83
91
|
function strategyOf(config, collection) {
|
|
84
92
|
return config.strategies?.[collection] ?? config.fallback ?? "lww-set";
|
|
85
93
|
}
|
|
@@ -136,12 +144,12 @@ function stampPresent(col, present, config, name, tick) {
|
|
|
136
144
|
return seen;
|
|
137
145
|
}
|
|
138
146
|
function stampFields(col, id, doc, tick) {
|
|
139
|
-
const rec = (col.fields ??=
|
|
147
|
+
const rec = (col.fields ??= dict())[id] ??= dict();
|
|
140
148
|
for (const key of Object.keys(doc)) {
|
|
141
149
|
const h = hash(doc[key]);
|
|
142
150
|
if (rec[key]?.[1] !== h) rec[key] = [tick(), h];
|
|
143
151
|
}
|
|
144
|
-
for (const key of Object.keys(rec)) if (!(key
|
|
152
|
+
for (const key of Object.keys(rec)) if (!Object.hasOwn(doc, key)) delete rec[key];
|
|
145
153
|
}
|
|
146
154
|
function tombstoneMissing(col, seen, tick) {
|
|
147
155
|
for (const id of Object.keys(col.clocks)) {
|
|
@@ -218,9 +226,9 @@ function detectConflicts(a, b, config, base) {
|
|
|
218
226
|
const strategy = strategyOf(config, name);
|
|
219
227
|
const aDocs = byId(config, name, a.collections[name]);
|
|
220
228
|
const bDocs = byId(config, name, b.collections[name]);
|
|
221
|
-
const aMeta = a.meta.cols[name]
|
|
222
|
-
const bMeta = b.meta.cols[name]
|
|
223
|
-
const baseMeta = base
|
|
229
|
+
const aMeta = cloneCol(a.meta.cols[name]);
|
|
230
|
+
const bMeta = cloneCol(b.meta.cols[name]);
|
|
231
|
+
const baseMeta = baseColOf(base, name);
|
|
224
232
|
for (const id of /* @__PURE__ */ new Set([...aDocs.keys(), ...bDocs.keys()])) {
|
|
225
233
|
const aClk = aMeta.clocks[id] ?? null;
|
|
226
234
|
const bClk = bMeta.clocks[id] ?? null;
|
|
@@ -272,9 +280,9 @@ function mergeCollection(name, a, b, config, base, conflicts) {
|
|
|
272
280
|
const strategy = strategyOf(config, name);
|
|
273
281
|
const aDocs = byId(config, name, a.collections[name]);
|
|
274
282
|
const bDocs = byId(config, name, b.collections[name]);
|
|
275
|
-
const aMeta = a.meta.cols[name]
|
|
276
|
-
const bMeta = b.meta.cols[name]
|
|
277
|
-
const baseMeta = base
|
|
283
|
+
const aMeta = cloneCol(a.meta.cols[name]);
|
|
284
|
+
const bMeta = cloneCol(b.meta.cols[name]);
|
|
285
|
+
const baseMeta = baseColOf(base, name);
|
|
278
286
|
const col = emptyCol();
|
|
279
287
|
const docs = [];
|
|
280
288
|
const ids = /* @__PURE__ */ new Set([
|
|
@@ -310,10 +318,10 @@ function mergeCollection(name, a, b, config, base, conflicts) {
|
|
|
310
318
|
const merged = mergeFields(id, aDocs.get(id), bDocs.get(id), aMeta, bMeta, winnerSide);
|
|
311
319
|
if (merged) {
|
|
312
320
|
doc = merged.doc;
|
|
313
|
-
(col.fields ??=
|
|
321
|
+
(col.fields ??= dict())[id] = merged.fields;
|
|
314
322
|
} else {
|
|
315
323
|
const f = (winnerSide === "local" ? aMeta : bMeta).fields?.[id];
|
|
316
|
-
if (f) (col.fields ??=
|
|
324
|
+
if (f) (col.fields ??= dict())[id] = dict(f);
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
if (doc !== void 0) {
|
|
@@ -326,17 +334,22 @@ function mergeCollection(name, a, b, config, base, conflicts) {
|
|
|
326
334
|
}
|
|
327
335
|
function mergeFields(id, aDoc, bDoc, aMeta, bMeta, winnerSide) {
|
|
328
336
|
if (!isRecord(aDoc) || !isRecord(bDoc)) return null;
|
|
329
|
-
const aF = aMeta.fields?.[id] ??
|
|
330
|
-
const bF = bMeta.fields?.[id] ??
|
|
337
|
+
const aF = aMeta.fields?.[id] ?? dict();
|
|
338
|
+
const bF = bMeta.fields?.[id] ?? dict();
|
|
331
339
|
const doc = {};
|
|
332
|
-
const fields =
|
|
340
|
+
const fields = dict();
|
|
333
341
|
for (const key of /* @__PURE__ */ new Set([...Object.keys(aDoc), ...Object.keys(bDoc)])) {
|
|
334
342
|
const a = aF[key] ?? null;
|
|
335
343
|
const b = bF[key] ?? null;
|
|
336
344
|
const side = pickFieldSide(a, b, winnerSide);
|
|
337
345
|
const src = side === "local" ? aDoc : bDoc;
|
|
338
|
-
if (!(key
|
|
339
|
-
doc
|
|
346
|
+
if (!Object.hasOwn(src, key)) continue;
|
|
347
|
+
Object.defineProperty(doc, key, {
|
|
348
|
+
value: src[key],
|
|
349
|
+
enumerable: true,
|
|
350
|
+
writable: true,
|
|
351
|
+
configurable: true
|
|
352
|
+
});
|
|
340
353
|
const stamped = side === "local" ? a : b;
|
|
341
354
|
if (stamped) fields[key] = stamped;
|
|
342
355
|
}
|
|
@@ -382,5 +395,5 @@ function changes(before, after, config) {
|
|
|
382
395
|
}
|
|
383
396
|
|
|
384
397
|
export { changes, createMeta, createNode, detectConflicts, gcTombstones, idOf, idPathFor, merge, stamp, unsyncableCounts };
|
|
385
|
-
//# sourceMappingURL=chunk-
|
|
386
|
-
//# sourceMappingURL=chunk-
|
|
398
|
+
//# sourceMappingURL=chunk-3P2W3XQD.js.map
|
|
399
|
+
//# sourceMappingURL=chunk-3P2W3XQD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sync/hlc.ts","../src/sync/merge.ts"],"names":[],"mappings":"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","file":"chunk-3P2W3XQD.js","sourcesContent":["/**\n * Hybrid Logical Clock: wall time + a counter + a node id, giving a\n * deterministic total order that tolerates clock skew. Encoded as a fixed-width\n * string so a plain string comparison equals causal order.\n */\n\nexport type Hlc = string;\n\ninterface Parts {\n\twall: number;\n\tcounter: number;\n\tnode: string;\n}\n\nconst WALL_W = 15;\nconst CTR_W = 6;\n\nfunction encode(p: Parts): Hlc {\n\treturn `${String(p.wall).padStart(WALL_W, '0')}|${String(p.counter).padStart(CTR_W, '0')}|${p.node}`;\n}\n\nfunction decode(h: Hlc): Parts {\n\tconst i = h.indexOf('|');\n\tconst j = h.indexOf('|', i + 1);\n\treturn { wall: Number(h.slice(0, i)), counter: Number(h.slice(i + 1, j)), node: h.slice(j + 1) };\n}\n\n/**\n * A fresh random replica id (one per device). Not a secret - but two replicas\n * sharing an id lose the tiebreak that makes the order total, so it comes from\n * the same source as everything else here. `randomUUID` is exposed only in a\n * secure context; `getRandomValues` is not, which is why the fallback is that\n * one and never `Math.random`.\n */\nexport function createNode(): string {\n\tif (typeof crypto === 'undefined') {\n\t\tthrow new TypeError(\n\t\t\t'createNode(): this platform provides no WebCrypto, so no replica id can be minted ' +\n\t\t\t\t'(evergreen browsers, Node 20+).'\n\t\t);\n\t}\n\tif (crypto.randomUUID) return crypto.randomUUID();\n\tconst bytes = new Uint8Array(16);\n\tcrypto.getRandomValues(bytes);\n\treturn [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * How far ahead of local wall time a clock minted elsewhere may sit before this\n * replica refuses it.\n *\n * A hybrid logical clock absorbs the highest value it sees, so ONE impossible\n * wall time does three things at once: it wins every last-writer-wins\n * comparison from then on, it raises the clock of every replica that merges it\n * (and of every replica those merge with, in turn), and it keeps doing so after\n * whoever sent it has been removed from the group. Five minutes leaves room for\n * a device whose clock was never synchronised; nothing legitimate needs more.\n */\nexport const MAX_DRIFT_MS = 5 * 60_000;\n\n/** Whether `h` claims a wall time further ahead than the drift bound allows. */\nexport function driftedAhead(\n\th: Hlc,\n\twallNow: number = Date.now(),\n\tmaxDriftMs: number = MAX_DRIFT_MS\n): boolean {\n\treturn hlcWall(h) > wallNow + maxDriftMs;\n}\n\n/** Issue a clock for a local event. Monotonic per node even if the wall clock moves back. */\nexport function issue(prev: Hlc | null, node: string, wallNow: number = Date.now()): Hlc {\n\tconst p = prev ? decode(prev) : null;\n\tconst prevWall = p ? p.wall : 0;\n\tconst wall = Math.max(wallNow, prevWall);\n\tconst counter = wall === prevWall && p ? p.counter + 1 : 0;\n\treturn encode({ wall, counter, node });\n}\n\n/** Fold a clock observed from another replica into the local one. */\nexport function receive(\n\tlocal: Hlc | null,\n\tremote: Hlc,\n\tnode: string,\n\twallNow: number = Date.now()\n): Hlc {\n\tconst l = local ? decode(local) : { wall: 0, counter: 0, node };\n\tconst r = decode(remote);\n\tconst wall = Math.max(wallNow, l.wall, r.wall);\n\tlet counter: number;\n\tif (wall === l.wall && wall === r.wall) counter = Math.max(l.counter, r.counter) + 1;\n\telse if (wall === l.wall) counter = l.counter + 1;\n\telse if (wall === r.wall) counter = r.counter + 1;\n\telse counter = 0;\n\treturn encode({ wall, counter, node });\n}\n\n/** The wall-time component (ms since epoch) encoded in a clock, for age-based\n * pruning (tombstone GC). Not a precise event time - HLC wall can run ahead of\n * real time under skew - but a safe lower bound on \"how old is this at least\". */\nexport function hlcWall(h: Hlc): number {\n\treturn Number(h.slice(0, h.indexOf('|')));\n}\n\n/** Total order: <0 if a precedes b, >0 if a follows b, 0 only if identical. */\nexport function compare(a: Hlc, b: Hlc): number {\n\tif (a < b) return -1;\n\tif (a > b) return 1;\n\treturn 0;\n}\n\n/** The later of two clocks. */\nexport function max(a: Hlc | null, b: Hlc | null): Hlc {\n\tif (!a) return b as Hlc;\n\tif (!b) return a;\n\treturn compare(a, b) >= 0 ? a : b;\n}\n","/**\n * Schema-agnostic convergent merge for plain JSON. It keeps a small sidecar of\n * metadata (one logical clock per record) and reconciles two replica states with\n * well-known set semantics. Documents stay plain objects: identity is a\n * configured id field, change detection is by content hash, nothing is injected.\n *\n * Per-collection strategy:\n * 'lww-set' records keyed by id; later write per id wins; deletes tombstoned.\n * 'lww-map' records keyed by id, merged field BY field: concurrent edits\n * to different fields of one record both survive; the same field\n * goes to the later clock. Deletes stay record-level.\n * 'grow-set' append-only; entries immutable; union, never a conflict.\n * 'lww-register' a single value; later write wins.\n * 'manual' surface concurrent edits to the same id instead of resolving.\n *\n * For collaborative rich text, store an Automerge/Yjs document as a binary file\n * in the snapshot instead; this engine stops short of sequence CRDTs.\n */\n\nimport { type Hlc, issue, max, compare, createNode, hlcWall, driftedAhead } from './hlc';\nimport { SelfstoreError } from '../selfstore/errors';\nimport { byCodePoint } from '../ordering';\n\nexport type { Hlc } from './hlc';\nexport type Id = string;\nexport type MergeStrategy = 'lww-set' | 'lww-map' | 'grow-set' | 'lww-register' | 'manual';\n\nexport interface SyncConfig {\n\t/** Field holding each record's stable id (default 'id'). Dot-notation allowed. */\n\tidField?: string;\n\t/** Per-collection id path override for records whose id is not at the top level. */\n\tids?: Record<string, string>;\n\t/** Strategy per collection name; anything unlisted uses `fallback`. */\n\tstrategies?: Record<string, MergeStrategy>;\n\t/** Strategy for unlisted collections (default 'lww-set'). */\n\tfallback?: MergeStrategy;\n}\n\n/** Sidecar metadata for one collection, keyed by record id. */\nexport interface ColMeta {\n\tclocks: Record<Id, Hlc>;\n\tdeleted: Record<Id, Hlc>;\n\thashes: Record<Id, number>;\n\t/** 'lww-map' only: per-field [clock, contentHash], so concurrent edits to\n\t * different fields of one record merge instead of one side losing. */\n\tfields?: Record<Id, Record<string, [Hlc, number]>>;\n}\n\n/** Everything one replica tracks. Travels with the data so the other side can merge. */\nexport interface SyncMeta {\n\tnode: string;\n\tclock: Hlc | null;\n\tcols: Record<string, ColMeta>;\n}\n\n/** A same-id concurrent edit the 'manual' strategy refused to auto-resolve.\n * `local`/`remote` carry the two full versions during the session; consumers\n * persisting conflicts (e.g. the store's journal) redact them, so they are\n * absent after a reload - never write user records to unencrypted storage. */\nexport interface Conflict {\n\tcollection: string;\n\tid: Id;\n\tlocal?: unknown;\n\tremote?: unknown;\n\t/** The side kept as the provisional value (so it still converges). */\n\tkept: 'local' | 'remote';\n}\n\nexport interface MergeResult {\n\tcollections: Record<string, unknown[]>;\n\tmeta: SyncMeta;\n\tconflicts: Conflict[];\n}\n\nfunction stableStringify(v: unknown): string {\n\tif (v === null || typeof v !== 'object') return JSON.stringify(v) ?? 'null';\n\tif (Array.isArray(v)) return '[' + v.map(stableStringify).join(',') + ']';\n\tconst o = v as Record<string, unknown>;\n\treturn (\n\t\t'{' +\n\t\tObject.keys(o)\n\t\t\t.sort(byCodePoint)\n\t\t\t.map((k) => JSON.stringify(k) + ':' + stableStringify(o[k]))\n\t\t\t.join(',') +\n\t\t'}'\n\t);\n}\n\n/** FNV-1a 32-bit hash, enough to detect that a record changed. */\nfunction hash(v: unknown): number {\n\tconst s = stableStringify(v);\n\tlet h = 0x811c9dc5;\n\tfor (let i = 0; i < s.length; i++) {\n\t\th ^= s.charCodeAt(i);\n\t\th = Math.imul(h, 0x01000193);\n\t}\n\treturn h >>> 0;\n}\n\n/**\n * A table keyed by a record id or a field name. Ids and field names are free\n * text: on a plain object `__proto__` reads `Object.prototype`, and writing\n * through it pollutes every object of the host. No prototype, no such key.\n */\nfunction dict<T>(from?: Record<string, T>): Record<string, T> {\n\tconst out = Object.create(null) as Record<string, T>;\n\tif (from) for (const [k, v] of Object.entries(from)) out[k] = v;\n\treturn out;\n}\n\nfunction emptyCol(): ColMeta {\n\treturn { clocks: dict(), deleted: dict(), hashes: dict() };\n}\n\n/** A copy of one collection's metadata in prototype-free tables, whatever it\n * was read from (a deserialized replica holds plain objects). */\nfunction cloneCol(c: ColMeta | undefined): ColMeta {\n\tif (!c) return emptyCol();\n\tconst out: ColMeta = {\n\t\tclocks: dict(c.clocks),\n\t\tdeleted: dict(c.deleted),\n\t\thashes: dict(c.hashes)\n\t};\n\tif (c.fields) {\n\t\tout.fields = dict();\n\t\tfor (const [id, f] of Object.entries(c.fields)) out.fields[id] = dict(f);\n\t}\n\treturn out;\n}\n\n/** The base's metadata for one collection, copied like the two sides'. */\nfunction baseColOf(base: SyncMeta | undefined, name: string): ColMeta | undefined {\n\treturn base ? cloneCol(base.cols[name]) : undefined;\n}\n\nfunction strategyOf(config: SyncConfig, collection: string): MergeStrategy {\n\treturn config.strategies?.[collection] ?? config.fallback ?? 'lww-set';\n}\n\n/** The id of a record at the path the config gives its collection, dotted\n * paths included; null when there is no string there. */\nexport function idOf(config: SyncConfig, collection: string, doc: unknown): Id | null {\n\tconst path = config.ids?.[collection] ?? config.idField ?? 'id';\n\tlet v: unknown = doc;\n\tfor (const key of path.split('.')) {\n\t\tif (v === null || typeof v !== 'object') return null;\n\t\tv = (v as Record<string, unknown>)[key];\n\t}\n\treturn typeof v === 'string' ? v : null;\n}\n\n/** The id path this config resolves for a collection (for diagnostics). */\nexport function idPathFor(config: SyncConfig, collection: string): string {\n\treturn config.ids?.[collection] ?? config.idField ?? 'id';\n}\n\n/** Records the engine cannot track because they have no string id at the\n * configured path. Identity is a string id field, so such a record is invisible\n * to stamp/merge - it would silently never replicate. Returns the count per\n * collection (nonzero entries only), for a dev-time warning. A number or missing\n * id is the classic footgun: give each record a string id, or map the field via\n * `sync.ids`. */\nexport function unsyncableCounts(\n\tcollections: Record<string, unknown[]>,\n\tconfig: SyncConfig = {}\n): Record<string, number> {\n\tconst out: Record<string, number> = {};\n\tfor (const [name, docs] of Object.entries(collections)) {\n\t\tlet n = 0;\n\t\tfor (const doc of docs ?? []) if (idOf(config, name, doc) === null) n++;\n\t\tif (n > 0) out[name] = n;\n\t}\n\treturn out;\n}\n\nexport function createMeta(node: string = createNode()): SyncMeta {\n\treturn { node, clock: null, cols: {} };\n}\n\n/**\n * Prune tombstones (delete markers) whose clock is older than `beforeWall` (a\n * wall-time ms cutoff, e.g. `Date.now() - horizon`). Tombstones otherwise\n * accumulate forever, since a delete must be remembered to out-vote a stale\n * replica still holding the record.\n *\n * Safe only when the cutoff is older than the longest a replica can stay offline\n * before it next syncs: a replica that never saw a delete, and whose tombstone\n * has been GC'd everywhere, will resurrect that record on its next merge. Pick a\n * horizon comfortably beyond your worst-case offline window. Returns new meta\n * (input untouched); present-record clocks are untouched.\n */\nexport function gcTombstones(meta: SyncMeta, beforeWall: number): SyncMeta {\n\tconst cols: Record<string, ColMeta> = {};\n\tfor (const [name, col] of Object.entries(meta.cols)) {\n\t\tconst next = cloneCol(col);\n\t\tfor (const [id, clk] of Object.entries(next.deleted)) {\n\t\t\tif (hlcWall(clk) < beforeWall) delete next.deleted[id];\n\t\t}\n\t\tcols[name] = next;\n\t}\n\treturn { node: meta.node, clock: meta.clock, cols };\n}\n\n/**\n * Diff `collections` against the metadata's content hashes, assign a fresh clock\n * to every added or changed record, and tombstone records that disappeared.\n * Returns new metadata (input untouched). Call on every local save.\n */\ntype Tick = () => Hlc;\n\n/** Stamp each present record: a fresh clock for anything added or changed (by\n * content hash). Returns the set of ids seen, for the tombstone pass. */\nfunction stampPresent(\n\tcol: ColMeta,\n\tpresent: unknown[],\n\tconfig: SyncConfig,\n\tname: string,\n\ttick: Tick\n): Set<Id> {\n\tconst fieldLevel = strategyOf(config, name) === 'lww-map';\n\tconst seen = new Set<Id>();\n\tfor (const doc of present) {\n\t\tconst id = idOf(config, name, doc);\n\t\tif (id === null) continue;\n\t\tseen.add(id);\n\t\tconst h = hash(doc);\n\t\tif (col.hashes[id] !== h) {\n\t\t\tcol.clocks[id] = tick();\n\t\t\tcol.hashes[id] = h;\n\t\t\tdelete col.deleted[id];\n\t\t\tif (fieldLevel && isRecord(doc)) stampFields(col, id, doc, tick);\n\t\t}\n\t}\n\treturn seen;\n}\n\n/** Stamp each added/changed field of one record ('lww-map'): a field keeps its\n * clock until its own content changes, so field clocks stay independent and a\n * merge can tell which side last touched which field. */\nfunction stampFields(col: ColMeta, id: Id, doc: Record<string, unknown>, tick: Tick): void {\n\tconst rec = ((col.fields ??= dict())[id] ??= dict());\n\tfor (const key of Object.keys(doc)) {\n\t\tconst h = hash(doc[key]);\n\t\tif (rec[key]?.[1] !== h) rec[key] = [tick(), h];\n\t}\n\tfor (const key of Object.keys(rec)) if (!Object.hasOwn(doc, key)) delete rec[key];\n}\n\n/** Tombstone every previously-tracked id that is no longer present. */\nfunction tombstoneMissing(col: ColMeta, seen: Set<Id>, tick: Tick): void {\n\tfor (const id of Object.keys(col.clocks)) {\n\t\tif (!seen.has(id)) {\n\t\t\tcol.deleted[id] = tick();\n\t\t\tdelete col.clocks[id];\n\t\t\tdelete col.hashes[id];\n\t\t\tdelete col.fields?.[id];\n\t\t}\n\t}\n}\n\nexport function stamp(\n\tmeta: SyncMeta,\n\tcollections: Record<string, unknown[]>,\n\tconfig: SyncConfig,\n\twallNow: number = Date.now()\n): SyncMeta {\n\tlet clock = meta.clock;\n\tconst tick: Tick = () => (clock = issue(clock, meta.node, wallNow));\n\tconst cols: Record<string, ColMeta> = {};\n\tconst names = new Set([...Object.keys(meta.cols), ...Object.keys(collections)]);\n\n\tfor (const name of names) {\n\t\tconst strategy = strategyOf(config, name);\n\t\tconst col = cloneCol(meta.cols[name]);\n\t\tconst present = collections[name] ?? [];\n\n\t\tconst seen = stampPresent(col, present, config, name, tick);\n\t\tif (strategy !== 'grow-set') tombstoneMissing(col, seen, tick);\n\t\tcols[name] = col;\n\t}\n\treturn { node: meta.node, clock, cols };\n}\n\n/** One replica's full state: the collections plus their sync metadata. What\n * `merge` and `detectConflicts` take on each side. */\nexport interface ReplicaState {\n\tcollections: Record<string, unknown[]>;\n\tmeta: SyncMeta;\n}\ntype State = ReplicaState;\n\n/**\n * Merge replica `b` into `a` (commutative for the convergent strategies). The\n * optional `base` lets 'manual' tell a true concurrent conflict from a one-sided\n * change; without it, 'manual' errs toward surfacing.\n */\n/** Every clock a replica carries, flattened: meta, records, tombstones, fields. */\nfunction* allClocks(meta: SyncMeta): Generator<Hlc> {\n\tif (meta.clock) yield meta.clock;\n\tfor (const col of Object.values(meta.cols)) {\n\t\tyield* Object.values(col.clocks);\n\t\tyield* Object.values(col.deleted);\n\t\tfor (const fields of Object.values(col.fields ?? {})) {\n\t\t\tfor (const [clock] of Object.values(fields)) yield clock;\n\t\t}\n\t}\n}\n\n/**\n * Refuse a replica whose clocks cannot be real. `b` is the incoming side by\n * signature, and only it is checked: a local replica that already absorbed a\n * bad clock must still be able to merge, or the fix would strand exactly the\n * stores it exists to protect. Refusing rather than clamping is deliberate -\n * a silently lowered clock would leave two replicas each convinced they hold\n * the later write.\n */\nfunction assertNoDrift(meta: SyncMeta, wallNow: number): void {\n\tfor (const clock of allClocks(meta)) {\n\t\tif (driftedAhead(clock, wallNow)) {\n\t\t\tthrow new SelfstoreError(\n\t\t\t\t'CLOCK_DRIFT',\n\t\t\t\t`This copy carries a clock ${Math.round((hlcWall(clock) - wallNow) / 60_000)} minutes ` +\n\t\t\t\t\t'ahead of this device. Check the clock of the device that wrote it: merging would ' +\n\t\t\t\t\t'let those writes win every future conflict, on every device.'\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function merge(a: State, b: State, config: SyncConfig, base?: SyncMeta): MergeResult {\n\tassertNoDrift(b.meta, Date.now());\n\tconst conflicts: Conflict[] = [];\n\tconst outCols: Record<string, unknown[]> = {};\n\tconst outMeta: SyncMeta = {\n\t\tnode: a.meta.node,\n\t\tclock: max(a.meta.clock, b.meta.clock),\n\t\tcols: {}\n\t};\n\n\tconst names = new Set([\n\t\t...Object.keys(a.collections),\n\t\t...Object.keys(b.collections),\n\t\t...Object.keys(a.meta.cols),\n\t\t...Object.keys(b.meta.cols)\n\t]);\n\n\tfor (const name of names) {\n\t\tconst merged = mergeCollection(name, a, b, config, base, conflicts);\n\t\toutCols[name] = merged.docs;\n\t\toutMeta.cols[name] = merged.col;\n\t}\n\n\treturn { collections: outCols, meta: outMeta, conflicts };\n}\n\n/**\n * Concurrent same-id edits that a convergent strategy resolves silently by keeping\n * the later clock. Informational only: it never changes what `merge` keeps, it\n * reports which records both replicas changed since `base` (the loser's version is\n * the one merge dropped). Empty without a base, since two-way state alone cannot\n * tell a concurrent edit from a one-sided change.\n */\nexport function detectConflicts(\n\ta: State,\n\tb: State,\n\tconfig: SyncConfig,\n\tbase?: SyncMeta\n): Conflict[] {\n\tif (!base) return [];\n\tconst out: Conflict[] = [];\n\tconst names = new Set([...Object.keys(a.collections), ...Object.keys(b.collections)]);\n\tfor (const name of names) {\n\t\tconst strategy = strategyOf(config, name);\n\t\tconst aDocs = byId(config, name, a.collections[name]);\n\t\tconst bDocs = byId(config, name, b.collections[name]);\n\t\tconst aMeta = cloneCol(a.meta.cols[name]);\n\t\tconst bMeta = cloneCol(b.meta.cols[name]);\n\t\tconst baseMeta = baseColOf(base, name);\n\t\tfor (const id of new Set<Id>([...aDocs.keys(), ...bDocs.keys()])) {\n\t\t\tconst aClk = aMeta.clocks[id] ?? null;\n\t\t\tconst bClk = bMeta.clocks[id] ?? null;\n\t\t\tconst winnerSide = pickWinner(aClk, bClk, aDocs.has(id));\n\t\t\t// lww-map merges different-field edits cleanly: only the same field\n\t\t\t// changed on both sides is a real (auto-resolved) conflict.\n\t\t\tconst conflict =\n\t\t\t\tstrategy === 'lww-map'\n\t\t\t\t\t? fieldConflict(\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\taDocs,\n\t\t\t\t\t\t\tbDocs,\n\t\t\t\t\t\t\taMeta.fields?.[id],\n\t\t\t\t\t\t\tbMeta.fields?.[id],\n\t\t\t\t\t\t\tbaseMeta?.fields?.[id],\n\t\t\t\t\t\t\twinnerSide\n\t\t\t\t\t\t)\n\t\t\t\t\t: manualConflict(name, id, aDocs, bDocs, aClk, bClk, baseMeta, winnerSide);\n\t\t\tif (conflict) out.push(conflict);\n\t\t}\n\t}\n\treturn out;\n}\n\n/** The 'lww-map' conflict check: a Conflict only when the same field changed on\n * both sides since base (different-field edits merge without loss). */\nfunction fieldConflict(\n\tname: string,\n\tid: Id,\n\taDocs: Map<Id, unknown>,\n\tbDocs: Map<Id, unknown>,\n\taF: Record<string, [Hlc, number]> | undefined,\n\tbF: Record<string, [Hlc, number]> | undefined,\n\tbaseF: Record<string, [Hlc, number]> | undefined,\n\twinnerSide: 'local' | 'remote'\n): Conflict | null {\n\tif (!(aDocs.has(id) && bDocs.has(id) && aF && bF)) return null;\n\tfor (const key of Object.keys(aF)) {\n\t\tconst a = aF[key];\n\t\tconst b = bF[key];\n\t\tif (!b || a[0] === b[0]) continue;\n\t\tconst baseClk = baseF?.[key]?.[0] ?? null;\n\t\tconst aChanged = !baseClk || compare(a[0], baseClk) > 0;\n\t\tconst bChanged = !baseClk || compare(b[0], baseClk) > 0;\n\t\tif (aChanged && bChanged)\n\t\t\treturn {\n\t\t\t\tcollection: name,\n\t\t\t\tid,\n\t\t\t\tlocal: aDocs.get(id),\n\t\t\t\tremote: bDocs.get(id),\n\t\t\t\tkept: winnerSide\n\t\t\t};\n\t}\n\treturn null;\n}\n\n/** The 'manual' concurrent-edit check for one record: a Conflict when both sides\n * changed the same id since `base` (or, without a base, both simply hold it and\n * their clocks differ). Pure; null when there is nothing to surface. */\nfunction manualConflict(\n\tname: string,\n\tid: Id,\n\taDocs: Map<Id, unknown>,\n\tbDocs: Map<Id, unknown>,\n\taClk: Hlc | null,\n\tbClk: Hlc | null,\n\tbaseMeta: ColMeta | undefined,\n\twinnerSide: 'local' | 'remote'\n): Conflict | null {\n\tif (!(aDocs.has(id) && bDocs.has(id) && aClk && bClk && aClk !== bClk)) return null;\n\tconst baseClk = baseMeta?.clocks[id] ?? null;\n\tconst aChanged = !baseClk || compare(aClk, baseClk) > 0;\n\tconst bChanged = !baseClk || compare(bClk, baseClk) > 0;\n\tif (!(aChanged && bChanged)) return null;\n\treturn { collection: name, id, local: aDocs.get(id), remote: bDocs.get(id), kept: winnerSide };\n}\n\n/** Reconcile every id of one collection across the two replicas. */\nfunction mergeCollection(\n\tname: string,\n\ta: State,\n\tb: State,\n\tconfig: SyncConfig,\n\tbase: SyncMeta | undefined,\n\tconflicts: Conflict[]\n): { docs: unknown[]; col: ColMeta } {\n\tconst strategy = strategyOf(config, name);\n\tconst aDocs = byId(config, name, a.collections[name]);\n\tconst bDocs = byId(config, name, b.collections[name]);\n\tconst aMeta = cloneCol(a.meta.cols[name]);\n\tconst bMeta = cloneCol(b.meta.cols[name]);\n\tconst baseMeta = baseColOf(base, name);\n\tconst col = emptyCol();\n\tconst docs: unknown[] = [];\n\n\tconst ids = new Set<Id>([\n\t\t...Object.keys(aMeta.clocks),\n\t\t...Object.keys(bMeta.clocks),\n\t\t...Object.keys(aMeta.deleted),\n\t\t...Object.keys(bMeta.deleted),\n\t\t...aDocs.keys(),\n\t\t...bDocs.keys()\n\t]);\n\n\tfor (const id of ids) {\n\t\tconst aClk = aMeta.clocks[id] ?? null;\n\t\tconst bClk = bMeta.clocks[id] ?? null;\n\t\tconst aliveClk = max(aClk, bClk);\n\t\tconst deadClk = max(aMeta.deleted[id] ?? null, bMeta.deleted[id] ?? null);\n\n\t\tconst deleteWins =\n\t\t\tstrategy !== 'grow-set' && deadClk && (!aliveClk || compare(deadClk, aliveClk) > 0);\n\n\t\tif (deleteWins) {\n\t\t\tcol.deleted[id] = deadClk as Hlc;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!aliveClk) {\n\t\t\tif (deadClk) col.deleted[id] = deadClk;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst aHas = aDocs.has(id);\n\t\tconst winnerSide = pickWinner(aClk, bClk, aHas);\n\t\tlet doc = winnerSide === 'local' ? aDocs.get(id) : bDocs.get(id);\n\n\t\tif (strategy === 'manual') {\n\t\t\tconst conflict = manualConflict(name, id, aDocs, bDocs, aClk, bClk, baseMeta, winnerSide);\n\t\t\tif (conflict) conflicts.push(conflict);\n\t\t}\n\n\t\tif (strategy === 'lww-map') {\n\t\t\tconst merged = mergeFields(id, aDocs.get(id), bDocs.get(id), aMeta, bMeta, winnerSide);\n\t\t\tif (merged) {\n\t\t\t\tdoc = merged.doc;\n\t\t\t\t(col.fields ??= dict())[id] = merged.fields;\n\t\t\t} else {\n\t\t\t\t// One side has no document: the record-level winner stands; carry its\n\t\t\t\t// field metadata forward so later merges stay field-aware.\n\t\t\t\tconst f = (winnerSide === 'local' ? aMeta : bMeta).fields?.[id];\n\t\t\t\tif (f) (col.fields ??= dict())[id] = dict(f);\n\t\t\t}\n\t\t}\n\n\t\tif (doc !== undefined) {\n\t\t\tdocs.push(doc);\n\t\t\tcol.clocks[id] = aliveClk;\n\t\t\tcol.hashes[id] = hash(doc);\n\t\t}\n\t}\n\n\treturn { docs, col };\n}\n\n/** Field-by-field merge of one record ('lww-map'): each field goes to the side\n * whose field clock is later; a field only one side stamped keeps that side's\n * value; unstamped fields (written under a record-level strategy) follow the\n * record-level winner.\n * Null when either document is missing (the record-level result stands). */\nfunction mergeFields(\n\tid: Id,\n\taDoc: unknown,\n\tbDoc: unknown,\n\taMeta: ColMeta,\n\tbMeta: ColMeta,\n\twinnerSide: 'local' | 'remote'\n): { doc: Record<string, unknown>; fields: Record<string, [Hlc, number]> } | null {\n\tif (!isRecord(aDoc) || !isRecord(bDoc)) return null;\n\tconst aF = aMeta.fields?.[id] ?? dict();\n\tconst bF = bMeta.fields?.[id] ?? dict();\n\tconst doc: Record<string, unknown> = {};\n\tconst fields = dict<[Hlc, number]>();\n\tfor (const key of new Set([...Object.keys(aDoc), ...Object.keys(bDoc)])) {\n\t\tconst a = aF[key] ?? null;\n\t\tconst b = bF[key] ?? null;\n\t\tconst side = pickFieldSide(a, b, winnerSide);\n\t\tconst src = side === 'local' ? aDoc : bDoc;\n\t\tif (!Object.hasOwn(src, key)) continue; // the winning side removed the field: drop it\n\t\t// Defined, never assigned: `doc.__proto__ = v` would swap the prototype.\n\t\tObject.defineProperty(doc, key, {\n\t\t\tvalue: src[key],\n\t\t\tenumerable: true,\n\t\t\twritable: true,\n\t\t\tconfigurable: true\n\t\t});\n\t\tconst stamped = side === 'local' ? a : b;\n\t\tif (stamped) fields[key] = stamped;\n\t}\n\treturn { doc, fields };\n}\n\n/** Which side supplies one field of an 'lww-map' record: the later field clock\n * when both sides stamped it, the stamping side when only one did, else the\n * record-level winner (unstamped fields follow the record). */\nfunction pickFieldSide(\n\ta: [Hlc, number] | null,\n\tb: [Hlc, number] | null,\n\twinnerSide: 'local' | 'remote'\n): 'local' | 'remote' {\n\tif (a && b) return compare(a[0], b[0]) >= 0 ? 'local' : 'remote';\n\tif (a) return 'local';\n\tif (b) return 'remote';\n\treturn winnerSide;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** Which side's value to keep for a record. With both clocks present, the later\n * clock wins (ties to local); otherwise the side that actually holds the doc. */\nfunction pickWinner(aClk: Hlc | null, bClk: Hlc | null, aHas: boolean): 'local' | 'remote' {\n\tif (aClk && bClk) return compare(aClk, bClk) >= 0 ? 'local' : 'remote';\n\treturn aHas ? 'local' : 'remote';\n}\n\nfunction byId(\n\tconfig: SyncConfig,\n\tcollection: string,\n\tdocs: unknown[] | undefined\n): Map<Id, unknown> {\n\tconst m = new Map<Id, unknown>();\n\tfor (const d of docs ?? []) {\n\t\tconst id = idOf(config, collection, d);\n\t\tif (id !== null) m.set(id, d);\n\t}\n\treturn m;\n}\n\n/** What a merge did to one collection, counted by record. */\nexport interface CollectionChanges {\n\tadded: number;\n\tupdated: number;\n\tremoved: number;\n}\n\n/** Per-collection record counts of what changed between two states (same id\n * resolution as the merge). Collections with no change are omitted, so an empty\n * result means the states are equivalent. Feeds the sync journal. */\nexport function changes(\n\tbefore: Record<string, unknown[]>,\n\tafter: Record<string, unknown[]>,\n\tconfig: SyncConfig\n): Record<string, CollectionChanges> {\n\tconst out: Record<string, CollectionChanges> = {};\n\tfor (const name of new Set([...Object.keys(before), ...Object.keys(after)])) {\n\t\tconst b = byId(config, name, before[name]);\n\t\tconst a = byId(config, name, after[name]);\n\t\tlet added = 0;\n\t\tlet updated = 0;\n\t\tlet removed = 0;\n\t\tfor (const [id, doc] of a) {\n\t\t\tif (!b.has(id)) added++;\n\t\t\telse if (hash(doc) !== hash(b.get(id))) updated++;\n\t\t}\n\t\tfor (const id of b.keys()) if (!a.has(id)) removed++;\n\t\tif (added || updated || removed) out[name] = { added, updated, removed };\n\t}\n\treturn out;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createNode, createMeta, gcTombstones, stamp, merge, unsyncableCounts, idPathFor, detectConflicts, changes } from './chunk-
|
|
1
|
+
import { createNode, createMeta, gcTombstones, stamp, merge, unsyncableCounts, idPathFor, detectConflicts, changes } from './chunk-3P2W3XQD.js';
|
|
2
2
|
import { inspect, checkPasswordPolicy, writeBox, BACKUP_MIME, readBoxWithSync, asBytes } from './chunk-E4S77JSN.js';
|
|
3
3
|
import { seal, isEnvelope, unseal, newDeviceKey } from './chunk-QBCU7OOP.js';
|
|
4
4
|
import { openManifest, GROUP_KEYING } from './chunk-F4ZZEJ5H.js';
|
|
@@ -290,22 +290,25 @@ function createLocalStore(opts) {
|
|
|
290
290
|
if (p === null) envelope = null;
|
|
291
291
|
armIdleLock();
|
|
292
292
|
}
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
293
|
+
const blobOf = /* @__PURE__ */ new WeakMap();
|
|
294
|
+
const blobFor = (f) => {
|
|
295
|
+
let blob = blobOf.get(f.bytes);
|
|
296
|
+
if (!blob) {
|
|
297
|
+
blob = new Blob([f.bytes], { type: f.mime });
|
|
298
|
+
blobOf.set(f.bytes, blob);
|
|
299
|
+
}
|
|
300
|
+
return blob;
|
|
301
|
+
};
|
|
302
|
+
const toCachedFiles = (snap) => snap.files.map((f) => ({ id: f.id, name: f.name, mime: f.mime, blob: blobFor(f) }));
|
|
299
303
|
async function cachedToSnapshot(local) {
|
|
300
304
|
return {
|
|
301
305
|
collections: local.collections,
|
|
302
306
|
files: await Promise.all(
|
|
303
|
-
local.files.map(async (f) =>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
mime: f.mime,
|
|
307
|
-
|
|
308
|
-
}))
|
|
307
|
+
local.files.map(async (f) => {
|
|
308
|
+
const bytes = new Uint8Array(await f.blob.arrayBuffer());
|
|
309
|
+
blobOf.set(bytes, f.blob);
|
|
310
|
+
return { id: f.id, name: f.name, mime: f.mime, bytes };
|
|
311
|
+
})
|
|
309
312
|
)
|
|
310
313
|
};
|
|
311
314
|
}
|
|
@@ -1969,6 +1972,8 @@ function indexedDbCache(name, opts) {
|
|
|
1969
1972
|
await (await db()).delete("kv", `m:${key2}`);
|
|
1970
1973
|
}
|
|
1971
1974
|
};
|
|
1975
|
+
const written = /* @__PURE__ */ new Map();
|
|
1976
|
+
const changed = (f, have) => !have.has(f.id) || written.has(f.id) && written.get(f.id) !== f.blob;
|
|
1972
1977
|
const base = {
|
|
1973
1978
|
kv,
|
|
1974
1979
|
async load() {
|
|
@@ -1987,7 +1992,10 @@ function indexedDbCache(name, opts) {
|
|
|
1987
1992
|
const files = [];
|
|
1988
1993
|
for (const fileKey of await d.getAllKeys("files")) {
|
|
1989
1994
|
const v = await d.get("files", fileKey);
|
|
1990
|
-
if (v)
|
|
1995
|
+
if (!v) continue;
|
|
1996
|
+
const file = { id: String(fileKey), ...await decodeFile(v) };
|
|
1997
|
+
written.set(file.id, file.blob);
|
|
1998
|
+
files.push(file);
|
|
1991
1999
|
}
|
|
1992
2000
|
return { collections, files };
|
|
1993
2001
|
},
|
|
@@ -2002,7 +2010,7 @@ function indexedDbCache(name, opts) {
|
|
|
2002
2010
|
const want = new Set(files.map((f) => f.id));
|
|
2003
2011
|
const k = await key();
|
|
2004
2012
|
const fresh = await Promise.all(
|
|
2005
|
-
files.filter((f) =>
|
|
2013
|
+
files.filter((f) => changed(f, have)).map(async (f) => {
|
|
2006
2014
|
const bytes = new Uint8Array(await f.blob.arrayBuffer());
|
|
2007
2015
|
return { id: f.id, rec: await sealFile(k, { name: f.name, mime: f.mime }, bytes) };
|
|
2008
2016
|
})
|
|
@@ -2011,11 +2019,14 @@ function indexedDbCache(name, opts) {
|
|
|
2011
2019
|
for (const { id, rec } of fresh) tx.store.put(rec, id);
|
|
2012
2020
|
for (const id of have) if (!want.has(id)) tx.store.delete(id);
|
|
2013
2021
|
await tx.done;
|
|
2022
|
+
written.clear();
|
|
2023
|
+
for (const f of files) written.set(f.id, f.blob);
|
|
2014
2024
|
},
|
|
2015
2025
|
async clear() {
|
|
2016
2026
|
const d = await db();
|
|
2017
2027
|
await d.clear("kv");
|
|
2018
2028
|
await d.clear("files");
|
|
2029
|
+
written.clear();
|
|
2019
2030
|
_key = null;
|
|
2020
2031
|
sealKey = null;
|
|
2021
2032
|
},
|
|
@@ -2225,5 +2236,5 @@ function gisDriveAuth(opts) {
|
|
|
2225
2236
|
}
|
|
2226
2237
|
|
|
2227
2238
|
export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache, preloadGoogleIdentity };
|
|
2228
|
-
//# sourceMappingURL=chunk-
|
|
2229
|
-
//# sourceMappingURL=chunk-
|
|
2239
|
+
//# sourceMappingURL=chunk-SHX36MIE.js.map
|
|
2240
|
+
//# sourceMappingURL=chunk-SHX36MIE.js.map
|