@gonvex/client 0.1.31 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -95
- package/dist/control.d.ts +416 -0
- package/dist/control.js +210 -0
- package/dist/control.js.map +1 -0
- package/dist/error-reporter.d.ts +20 -6
- package/dist/error-reporter.js +55 -27
- package/dist/error-reporter.js.map +1 -1
- package/dist/index.d.ts +170 -132
- package/dist/index.js +1123 -1138
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-replica.d.ts +20 -0
- package/dist/indexeddb-replica.js +193 -0
- package/dist/indexeddb-replica.js.map +1 -0
- package/dist/kv-stores.d.ts +15 -0
- package/dist/kv-stores.js +60 -0
- package/dist/kv-stores.js.map +1 -0
- package/dist/local-replica.d.ts +240 -0
- package/dist/local-replica.js +590 -0
- package/dist/local-replica.js.map +1 -0
- package/dist/optimistic.d.ts +34 -55
- package/dist/optimistic.js +70 -269
- package/dist/optimistic.js.map +1 -1
- package/dist/outbox.d.ts +74 -16
- package/dist/outbox.js +194 -14
- package/dist/outbox.js.map +1 -1
- package/dist/query-expression.d.ts +58 -0
- package/dist/query-expression.js +164 -0
- package/dist/query-expression.js.map +1 -0
- package/dist/replica-integrity.d.ts +5 -0
- package/dist/replica-integrity.js +58 -0
- package/dist/replica-integrity.js.map +1 -0
- package/package.json +4 -4
- package/dist/browser-cache-client.d.ts +0 -77
- package/dist/browser-cache-client.js +0 -156
- package/dist/browser-cache-client.js.map +0 -1
- package/dist/browser-cache-shared-worker.d.ts +0 -35
- package/dist/browser-cache-shared-worker.js +0 -118
- package/dist/browser-cache-shared-worker.js.map +0 -1
- package/dist/browser-cache.d.ts +0 -43
- package/dist/browser-cache.js +0 -67
- package/dist/browser-cache.js.map +0 -1
- package/dist/browser-capabilities.d.ts +0 -21
- package/dist/browser-capabilities.js +0 -31
- package/dist/browser-capabilities.js.map +0 -1
- package/dist/cache-coordinator.d.ts +0 -37
- package/dist/cache-coordinator.js +0 -109
- package/dist/cache-coordinator.js.map +0 -1
- package/dist/cache.d.ts +0 -74
- package/dist/cache.js +0 -120
- package/dist/cache.js.map +0 -1
- package/dist/persistent-cache.d.ts +0 -41
- package/dist/persistent-cache.js +0 -103
- package/dist/persistent-cache.js.map +0 -1
- package/dist/query-cache.d.ts +0 -88
- package/dist/query-cache.js +0 -346
- package/dist/query-cache.js.map +0 -1
- package/dist/sync-store.d.ts +0 -96
- package/dist/sync-store.js +0 -500
- package/dist/sync-store.js.map +0 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Dexie } from "dexie";
|
|
2
|
+
const defaultReplicaScope = "default";
|
|
3
|
+
const replicaSchemaVersion = 3;
|
|
4
|
+
/** Atomic normalized web persistence for the Gonvex Local Replica. */
|
|
5
|
+
export class IndexedDBLocalReplicaStorage {
|
|
6
|
+
database;
|
|
7
|
+
initialized;
|
|
8
|
+
constructor(name = "gonvex-local-replica") {
|
|
9
|
+
this.database = new Dexie(name);
|
|
10
|
+
this.database.version(1).stores({ snapshots: "&key" });
|
|
11
|
+
this.database.version(2).stores({ snapshots: "&scope" }).upgrade(async (transaction) => {
|
|
12
|
+
await transaction.table("snapshots").toCollection().modify((record) => {
|
|
13
|
+
record.scope = defaultReplicaScope;
|
|
14
|
+
delete record.key;
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
this.database.version(replicaSchemaVersion).stores({
|
|
18
|
+
entities: "[scope+entity+id], scope, [scope+entity]",
|
|
19
|
+
windows: "[scope+signature], scope",
|
|
20
|
+
meta: "[scope+key], scope",
|
|
21
|
+
// Retain the old table only as a one-time upgrade source. It is never
|
|
22
|
+
// read after initialization and is removed from the active schema.
|
|
23
|
+
snapshots: "&scope",
|
|
24
|
+
}).upgrade(async (transaction) => {
|
|
25
|
+
const snapshots = await transaction.table("snapshots").toArray();
|
|
26
|
+
const entities = transaction.table("entities");
|
|
27
|
+
const windows = transaction.table("windows");
|
|
28
|
+
const meta = transaction.table("meta");
|
|
29
|
+
for (const record of snapshots) {
|
|
30
|
+
const scope = record.scope ?? defaultReplicaScope;
|
|
31
|
+
const snapshot = record.snapshot;
|
|
32
|
+
for (const [entity, rows] of Object.entries(snapshot.entities ?? {})) {
|
|
33
|
+
for (const [id, value] of Object.entries(rows)) {
|
|
34
|
+
await entities.put({ scope, entity, id, value: JSON.stringify(value) });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const [signature, window] of Object.entries(snapshot.liveQueries ?? {})) {
|
|
38
|
+
await windows.put({ scope, signature, value: JSON.stringify(window) });
|
|
39
|
+
}
|
|
40
|
+
if (snapshot.cursor) {
|
|
41
|
+
await meta.put({ scope, key: "cursor", value: JSON.stringify(snapshot.cursor) });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// The upgrade transaction is atomic: only remove the legacy full
|
|
45
|
+
// snapshots after every entity/window/cursor has been copied into the
|
|
46
|
+
// normalized stores. Keeping the source rows would retain a second,
|
|
47
|
+
// row-bearing server-state store indefinitely.
|
|
48
|
+
await transaction.table("snapshots").clear();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
initialize() {
|
|
52
|
+
return this.initialized ??= this.database.open().then(() => undefined);
|
|
53
|
+
}
|
|
54
|
+
async load(scope = defaultReplicaScope) {
|
|
55
|
+
await this.initialize();
|
|
56
|
+
const normalizedScope = normalizeScope(scope);
|
|
57
|
+
const [entityRecords, windowRecords, cursor] = await Promise.all([
|
|
58
|
+
this.database.entities.where("scope").equals(normalizedScope).toArray(),
|
|
59
|
+
this.database.windows.where("scope").equals(normalizedScope).toArray(),
|
|
60
|
+
this.database.meta.get([normalizedScope, "cursor"]),
|
|
61
|
+
]);
|
|
62
|
+
if (entityRecords.length === 0 && windowRecords.length === 0 && !cursor)
|
|
63
|
+
return undefined;
|
|
64
|
+
const entities = {};
|
|
65
|
+
for (const record of entityRecords)
|
|
66
|
+
(entities[record.entity] ??= {})[record.id] = JSON.parse(record.value);
|
|
67
|
+
const liveQueries = {};
|
|
68
|
+
for (const record of windowRecords)
|
|
69
|
+
liveQueries[record.signature] = JSON.parse(record.value);
|
|
70
|
+
return {
|
|
71
|
+
cursor: cursor ? JSON.parse(cursor.value) : undefined,
|
|
72
|
+
entities,
|
|
73
|
+
liveQueries,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async applyTransaction(transaction, _snapshot, scope = defaultReplicaScope) {
|
|
77
|
+
await this.initialize();
|
|
78
|
+
const normalizedScope = normalizeScope(scope);
|
|
79
|
+
await this.database.transaction("rw", this.database.entities, this.database.windows, this.database.meta, async () => {
|
|
80
|
+
const previous = await this.database.meta.get([normalizedScope, "cursor"]);
|
|
81
|
+
if (previous && JSON.parse(previous.value).epoch !== transaction.cursor.epoch) {
|
|
82
|
+
await this.database.entities.where("scope").equals(normalizedScope).delete();
|
|
83
|
+
await this.database.windows.where("scope").equals(normalizedScope).delete();
|
|
84
|
+
}
|
|
85
|
+
for (const change of transaction.changes) {
|
|
86
|
+
if (change.operation === "delete") {
|
|
87
|
+
await this.database.entities.delete([normalizedScope, change.entity, change.id]);
|
|
88
|
+
const windows = await this.database.windows.where("scope").equals(normalizedScope).toArray();
|
|
89
|
+
for (const record of windows) {
|
|
90
|
+
const window = JSON.parse(record.value);
|
|
91
|
+
if (!window.ids.includes(change.id))
|
|
92
|
+
continue;
|
|
93
|
+
window.ids = window.ids.filter((id) => id !== change.id);
|
|
94
|
+
await this.database.windows.put({ ...record, value: JSON.stringify(window) });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (change.newValue) {
|
|
98
|
+
await this.database.entities.put({
|
|
99
|
+
scope: normalizedScope,
|
|
100
|
+
entity: change.entity,
|
|
101
|
+
id: change.id,
|
|
102
|
+
value: JSON.stringify(change.newValue),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const membership of transaction.memberships ?? []) {
|
|
107
|
+
await this.database.windows.put({
|
|
108
|
+
scope: normalizedScope,
|
|
109
|
+
signature: membership.signature,
|
|
110
|
+
value: JSON.stringify(normalizeWindow(membership)),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
await this.database.meta.put({ scope: normalizedScope, key: "cursor", value: JSON.stringify(transaction.cursor) });
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async replaceWindow(window, snapshot, scope = defaultReplicaScope) {
|
|
117
|
+
await this.initialize();
|
|
118
|
+
const normalizedScope = normalizeScope(scope);
|
|
119
|
+
await this.database.transaction("rw", this.database.entities, this.database.windows, this.database.meta, async () => {
|
|
120
|
+
for (const [entity, rows] of Object.entries(snapshot.entities)) {
|
|
121
|
+
for (const [id, value] of Object.entries(rows)) {
|
|
122
|
+
await this.database.entities.put({ scope: normalizedScope, entity, id, value: JSON.stringify(value) });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
await this.database.windows.put({ scope: normalizedScope, signature: window.signature, value: JSON.stringify(normalizeWindow(window)) });
|
|
126
|
+
if (snapshot.cursor)
|
|
127
|
+
await this.database.meta.put({ scope: normalizedScope, key: "cursor", value: JSON.stringify(snapshot.cursor) });
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async applyWindowDelta(window, _delta, snapshot, scope = defaultReplicaScope) {
|
|
131
|
+
// The in-memory replica has already computed the normalized result. The
|
|
132
|
+
// storage transaction receives that result and persists it atomically;
|
|
133
|
+
// retaining unrelated entities is deliberate and conservative.
|
|
134
|
+
await this.replaceWindow(window, snapshot, scope);
|
|
135
|
+
}
|
|
136
|
+
async removeWindow(signature, snapshot, scope = defaultReplicaScope) {
|
|
137
|
+
await this.initialize();
|
|
138
|
+
const normalizedScope = normalizeScope(scope);
|
|
139
|
+
await this.database.transaction("rw", this.database.windows, this.database.meta, async () => {
|
|
140
|
+
await this.database.windows.delete([normalizedScope, signature]);
|
|
141
|
+
if (snapshot.cursor)
|
|
142
|
+
await this.database.meta.put({ scope: normalizedScope, key: "cursor", value: JSON.stringify(snapshot.cursor) });
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async replaceSnapshot(snapshot, scope = defaultReplicaScope) {
|
|
146
|
+
await this.initialize();
|
|
147
|
+
const normalizedScope = normalizeScope(scope);
|
|
148
|
+
await this.database.transaction("rw", this.database.entities, this.database.windows, this.database.meta, async () => {
|
|
149
|
+
await this.database.entities.where("scope").equals(normalizedScope).delete();
|
|
150
|
+
await this.database.windows.where("scope").equals(normalizedScope).delete();
|
|
151
|
+
await this.database.meta.delete([normalizedScope, "cursor"]);
|
|
152
|
+
for (const [entity, rows] of Object.entries(snapshot.entities)) {
|
|
153
|
+
for (const [id, value] of Object.entries(rows)) {
|
|
154
|
+
await this.database.entities.put({ scope: normalizedScope, entity, id, value: JSON.stringify(value) });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const [signature, window] of Object.entries(snapshot.liveQueries)) {
|
|
158
|
+
await this.database.windows.put({ scope: normalizedScope, signature, value: JSON.stringify(normalizeWindow(window)) });
|
|
159
|
+
}
|
|
160
|
+
if (snapshot.cursor)
|
|
161
|
+
await this.database.meta.put({ scope: normalizedScope, key: "cursor", value: JSON.stringify(snapshot.cursor) });
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async clear(scope = defaultReplicaScope) {
|
|
165
|
+
await this.initialize();
|
|
166
|
+
const normalizedScope = normalizeScope(scope);
|
|
167
|
+
await this.database.transaction("rw", this.database.entities, this.database.windows, this.database.meta, async () => {
|
|
168
|
+
await this.database.entities.where("scope").equals(normalizedScope).delete();
|
|
169
|
+
await this.database.windows.where("scope").equals(normalizedScope).delete();
|
|
170
|
+
await this.database.meta.where("scope").equals(normalizedScope).delete();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
close() {
|
|
174
|
+
this.database.close();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export function indexedDBLocalReplica(name) {
|
|
178
|
+
return new IndexedDBLocalReplicaStorage(name);
|
|
179
|
+
}
|
|
180
|
+
function normalizeScope(scope) {
|
|
181
|
+
return typeof scope === "string" && scope.trim() ? scope : defaultReplicaScope;
|
|
182
|
+
}
|
|
183
|
+
function normalizeWindow(value) {
|
|
184
|
+
return {
|
|
185
|
+
...value,
|
|
186
|
+
kind: value.kind ?? "live",
|
|
187
|
+
key: value.key ?? "id",
|
|
188
|
+
ids: [...value.ids],
|
|
189
|
+
resultPath: value.resultPath ? [...value.resultPath] : undefined,
|
|
190
|
+
hashes: value.hashes ? { ...value.hashes } : undefined,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=indexeddb-replica.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb-replica.js","sourceRoot":"","sources":["../src/indexeddb-replica.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OutboxStore } from "./outbox.js";
|
|
2
|
+
/** Minimal host persistence boundary used by the reducer outbox and adapters. */
|
|
3
|
+
export type GonvexKv = {
|
|
4
|
+
get(table: string, key: string): Promise<string | undefined>;
|
|
5
|
+
set(table: string, key: string, value: string): Promise<void>;
|
|
6
|
+
delete(table: string, key: string): Promise<void>;
|
|
7
|
+
list(table: string): Promise<Array<{
|
|
8
|
+
key: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}>>;
|
|
11
|
+
clear(table: string, keyPrefix?: string): Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
export declare const kvReducerOutboxTable = "reducer_outbox";
|
|
14
|
+
export declare function createMemoryGonvexKv(): GonvexKv;
|
|
15
|
+
export declare function createKvOutboxStore(kv: GonvexKv): OutboxStore;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const kvReducerOutboxTable = "reducer_outbox";
|
|
2
|
+
export function createMemoryGonvexKv() {
|
|
3
|
+
const tables = new Map();
|
|
4
|
+
const table = (name) => {
|
|
5
|
+
let rows = tables.get(name);
|
|
6
|
+
if (!rows) {
|
|
7
|
+
rows = new Map();
|
|
8
|
+
tables.set(name, rows);
|
|
9
|
+
}
|
|
10
|
+
return rows;
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
async get(name, key) { return table(name).get(key); },
|
|
14
|
+
async set(name, key, value) { table(name).set(key, value); },
|
|
15
|
+
async delete(name, key) { table(name).delete(key); },
|
|
16
|
+
async list(name) { return [...table(name)].map(([key, value]) => ({ key, value })); },
|
|
17
|
+
async clear(name, prefix) {
|
|
18
|
+
const rows = table(name);
|
|
19
|
+
if (!prefix) {
|
|
20
|
+
rows.clear();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const key of rows.keys())
|
|
24
|
+
if (key.startsWith(prefix))
|
|
25
|
+
rows.delete(key);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function createKvOutboxStore(kv) {
|
|
30
|
+
return {
|
|
31
|
+
async load() {
|
|
32
|
+
const entries = [];
|
|
33
|
+
for (const { value } of await kv.list(kvReducerOutboxTable)) {
|
|
34
|
+
try {
|
|
35
|
+
const entry = JSON.parse(value);
|
|
36
|
+
if (entry && typeof entry.id === "number")
|
|
37
|
+
entries.push(entry);
|
|
38
|
+
}
|
|
39
|
+
catch { /* ignore corrupt disposable rows */ }
|
|
40
|
+
}
|
|
41
|
+
return entries.sort((left, right) => left.id - right.id);
|
|
42
|
+
},
|
|
43
|
+
async put(entry) { await kv.set(kvReducerOutboxTable, String(entry.id), JSON.stringify(entry)); },
|
|
44
|
+
async delete(id) { await kv.delete(kvReducerOutboxTable, String(id)); },
|
|
45
|
+
async clear(scope) {
|
|
46
|
+
if (scope === undefined) {
|
|
47
|
+
await kv.clear(kvReducerOutboxTable);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
for (const { key, value } of await kv.list(kvReducerOutboxTable)) {
|
|
51
|
+
try {
|
|
52
|
+
if (JSON.parse(value)?.scope === scope)
|
|
53
|
+
await kv.delete(kvReducerOutboxTable, key);
|
|
54
|
+
}
|
|
55
|
+
catch { /* ignore */ }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=kv-stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stores.js","sourceRoot":"","sources":["../src/kv-stores.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAErD,MAAM,UAAU,oBAAoB;IAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;IACtD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACrD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpD,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YACtC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE;gBAAE,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;oBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9E,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,EAAY;IAC9C,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,GAAyB,EAAE,CAAC;YACzC,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,MAAM,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAuB,CAAC;oBACtD,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;wBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACjE,CAAC;gBAAC,MAAM,CAAC,CAAC,oCAAoC,CAAC,CAAC;YAClD,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACjG,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,KAAK,CAAC,KAAK,CAAC,KAAK;YACf,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAAC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YAC1E,KAAK,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,MAAM,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC;gBACjE,IAAI,CAAC;oBAAC,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAwB,EAAE,KAAK,KAAK,KAAK;wBAAE,MAAM,EAAE,CAAC,MAAM,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YAC5I,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { JsonValue, ReplicaCursor, SubscriptionRevision } from "@gonvex/protocol";
|
|
2
|
+
import type { OptimisticPatch } from "./optimistic.js";
|
|
3
|
+
export type ReplicaRow = Record<string, JsonValue>;
|
|
4
|
+
export type ReplicaChange = {
|
|
5
|
+
entity: string;
|
|
6
|
+
id: string;
|
|
7
|
+
operation: "insert" | "update" | "delete";
|
|
8
|
+
oldValue?: ReplicaRow;
|
|
9
|
+
newValue?: ReplicaRow;
|
|
10
|
+
changedColumns?: string[];
|
|
11
|
+
};
|
|
12
|
+
export type ReplicaWindow = {
|
|
13
|
+
signature: string;
|
|
14
|
+
kind: "live" | "replica";
|
|
15
|
+
entity: string;
|
|
16
|
+
key: string;
|
|
17
|
+
ids: string[];
|
|
18
|
+
cursor?: ReplicaCursor;
|
|
19
|
+
completeness: "complete" | "partial";
|
|
20
|
+
source: "server" | "cache";
|
|
21
|
+
resultSkeleton?: JsonValue;
|
|
22
|
+
resultPath?: string[];
|
|
23
|
+
scalar?: JsonValue;
|
|
24
|
+
windowRevision?: string;
|
|
25
|
+
subscriptionRevision?: SubscriptionRevision;
|
|
26
|
+
mode?: "eager" | "progressive";
|
|
27
|
+
truncated?: boolean;
|
|
28
|
+
orderBy?: string;
|
|
29
|
+
orderDirection?: "asc" | "desc";
|
|
30
|
+
maxRows?: number;
|
|
31
|
+
maxBytes?: number;
|
|
32
|
+
hashes?: Record<string, string>;
|
|
33
|
+
};
|
|
34
|
+
export type ReplicaTransaction = {
|
|
35
|
+
cursor: ReplicaCursor;
|
|
36
|
+
originCommandId?: string;
|
|
37
|
+
changes: ReplicaChange[];
|
|
38
|
+
memberships?: ReplicaWindow[];
|
|
39
|
+
};
|
|
40
|
+
export type ReplicaSnapshot = {
|
|
41
|
+
cursor?: ReplicaCursor;
|
|
42
|
+
entities: Record<string, Record<string, ReplicaRow>>;
|
|
43
|
+
liveQueries: Record<string, ReplicaWindow>;
|
|
44
|
+
};
|
|
45
|
+
/** Opaque persistence namespace for one deployment/project/tenant identity. */
|
|
46
|
+
export type ReplicaScope = string;
|
|
47
|
+
/**
|
|
48
|
+
* Storage implementations must persist the complete transaction atomically.
|
|
49
|
+
* The SQLite adapter maps this call to BEGIN/apply/cursor/COMMIT; IndexedDB
|
|
50
|
+
* implementations use one readwrite transaction over the same stores.
|
|
51
|
+
*/
|
|
52
|
+
export interface LocalReplicaStorage {
|
|
53
|
+
load(scope?: ReplicaScope): Promise<ReplicaSnapshot | undefined>;
|
|
54
|
+
applyTransaction(transaction: ReplicaTransaction, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
55
|
+
/** Persist a normalized Query/Collection materialization atomically. */
|
|
56
|
+
replaceSnapshot?(snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
57
|
+
replaceWindow?(window: ReplicaWindow, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
58
|
+
applyWindowDelta?(window: ReplicaWindow, delta: {
|
|
59
|
+
upserts: ReplicaRow[];
|
|
60
|
+
deleted: string[];
|
|
61
|
+
}, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
62
|
+
removeWindow?(signature: string, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
63
|
+
clear?(scope?: ReplicaScope): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export type ReplicaFreshness = "current" | "verifying" | "offline";
|
|
66
|
+
export type LiveQueryResult<T extends ReplicaRow = ReplicaRow> = {
|
|
67
|
+
rows: T[];
|
|
68
|
+
/** Ordered normalized entity IDs owned by the retained query window. */
|
|
69
|
+
ids: string[];
|
|
70
|
+
total?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
limit?: number;
|
|
73
|
+
source: "server" | "cache";
|
|
74
|
+
completeness: "complete" | "partial";
|
|
75
|
+
freshness: ReplicaFreshness;
|
|
76
|
+
supported?: boolean;
|
|
77
|
+
unsupportedOperator?: string;
|
|
78
|
+
};
|
|
79
|
+
export type ReplicaCollectionState<T extends ReplicaRow = ReplicaRow> = LiveQueryResult<T> & {
|
|
80
|
+
truncated: boolean;
|
|
81
|
+
computedRevision: number;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Read-only view of the normalized Local Replica exposed to application code.
|
|
85
|
+
*
|
|
86
|
+
* Mutations are deliberately absent from this interface. The Gonvex client
|
|
87
|
+
* applies committed transactions, optimistic reducer effects, scope changes,
|
|
88
|
+
* and cache/window updates internally so application code cannot create a
|
|
89
|
+
* second state-management path or advance the local replica by hand.
|
|
90
|
+
*/
|
|
91
|
+
export interface LocalReplicaView {
|
|
92
|
+
cursor(): ReplicaCursor | undefined;
|
|
93
|
+
freshness(): ReplicaFreshness;
|
|
94
|
+
version(): number;
|
|
95
|
+
subscribe(listener: () => void): () => void;
|
|
96
|
+
hasPendingCommand(commandId: string): boolean;
|
|
97
|
+
getWindow(signature: string): ReplicaWindow | undefined;
|
|
98
|
+
listWindows(): ReplicaWindow[];
|
|
99
|
+
windowRows<T extends ReplicaRow = ReplicaRow>(signature: string): T[];
|
|
100
|
+
entity<T extends ReplicaRow = ReplicaRow>(entity: string, id: string): T | undefined;
|
|
101
|
+
entityBatch<T extends ReplicaRow = ReplicaRow>(entity: string, ids: readonly string[]): Array<T | undefined>;
|
|
102
|
+
entityRows<T extends ReplicaRow = ReplicaRow>(entity: string): T[];
|
|
103
|
+
entityCompleteness(entity: string): "complete" | "partial";
|
|
104
|
+
liveQuery<T extends ReplicaRow = ReplicaRow>(signature: string): LiveQueryResult<T>;
|
|
105
|
+
collectionState<T extends ReplicaRow = ReplicaRow>(signature: string): ReplicaCollectionState<T>;
|
|
106
|
+
hasLiveQuery(signature: string): boolean;
|
|
107
|
+
snapshot(): ReplicaSnapshot;
|
|
108
|
+
}
|
|
109
|
+
export declare class LocalReplica implements LocalReplicaView {
|
|
110
|
+
private readonly storage?;
|
|
111
|
+
private cursorValue?;
|
|
112
|
+
private entities;
|
|
113
|
+
private liveQueries;
|
|
114
|
+
/** Rows introduced by a materialized window may be reclaimed conservatively. */
|
|
115
|
+
private readonly windowOwned;
|
|
116
|
+
private pendingCommands;
|
|
117
|
+
private listeners;
|
|
118
|
+
private persistence;
|
|
119
|
+
private application;
|
|
120
|
+
private hydration?;
|
|
121
|
+
private freshnessValue;
|
|
122
|
+
private versionValue;
|
|
123
|
+
private scopeValue;
|
|
124
|
+
private scopeLoaded;
|
|
125
|
+
private scopeActivation?;
|
|
126
|
+
private scopeActivationName?;
|
|
127
|
+
private scopeActivationGeneration;
|
|
128
|
+
constructor(storage?: LocalReplicaStorage | undefined);
|
|
129
|
+
hydrate(): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Atomically switch the authoritative store to an opaque persistence scope.
|
|
132
|
+
* A newer request supersedes an older one even if the older storage read is
|
|
133
|
+
* slow (or resolves after auth has already changed again).
|
|
134
|
+
*/
|
|
135
|
+
activateScope(scope?: ReplicaScope, forceReload?: boolean): Promise<void>;
|
|
136
|
+
cursor(): {
|
|
137
|
+
epoch: string;
|
|
138
|
+
revision: number;
|
|
139
|
+
} | undefined;
|
|
140
|
+
freshness(): ReplicaFreshness;
|
|
141
|
+
version(): number;
|
|
142
|
+
setFreshness(freshness: ReplicaFreshness): void;
|
|
143
|
+
subscribe(listener: () => void): () => boolean;
|
|
144
|
+
applyOptimistic(commandId: string, patches: OptimisticPatch[]): void;
|
|
145
|
+
acknowledgeCommand(commandId: string, committedRevision?: number): void;
|
|
146
|
+
rejectCommand(commandId: string): void;
|
|
147
|
+
/** True while an optimistic command still contributes visible patches. */
|
|
148
|
+
hasPendingCommand(commandId: string): boolean;
|
|
149
|
+
applyTransaction(transaction: ReplicaTransaction, scope?: ReplicaScope): Promise<void>;
|
|
150
|
+
materializeWindow(input: {
|
|
151
|
+
signature: string;
|
|
152
|
+
kind?: "live" | "replica";
|
|
153
|
+
entity: string;
|
|
154
|
+
key: string;
|
|
155
|
+
rows: ReplicaRow[];
|
|
156
|
+
completeness: "complete" | "partial";
|
|
157
|
+
source: "server" | "cache";
|
|
158
|
+
cursor?: ReplicaCursor;
|
|
159
|
+
resultSkeleton?: JsonValue;
|
|
160
|
+
resultPath?: string[];
|
|
161
|
+
scalar?: JsonValue;
|
|
162
|
+
windowRevision?: string;
|
|
163
|
+
subscriptionRevision?: SubscriptionRevision;
|
|
164
|
+
mode?: "eager" | "progressive";
|
|
165
|
+
truncated?: boolean;
|
|
166
|
+
orderBy?: string;
|
|
167
|
+
orderDirection?: "asc" | "desc";
|
|
168
|
+
maxRows?: number;
|
|
169
|
+
maxBytes?: number;
|
|
170
|
+
hashes?: Record<string, string>;
|
|
171
|
+
removedIDs?: string[];
|
|
172
|
+
scope?: ReplicaScope;
|
|
173
|
+
}): Promise<void>;
|
|
174
|
+
/** Replace one server/cache window while retaining shared normalized entities. */
|
|
175
|
+
replaceWindow(input: Parameters<LocalReplica["materializeWindow"]>[0]): Promise<void>;
|
|
176
|
+
/** Apply a bounded window delta and persist it in the same transaction. */
|
|
177
|
+
applyWindowDelta(input: {
|
|
178
|
+
signature: string;
|
|
179
|
+
kind?: "live" | "replica";
|
|
180
|
+
entity: string;
|
|
181
|
+
key: string;
|
|
182
|
+
upserts: ReplicaRow[];
|
|
183
|
+
deleted: string[];
|
|
184
|
+
completeness?: "complete" | "partial";
|
|
185
|
+
source?: "server" | "cache";
|
|
186
|
+
cursor?: ReplicaCursor;
|
|
187
|
+
resultSkeleton?: JsonValue;
|
|
188
|
+
resultPath?: string[];
|
|
189
|
+
scalar?: JsonValue;
|
|
190
|
+
windowRevision?: string;
|
|
191
|
+
subscriptionRevision?: SubscriptionRevision;
|
|
192
|
+
mode?: "eager" | "progressive";
|
|
193
|
+
truncated?: boolean;
|
|
194
|
+
orderBy?: string;
|
|
195
|
+
orderDirection?: "asc" | "desc";
|
|
196
|
+
maxRows?: number;
|
|
197
|
+
maxBytes?: number;
|
|
198
|
+
hashes?: Record<string, string>;
|
|
199
|
+
removedIDs?: string[];
|
|
200
|
+
scope?: ReplicaScope;
|
|
201
|
+
}): Promise<void>;
|
|
202
|
+
getWindow(signature: string): ReplicaWindow | undefined;
|
|
203
|
+
listWindows(): ReplicaWindow[];
|
|
204
|
+
windowRows<T extends ReplicaRow = ReplicaRow>(signature: string): T[];
|
|
205
|
+
removeWindow(signature: string, scope?: ReplicaScope): Promise<void>;
|
|
206
|
+
clear(scope?: ReplicaScope): Promise<void>;
|
|
207
|
+
private materializeWindowNow;
|
|
208
|
+
private applyTransactionNow;
|
|
209
|
+
entity<T extends ReplicaRow = ReplicaRow>(entity: string, id: string): T | undefined;
|
|
210
|
+
/** Resolve several IDs from one atomic Local Replica version. */
|
|
211
|
+
entityBatch<T extends ReplicaRow = ReplicaRow>(entity: string, ids: readonly string[]): Array<T | undefined>;
|
|
212
|
+
/** All cached rows for one normalized entity, including optimistic overlays. */
|
|
213
|
+
entityRows<T extends ReplicaRow = ReplicaRow>(entity: string): T[];
|
|
214
|
+
/** Exact only when an authoritative, non-truncated Replica Collection covers the entity. */
|
|
215
|
+
entityCompleteness(entity: string): "complete" | "partial";
|
|
216
|
+
liveQuery<T extends ReplicaRow = ReplicaRow>(signature: string): LiveQueryResult<T>;
|
|
217
|
+
/** Rows and protocol-owned completeness for one Replica Collection window. */
|
|
218
|
+
collectionState<T extends ReplicaRow = ReplicaRow>(signature: string): ReplicaCollectionState<T>;
|
|
219
|
+
hasLiveQuery(signature: string): boolean;
|
|
220
|
+
snapshot(): ReplicaSnapshot;
|
|
221
|
+
private reconcileCommands;
|
|
222
|
+
private persist;
|
|
223
|
+
private notify;
|
|
224
|
+
private trackWindowOwnership;
|
|
225
|
+
private pruneOwnedEntities;
|
|
226
|
+
private pruneOwnedEntitiesAfterRemoval;
|
|
227
|
+
}
|
|
228
|
+
export declare class MemoryLocalReplicaStorage implements LocalReplicaStorage {
|
|
229
|
+
private readonly values;
|
|
230
|
+
load(scope?: ReplicaScope): Promise<ReplicaSnapshot | undefined>;
|
|
231
|
+
applyTransaction(_transaction: ReplicaTransaction, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
232
|
+
replaceSnapshot(snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
233
|
+
replaceWindow(_window: ReplicaWindow, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
234
|
+
applyWindowDelta(_window: ReplicaWindow, _delta: {
|
|
235
|
+
upserts: ReplicaRow[];
|
|
236
|
+
deleted: string[];
|
|
237
|
+
}, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
238
|
+
removeWindow(_signature: string, snapshot: ReplicaSnapshot, scope?: ReplicaScope): Promise<void>;
|
|
239
|
+
clear(scope?: ReplicaScope): Promise<void>;
|
|
240
|
+
}
|