@tanstack/electron-db-sqlite-persistence 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +71 -0
  2. package/dist/cjs/electron-coordinator.cjs +541 -0
  3. package/dist/cjs/electron-coordinator.cjs.map +1 -0
  4. package/dist/cjs/electron-coordinator.d.cts +67 -0
  5. package/dist/cjs/errors.d.cts +33 -0
  6. package/dist/cjs/index.cjs +14 -0
  7. package/dist/cjs/index.cjs.map +1 -0
  8. package/dist/cjs/index.d.cts +8 -0
  9. package/dist/cjs/main.cjs +213 -0
  10. package/dist/cjs/main.cjs.map +1 -0
  11. package/dist/cjs/main.d.cts +12 -0
  12. package/dist/cjs/protocol.cjs +7 -0
  13. package/dist/cjs/protocol.cjs.map +1 -0
  14. package/dist/cjs/protocol.d.cts +104 -0
  15. package/dist/cjs/renderer.cjs +221 -0
  16. package/dist/cjs/renderer.cjs.map +1 -0
  17. package/dist/cjs/renderer.d.cts +13 -0
  18. package/dist/esm/electron-coordinator.d.ts +67 -0
  19. package/dist/esm/electron-coordinator.js +541 -0
  20. package/dist/esm/electron-coordinator.js.map +1 -0
  21. package/dist/esm/errors.d.ts +33 -0
  22. package/dist/esm/index.d.ts +8 -0
  23. package/dist/esm/index.js +11 -0
  24. package/dist/esm/index.js.map +1 -0
  25. package/dist/esm/main.d.ts +12 -0
  26. package/dist/esm/main.js +213 -0
  27. package/dist/esm/main.js.map +1 -0
  28. package/dist/esm/protocol.d.ts +104 -0
  29. package/dist/esm/protocol.js +7 -0
  30. package/dist/esm/protocol.js.map +1 -0
  31. package/dist/esm/renderer.d.ts +13 -0
  32. package/dist/esm/renderer.js +221 -0
  33. package/dist/esm/renderer.js.map +1 -0
  34. package/package.json +80 -0
  35. package/src/electron-coordinator.ts +842 -0
  36. package/src/errors.ts +110 -0
  37. package/src/index.ts +17 -0
  38. package/src/main.ts +315 -0
  39. package/src/protocol.ts +137 -0
  40. package/src/renderer.ts +391 -0
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const dbSqlitePersistenceCore = require("@tanstack/db-sqlite-persistence-core");
4
+ const electronCoordinator = require("./electron-coordinator.cjs");
5
+ const protocol = require("./protocol.cjs");
6
+ const DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
7
+ let nextRequestId = 1;
8
+ function createRequestId() {
9
+ const requestId = nextRequestId;
10
+ nextRequestId++;
11
+ return `electron-persistence-${requestId}`;
12
+ }
13
+ function withTimeout(promise, timeoutMs, timeoutMessage) {
14
+ if (timeoutMs <= 0) {
15
+ return promise;
16
+ }
17
+ return new Promise((resolve, reject) => {
18
+ const timer = setTimeout(() => {
19
+ reject(new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(timeoutMessage));
20
+ }, timeoutMs);
21
+ promise.then(
22
+ (value) => {
23
+ clearTimeout(timer);
24
+ resolve(value);
25
+ },
26
+ (error) => {
27
+ clearTimeout(timer);
28
+ reject(error);
29
+ }
30
+ );
31
+ });
32
+ }
33
+ function assertValidResponse(response, request) {
34
+ if (response.v !== protocol.ELECTRON_PERSISTENCE_PROTOCOL_VERSION) {
35
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
36
+ `Unexpected electron persistence protocol version "${response.v}" in response`
37
+ );
38
+ }
39
+ if (response.requestId !== request.requestId) {
40
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
41
+ `Mismatched electron persistence response request id. Expected "${request.requestId}", received "${response.requestId}"`
42
+ );
43
+ }
44
+ if (response.method !== request.method) {
45
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
46
+ `Mismatched electron persistence response method. Expected "${request.method}", received "${response.method}"`
47
+ );
48
+ }
49
+ }
50
+ function createSerializableLoadSubsetOptions(subsetOptions) {
51
+ const { subscription: _subscription, ...serializableOptions } = subsetOptions;
52
+ return serializableOptions;
53
+ }
54
+ function createRendererRequestExecutor(options) {
55
+ const channel = options.channel ?? protocol.DEFAULT_ELECTRON_PERSISTENCE_CHANNEL;
56
+ const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
57
+ return async (method, collectionId, payload, resolution) => {
58
+ const request = {
59
+ v: protocol.ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
60
+ requestId: createRequestId(),
61
+ collectionId,
62
+ method,
63
+ resolution,
64
+ payload
65
+ };
66
+ const response = await withTimeout(
67
+ options.invoke(channel, request),
68
+ timeoutMs,
69
+ `Electron persistence request timed out (method=${method}, collection=${collectionId}, timeoutMs=${timeoutMs})`
70
+ );
71
+ assertValidResponse(response, request);
72
+ if (!response.ok) {
73
+ const remoteError = new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
74
+ `${response.error.name}: ${response.error.message}`
75
+ );
76
+ if (typeof response.error.stack === `string`) {
77
+ remoteError.stack = response.error.stack;
78
+ }
79
+ if (typeof response.error.code === `string`) {
80
+ remoteError.code = response.error.code;
81
+ }
82
+ throw remoteError;
83
+ }
84
+ return response.result;
85
+ };
86
+ }
87
+ function createResolvedRendererAdapter(executeRequest, resolution) {
88
+ return {
89
+ loadSubset: async (collectionId, subsetOptions, ctx) => {
90
+ const result = await executeRequest(
91
+ `loadSubset`,
92
+ collectionId,
93
+ {
94
+ options: createSerializableLoadSubsetOptions(subsetOptions),
95
+ ctx
96
+ },
97
+ resolution
98
+ );
99
+ return result;
100
+ },
101
+ applyCommittedTx: async (collectionId, tx) => {
102
+ await executeRequest(
103
+ `applyCommittedTx`,
104
+ collectionId,
105
+ {
106
+ tx
107
+ },
108
+ resolution
109
+ );
110
+ },
111
+ loadCollectionMetadata: async (collectionId) => {
112
+ return executeRequest(
113
+ `loadCollectionMetadata`,
114
+ collectionId,
115
+ {},
116
+ resolution
117
+ );
118
+ },
119
+ scanRows: async (collectionId, options) => {
120
+ const result = await executeRequest(
121
+ `scanRows`,
122
+ collectionId,
123
+ { options },
124
+ resolution
125
+ );
126
+ return result;
127
+ },
128
+ ensureIndex: async (collectionId, signature, spec) => {
129
+ await executeRequest(
130
+ `ensureIndex`,
131
+ collectionId,
132
+ {
133
+ signature,
134
+ spec
135
+ },
136
+ resolution
137
+ );
138
+ },
139
+ markIndexRemoved: async (collectionId, signature) => {
140
+ await executeRequest(
141
+ `markIndexRemoved`,
142
+ collectionId,
143
+ {
144
+ signature
145
+ },
146
+ resolution
147
+ );
148
+ },
149
+ pullSince: async (collectionId, fromRowVersion) => {
150
+ const result = await executeRequest(
151
+ `pullSince`,
152
+ collectionId,
153
+ {
154
+ fromRowVersion
155
+ },
156
+ resolution
157
+ );
158
+ return result;
159
+ },
160
+ getStreamPosition: async (collectionId) => {
161
+ return executeRequest(`getStreamPosition`, collectionId, {}, resolution);
162
+ }
163
+ };
164
+ }
165
+ function resolveInvoke(options) {
166
+ if (options.invoke) {
167
+ return options.invoke;
168
+ }
169
+ if (options.ipcRenderer) {
170
+ return (channel, request) => options.ipcRenderer.invoke(channel, request);
171
+ }
172
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
173
+ `Electron renderer persistence requires either invoke or ipcRenderer`
174
+ );
175
+ }
176
+ function createElectronSQLitePersistence(options) {
177
+ const invoke = resolveInvoke(options);
178
+ const coordinator = options.coordinator ?? new dbSqlitePersistenceCore.SingleProcessCoordinator();
179
+ const executeRequest = createRendererRequestExecutor({
180
+ invoke,
181
+ channel: options.channel,
182
+ timeoutMs: options.timeoutMs
183
+ });
184
+ const adapterCache = /* @__PURE__ */ new Map();
185
+ const getAdapterForCollection = (mode, schemaVersion) => {
186
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
187
+ const cacheKey = `mode:${mode}|${schemaVersionKey}`;
188
+ const cachedAdapter = adapterCache.get(cacheKey);
189
+ if (cachedAdapter) {
190
+ return cachedAdapter;
191
+ }
192
+ const adapter = createResolvedRendererAdapter(executeRequest, {
193
+ mode,
194
+ schemaVersion
195
+ });
196
+ adapterCache.set(cacheKey, adapter);
197
+ if (coordinator instanceof electronCoordinator.ElectronCollectionCoordinator) {
198
+ coordinator.setAdapter(adapter);
199
+ }
200
+ return adapter;
201
+ };
202
+ const createCollectionPersistence = (mode, schemaVersion) => ({
203
+ adapter: getAdapterForCollection(
204
+ mode,
205
+ schemaVersion
206
+ ),
207
+ coordinator
208
+ });
209
+ const defaultPersistence = createCollectionPersistence(
210
+ `sync-absent`,
211
+ void 0
212
+ );
213
+ return {
214
+ ...defaultPersistence,
215
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
216
+ // Backward compatible fallback for older callers.
217
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
218
+ };
219
+ }
220
+ exports.createElectronSQLitePersistence = createElectronSQLitePersistence;
221
+ //# sourceMappingURL=renderer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.cjs","sources":["../../src/renderer.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n SingleProcessCoordinator,\n} from '@tanstack/db-sqlite-persistence-core'\nimport { ElectronCollectionCoordinator } from './electron-coordinator'\nimport {\n DEFAULT_ELECTRON_PERSISTENCE_CHANNEL,\n ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n} from './protocol'\nimport type {\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n PersistedIndexSpec,\n PersistedTx,\n SQLitePullSinceResult,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type {\n ElectronPersistedKey,\n ElectronPersistedRow,\n ElectronPersistenceInvoke,\n ElectronPersistenceMethod,\n ElectronPersistencePayloadMap,\n ElectronPersistenceRequest,\n ElectronPersistenceRequestEnvelope,\n ElectronPersistenceResolution,\n ElectronPersistenceResponseEnvelope,\n ElectronPersistenceResultMap,\n} from './protocol'\nimport type { LoadSubsetOptions } from '@tanstack/db'\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 5_000\nlet nextRequestId = 1\n\nfunction createRequestId(): string {\n const requestId = nextRequestId\n nextRequestId++\n return `electron-persistence-${requestId}`\n}\n\nfunction withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage: string,\n): Promise<T> {\n if (timeoutMs <= 0) {\n return promise\n }\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new InvalidPersistedCollectionConfigError(timeoutMessage))\n }, timeoutMs)\n\n promise.then(\n (value) => {\n clearTimeout(timer)\n resolve(value)\n },\n (error: unknown) => {\n clearTimeout(timer)\n reject(error)\n },\n )\n })\n}\n\nfunction assertValidResponse(\n response: ElectronPersistenceResponseEnvelope,\n request: ElectronPersistenceRequestEnvelope,\n): void {\n if (response.v !== ELECTRON_PERSISTENCE_PROTOCOL_VERSION) {\n throw new InvalidPersistedCollectionConfigError(\n `Unexpected electron persistence protocol version \"${response.v}\" in response`,\n )\n }\n\n if (response.requestId !== request.requestId) {\n throw new InvalidPersistedCollectionConfigError(\n `Mismatched electron persistence response request id. Expected \"${request.requestId}\", received \"${response.requestId}\"`,\n )\n }\n\n if (response.method !== request.method) {\n throw new InvalidPersistedCollectionConfigError(\n `Mismatched electron persistence response method. Expected \"${request.method}\", received \"${response.method}\"`,\n )\n }\n}\n\nfunction createSerializableLoadSubsetOptions(\n subsetOptions: LoadSubsetOptions,\n): LoadSubsetOptions {\n const { subscription: _subscription, ...serializableOptions } = subsetOptions\n return serializableOptions\n}\n\ntype RendererRequestExecutor = <TMethod extends ElectronPersistenceMethod>(\n method: TMethod,\n collectionId: string,\n payload: ElectronPersistencePayloadMap[TMethod],\n resolution?: ElectronPersistenceResolution,\n) => Promise<ElectronPersistenceResultMap[TMethod]>\n\nfunction createRendererRequestExecutor(options: {\n invoke: ElectronPersistenceInvoke\n channel?: string\n timeoutMs?: number\n}): RendererRequestExecutor {\n const channel = options.channel ?? DEFAULT_ELECTRON_PERSISTENCE_CHANNEL\n const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS\n\n return async <TMethod extends ElectronPersistenceMethod>(\n method: TMethod,\n collectionId: string,\n payload: ElectronPersistencePayloadMap[TMethod],\n resolution?: ElectronPersistenceResolution,\n ) => {\n const request = {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: createRequestId(),\n collectionId,\n method,\n resolution,\n payload,\n } as ElectronPersistenceRequest\n\n const response = await withTimeout(\n options.invoke(channel, request),\n timeoutMs,\n `Electron persistence request timed out (method=${method}, collection=${collectionId}, timeoutMs=${timeoutMs})`,\n )\n assertValidResponse(response, request)\n\n if (!response.ok) {\n const remoteError = new InvalidPersistedCollectionConfigError(\n `${response.error.name}: ${response.error.message}`,\n )\n if (typeof response.error.stack === `string`) {\n remoteError.stack = response.error.stack\n }\n if (typeof response.error.code === `string`) {\n ;(remoteError as Error & { code?: string }).code = response.error.code\n }\n throw remoteError\n }\n\n return response.result as ElectronPersistenceResultMap[TMethod]\n }\n}\n\ntype ElectronRendererResolvedAdapter<\n T extends object,\n TKey extends string | number = string | number,\n> = PersistedCollectionPersistence<T, TKey>[`adapter`] & {\n loadCollectionMetadata: (\n collectionId: string,\n ) => Promise<Array<{ key: string; value: unknown }>>\n scanRows: (\n collectionId: string,\n options?: { metadataOnly?: boolean },\n ) => Promise<Array<{ key: TKey; value: T; metadata?: unknown }>>\n pullSince: (\n collectionId: string,\n fromRowVersion: number,\n ) => Promise<SQLitePullSinceResult<TKey>>\n getStreamPosition: (collectionId: string) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n}\n\nfunction createResolvedRendererAdapter<\n T extends object,\n TKey extends string | number = string | number,\n>(\n executeRequest: RendererRequestExecutor,\n resolution?: ElectronPersistenceResolution,\n): ElectronRendererResolvedAdapter<T, TKey> {\n return {\n loadSubset: async (\n collectionId: string,\n subsetOptions: LoadSubsetOptions,\n ctx?: { requiredIndexSignatures?: ReadonlyArray<string> },\n ) => {\n const result = await executeRequest(\n `loadSubset`,\n collectionId,\n {\n options: createSerializableLoadSubsetOptions(subsetOptions),\n ctx,\n },\n resolution,\n )\n\n return result as Array<{ key: TKey; value: T }>\n },\n applyCommittedTx: async (\n collectionId: string,\n tx: PersistedTx<T, TKey>,\n ): Promise<void> => {\n await executeRequest(\n `applyCommittedTx`,\n collectionId,\n {\n tx: tx as PersistedTx<ElectronPersistedRow, ElectronPersistedKey>,\n },\n resolution,\n )\n },\n loadCollectionMetadata: async (\n collectionId: string,\n ): Promise<Array<{ key: string; value: unknown }>> => {\n return executeRequest(\n `loadCollectionMetadata`,\n collectionId,\n {},\n resolution,\n )\n },\n scanRows: async (\n collectionId: string,\n options?: { metadataOnly?: boolean },\n ): Promise<Array<{ key: TKey; value: T; metadata?: unknown }>> => {\n const result = await executeRequest(\n `scanRows`,\n collectionId,\n { options },\n resolution,\n )\n return result as Array<{ key: TKey; value: T; metadata?: unknown }>\n },\n ensureIndex: async (\n collectionId: string,\n signature: string,\n spec: PersistedIndexSpec,\n ): Promise<void> => {\n await executeRequest(\n `ensureIndex`,\n collectionId,\n {\n signature,\n spec,\n },\n resolution,\n )\n },\n markIndexRemoved: async (\n collectionId: string,\n signature: string,\n ): Promise<void> => {\n await executeRequest(\n `markIndexRemoved`,\n collectionId,\n {\n signature,\n },\n resolution,\n )\n },\n pullSince: async (\n collectionId: string,\n fromRowVersion: number,\n ): Promise<SQLitePullSinceResult<TKey>> => {\n const result = await executeRequest(\n `pullSince`,\n collectionId,\n {\n fromRowVersion,\n },\n resolution,\n )\n return result as SQLitePullSinceResult<TKey>\n },\n getStreamPosition: async (\n collectionId: string,\n ): Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }> => {\n return executeRequest(`getStreamPosition`, collectionId, {}, resolution)\n },\n }\n}\n\nexport type ElectronIpcRendererLike = {\n invoke: (\n channel: string,\n request: ElectronPersistenceRequestEnvelope,\n ) => Promise<ElectronPersistenceResponseEnvelope>\n}\n\nexport type ElectronSQLitePersistenceOptions = {\n invoke?: ElectronPersistenceInvoke\n ipcRenderer?: ElectronIpcRendererLike\n channel?: string\n timeoutMs?: number\n coordinator?: PersistedCollectionCoordinator\n}\n\nfunction resolveInvoke(\n options: ElectronSQLitePersistenceOptions,\n): ElectronPersistenceInvoke {\n if (options.invoke) {\n return options.invoke\n }\n\n if (options.ipcRenderer) {\n return (channel, request) => options.ipcRenderer!.invoke(channel, request)\n }\n\n throw new InvalidPersistedCollectionConfigError(\n `Electron renderer persistence requires either invoke or ipcRenderer`,\n )\n}\n\nexport function createElectronSQLitePersistence<\n T extends object,\n TKey extends string | number = string | number,\n>(\n options: ElectronSQLitePersistenceOptions,\n): PersistedCollectionPersistence<T, TKey> {\n const invoke = resolveInvoke(options)\n const coordinator = options.coordinator ?? new SingleProcessCoordinator()\n const executeRequest = createRendererRequestExecutor({\n invoke,\n channel: options.channel,\n timeoutMs: options.timeoutMs,\n })\n const adapterCache = new Map<\n string,\n ElectronRendererResolvedAdapter<Record<string, unknown>, string | number>\n >()\n\n const getAdapterForCollection = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const schemaVersionKey =\n schemaVersion === undefined ? `schema:default` : `schema:${schemaVersion}`\n const cacheKey = `mode:${mode}|${schemaVersionKey}`\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createResolvedRendererAdapter<\n Record<string, unknown>,\n string | number\n >(executeRequest, {\n mode,\n schemaVersion,\n })\n adapterCache.set(cacheKey, adapter)\n\n // Wire the adapter into the coordinator so it can handle\n // leader-side RPCs (applyCommittedTx, pullSince, getStreamPosition, etc.)\n if (coordinator instanceof ElectronCollectionCoordinator) {\n coordinator.setAdapter(adapter)\n }\n\n return adapter\n }\n\n const createCollectionPersistence = (\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence<T, TKey> => ({\n adapter: getAdapterForCollection(\n mode,\n schemaVersion,\n ) as unknown as PersistedCollectionPersistence<T, TKey>[`adapter`],\n coordinator,\n })\n\n const defaultPersistence = createCollectionPersistence(\n `sync-absent`,\n undefined,\n )\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ mode, schemaVersion }) =>\n createCollectionPersistence(mode, schemaVersion),\n // Backward compatible fallback for older callers.\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(mode, undefined),\n }\n}\n"],"names":["InvalidPersistedCollectionConfigError","ELECTRON_PERSISTENCE_PROTOCOL_VERSION","DEFAULT_ELECTRON_PERSISTENCE_CHANNEL","SingleProcessCoordinator","ElectronCollectionCoordinator"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { PersistedCollectionCoordinator, PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core';
2
+ import { ElectronPersistenceInvoke, ElectronPersistenceRequestEnvelope, ElectronPersistenceResponseEnvelope } from './protocol.cjs';
3
+ export type ElectronIpcRendererLike = {
4
+ invoke: (channel: string, request: ElectronPersistenceRequestEnvelope) => Promise<ElectronPersistenceResponseEnvelope>;
5
+ };
6
+ export type ElectronSQLitePersistenceOptions = {
7
+ invoke?: ElectronPersistenceInvoke;
8
+ ipcRenderer?: ElectronIpcRendererLike;
9
+ channel?: string;
10
+ timeoutMs?: number;
11
+ coordinator?: PersistedCollectionCoordinator;
12
+ };
13
+ export declare function createElectronSQLitePersistence<T extends object, TKey extends string | number = string | number>(options: ElectronSQLitePersistenceOptions): PersistedCollectionPersistence<T, TKey>;
@@ -0,0 +1,67 @@
1
+ import { ApplyLocalMutationsResponse, PersistedCollectionCoordinator, PersistedIndexSpec, PersistedMutationEnvelope, PersistenceAdapter, ProtocolEnvelope, PullSinceResponse } from '@tanstack/db-sqlite-persistence-core';
2
+ import { LoadSubsetOptions } from '@tanstack/db';
3
+ type AdapterWithPullSince = PersistenceAdapter<Record<string, unknown>, string | number> & {
4
+ pullSince?: (collectionId: string, fromRowVersion: number) => Promise<{
5
+ latestRowVersion: number;
6
+ requiresFullReload: true;
7
+ } | {
8
+ latestRowVersion: number;
9
+ requiresFullReload: false;
10
+ changedKeys: Array<string | number>;
11
+ deletedKeys: Array<string | number>;
12
+ }>;
13
+ getStreamPosition?: (collectionId: string) => Promise<{
14
+ latestTerm: number;
15
+ latestSeq: number;
16
+ latestRowVersion: number;
17
+ }>;
18
+ };
19
+ export type ElectronCollectionCoordinatorOptions = {
20
+ dbName: string;
21
+ adapter?: AdapterWithPullSince;
22
+ };
23
+ export declare class ElectronCollectionCoordinator implements PersistedCollectionCoordinator {
24
+ private readonly nodeId;
25
+ private readonly dbName;
26
+ private adapter;
27
+ private readonly channel;
28
+ private readonly collections;
29
+ private readonly pendingRPCs;
30
+ private readonly appliedEnvelopeIds;
31
+ private disposed;
32
+ /** Method indirection to prevent TypeScript from narrowing `disposed` across awaits */
33
+ private isDisposed;
34
+ private requireAdapter;
35
+ constructor(options: ElectronCollectionCoordinatorOptions);
36
+ /**
37
+ * Set or replace the persistence adapter used for leader-side RPC handling.
38
+ * Called by `createElectronSQLitePersistence` to wire the internally-created
39
+ * adapter into the coordinator.
40
+ */
41
+ setAdapter(adapter: AdapterWithPullSince): void;
42
+ getNodeId(): string;
43
+ subscribe(collectionId: string, onMessage: (message: ProtocolEnvelope<unknown>) => void): () => void;
44
+ publish(_collectionId: string, message: ProtocolEnvelope<unknown>): void;
45
+ isLeader(collectionId: string): boolean;
46
+ ensureLeadership(collectionId: string): Promise<void>;
47
+ requestEnsureRemoteSubset(collectionId: string, options: LoadSubsetOptions): Promise<void>;
48
+ requestEnsurePersistedIndex(collectionId: string, signature: string, spec: PersistedIndexSpec): Promise<void>;
49
+ requestApplyLocalMutations(collectionId: string, mutations: Array<PersistedMutationEnvelope>): Promise<ApplyLocalMutationsResponse>;
50
+ pullSince(collectionId: string, fromRowVersion: number): Promise<PullSinceResponse>;
51
+ dispose(): void;
52
+ private ensureCollectionState;
53
+ private acquireLeadership;
54
+ private releaseLeadership;
55
+ private emitHeartbeat;
56
+ private onChannelMessage;
57
+ private sendRPC;
58
+ private sendRPCOnce;
59
+ private handleRPCRequest;
60
+ private handleEnsureRemoteSubset;
61
+ private handleEnsurePersistedIndex;
62
+ private handleApplyLocalMutations;
63
+ private handlePullSince;
64
+ private withWriterLock;
65
+ private pruneAppliedEnvelopeIds;
66
+ }
67
+ export {};