@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.
Files changed (59) hide show
  1. package/README.md +119 -95
  2. package/dist/control.d.ts +416 -0
  3. package/dist/control.js +210 -0
  4. package/dist/control.js.map +1 -0
  5. package/dist/error-reporter.d.ts +20 -6
  6. package/dist/error-reporter.js +55 -27
  7. package/dist/error-reporter.js.map +1 -1
  8. package/dist/index.d.ts +170 -132
  9. package/dist/index.js +1123 -1138
  10. package/dist/index.js.map +1 -1
  11. package/dist/indexeddb-replica.d.ts +20 -0
  12. package/dist/indexeddb-replica.js +193 -0
  13. package/dist/indexeddb-replica.js.map +1 -0
  14. package/dist/kv-stores.d.ts +15 -0
  15. package/dist/kv-stores.js +60 -0
  16. package/dist/kv-stores.js.map +1 -0
  17. package/dist/local-replica.d.ts +240 -0
  18. package/dist/local-replica.js +590 -0
  19. package/dist/local-replica.js.map +1 -0
  20. package/dist/optimistic.d.ts +34 -55
  21. package/dist/optimistic.js +70 -269
  22. package/dist/optimistic.js.map +1 -1
  23. package/dist/outbox.d.ts +74 -16
  24. package/dist/outbox.js +194 -14
  25. package/dist/outbox.js.map +1 -1
  26. package/dist/query-expression.d.ts +58 -0
  27. package/dist/query-expression.js +164 -0
  28. package/dist/query-expression.js.map +1 -0
  29. package/dist/replica-integrity.d.ts +5 -0
  30. package/dist/replica-integrity.js +58 -0
  31. package/dist/replica-integrity.js.map +1 -0
  32. package/package.json +4 -4
  33. package/dist/browser-cache-client.d.ts +0 -77
  34. package/dist/browser-cache-client.js +0 -156
  35. package/dist/browser-cache-client.js.map +0 -1
  36. package/dist/browser-cache-shared-worker.d.ts +0 -35
  37. package/dist/browser-cache-shared-worker.js +0 -118
  38. package/dist/browser-cache-shared-worker.js.map +0 -1
  39. package/dist/browser-cache.d.ts +0 -43
  40. package/dist/browser-cache.js +0 -67
  41. package/dist/browser-cache.js.map +0 -1
  42. package/dist/browser-capabilities.d.ts +0 -21
  43. package/dist/browser-capabilities.js +0 -31
  44. package/dist/browser-capabilities.js.map +0 -1
  45. package/dist/cache-coordinator.d.ts +0 -37
  46. package/dist/cache-coordinator.js +0 -109
  47. package/dist/cache-coordinator.js.map +0 -1
  48. package/dist/cache.d.ts +0 -74
  49. package/dist/cache.js +0 -120
  50. package/dist/cache.js.map +0 -1
  51. package/dist/persistent-cache.d.ts +0 -41
  52. package/dist/persistent-cache.js +0 -103
  53. package/dist/persistent-cache.js.map +0 -1
  54. package/dist/query-cache.d.ts +0 -88
  55. package/dist/query-cache.js +0 -346
  56. package/dist/query-cache.js.map +0 -1
  57. package/dist/sync-store.d.ts +0 -96
  58. package/dist/sync-store.js +0 -500
  59. 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":"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"}
@@ -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
+ }