@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,12 @@
1
+ import { PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core';
2
+ import { ElectronPersistedKey, ElectronPersistedRow, ElectronPersistenceRequestEnvelope, ElectronPersistenceResponseEnvelope } from './protocol.js';
3
+ export type ElectronIpcMainLike = {
4
+ handle: (channel: string, listener: (event: unknown, request: ElectronPersistenceRequestEnvelope) => Promise<ElectronPersistenceResponseEnvelope>) => void;
5
+ removeHandler?: (channel: string) => void;
6
+ };
7
+ export type ElectronSQLiteMainProcessOptions = {
8
+ persistence: PersistedCollectionPersistence<ElectronPersistedRow, ElectronPersistedKey>;
9
+ ipcMain: ElectronIpcMainLike;
10
+ channel?: string;
11
+ };
12
+ export declare function exposeElectronSQLitePersistence(options: ElectronSQLiteMainProcessOptions): () => void;
@@ -0,0 +1,213 @@
1
+ import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
2
+ import { DEFAULT_ELECTRON_PERSISTENCE_CHANNEL, ELECTRON_PERSISTENCE_PROTOCOL_VERSION } from "./protocol.js";
3
+ function serializeError(error) {
4
+ const fallbackMessage = `Unknown electron persistence error`;
5
+ if (!(error instanceof Error)) {
6
+ return {
7
+ name: `Error`,
8
+ message: fallbackMessage,
9
+ code: void 0
10
+ };
11
+ }
12
+ const codedError = error;
13
+ return {
14
+ name: error.name || `Error`,
15
+ message: error.message || fallbackMessage,
16
+ stack: error.stack,
17
+ code: typeof codedError.code === `string` ? codedError.code : void 0
18
+ };
19
+ }
20
+ function createErrorResponse(request, error) {
21
+ return {
22
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
23
+ requestId: request.requestId,
24
+ method: request.method,
25
+ ok: false,
26
+ error: serializeError(error)
27
+ };
28
+ }
29
+ function assertValidRequest(request) {
30
+ if (request.v !== ELECTRON_PERSISTENCE_PROTOCOL_VERSION) {
31
+ throw new InvalidPersistedCollectionConfigError(
32
+ `Unsupported electron persistence protocol version "${request.v}"`
33
+ );
34
+ }
35
+ if (typeof request.requestId !== `string` || request.requestId.trim().length === 0) {
36
+ throw new InvalidPersistedCollectionConfigError(
37
+ `Electron persistence requestId cannot be empty`
38
+ );
39
+ }
40
+ if (typeof request.collectionId !== `string` || request.collectionId.trim().length === 0) {
41
+ throw new InvalidPersistedCollectionConfigError(
42
+ `Electron persistence collectionId cannot be empty`
43
+ );
44
+ }
45
+ }
46
+ async function executeRequestAgainstAdapter(request, adapter) {
47
+ switch (request.method) {
48
+ case `loadSubset`: {
49
+ const result = await adapter.loadSubset(
50
+ request.collectionId,
51
+ request.payload.options,
52
+ request.payload.ctx
53
+ );
54
+ return {
55
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
56
+ requestId: request.requestId,
57
+ method: request.method,
58
+ ok: true,
59
+ result
60
+ };
61
+ }
62
+ case `loadCollectionMetadata`: {
63
+ if (!adapter.loadCollectionMetadata) {
64
+ throw new InvalidPersistedCollectionConfigError(
65
+ `loadCollectionMetadata is not supported by the configured electron persistence adapter`
66
+ );
67
+ }
68
+ const result = await adapter.loadCollectionMetadata(request.collectionId);
69
+ return {
70
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
71
+ requestId: request.requestId,
72
+ method: request.method,
73
+ ok: true,
74
+ result
75
+ };
76
+ }
77
+ case `scanRows`: {
78
+ if (!adapter.scanRows) {
79
+ throw new InvalidPersistedCollectionConfigError(
80
+ `scanRows is not supported by the configured electron persistence adapter`
81
+ );
82
+ }
83
+ const result = await adapter.scanRows(
84
+ request.collectionId,
85
+ request.payload.options
86
+ );
87
+ return {
88
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
89
+ requestId: request.requestId,
90
+ method: request.method,
91
+ ok: true,
92
+ result
93
+ };
94
+ }
95
+ case `applyCommittedTx`: {
96
+ await adapter.applyCommittedTx(request.collectionId, request.payload.tx);
97
+ return {
98
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
99
+ requestId: request.requestId,
100
+ method: request.method,
101
+ ok: true,
102
+ result: null
103
+ };
104
+ }
105
+ case `ensureIndex`: {
106
+ await adapter.ensureIndex(
107
+ request.collectionId,
108
+ request.payload.signature,
109
+ request.payload.spec
110
+ );
111
+ return {
112
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
113
+ requestId: request.requestId,
114
+ method: request.method,
115
+ ok: true,
116
+ result: null
117
+ };
118
+ }
119
+ case `markIndexRemoved`: {
120
+ if (!adapter.markIndexRemoved) {
121
+ throw new InvalidPersistedCollectionConfigError(
122
+ `markIndexRemoved is not supported by the configured electron persistence adapter`
123
+ );
124
+ }
125
+ await adapter.markIndexRemoved(
126
+ request.collectionId,
127
+ request.payload.signature
128
+ );
129
+ return {
130
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
131
+ requestId: request.requestId,
132
+ method: request.method,
133
+ ok: true,
134
+ result: null
135
+ };
136
+ }
137
+ case `pullSince`: {
138
+ if (!adapter.pullSince) {
139
+ throw new InvalidPersistedCollectionConfigError(
140
+ `pullSince is not supported by the configured electron persistence adapter`
141
+ );
142
+ }
143
+ const result = await adapter.pullSince(
144
+ request.collectionId,
145
+ request.payload.fromRowVersion
146
+ );
147
+ return {
148
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
149
+ requestId: request.requestId,
150
+ method: request.method,
151
+ ok: true,
152
+ result
153
+ };
154
+ }
155
+ case `getStreamPosition`: {
156
+ if (!adapter.getStreamPosition) {
157
+ throw new InvalidPersistedCollectionConfigError(
158
+ `getStreamPosition is not supported by the configured electron persistence adapter`
159
+ );
160
+ }
161
+ const position = await adapter.getStreamPosition(request.collectionId);
162
+ return {
163
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
164
+ requestId: request.requestId,
165
+ method: request.method,
166
+ ok: true,
167
+ result: position
168
+ };
169
+ }
170
+ }
171
+ }
172
+ function resolveModeAwarePersistence(persistence, request) {
173
+ const mode = request.resolution?.mode ?? `sync-absent`;
174
+ const schemaVersion = request.resolution?.schemaVersion;
175
+ const collectionAwarePersistence = persistence.resolvePersistenceForCollection?.({
176
+ collectionId: request.collectionId,
177
+ mode,
178
+ schemaVersion
179
+ });
180
+ if (collectionAwarePersistence) {
181
+ return collectionAwarePersistence;
182
+ }
183
+ const modeAwarePersistence = persistence.resolvePersistenceForMode?.(mode);
184
+ return modeAwarePersistence ?? persistence;
185
+ }
186
+ function exposeElectronSQLitePersistence(options) {
187
+ const channel = options.channel ?? DEFAULT_ELECTRON_PERSISTENCE_CHANNEL;
188
+ options.ipcMain.handle(
189
+ channel,
190
+ async (_event, request) => {
191
+ try {
192
+ assertValidRequest(request);
193
+ const modeAwarePersistence = resolveModeAwarePersistence(
194
+ options.persistence,
195
+ request
196
+ );
197
+ return await executeRequestAgainstAdapter(
198
+ request,
199
+ modeAwarePersistence.adapter
200
+ );
201
+ } catch (error) {
202
+ return createErrorResponse(request, error);
203
+ }
204
+ }
205
+ );
206
+ return () => {
207
+ options.ipcMain.removeHandler?.(channel);
208
+ };
209
+ }
210
+ export {
211
+ exposeElectronSQLitePersistence
212
+ };
213
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sources":["../../src/main.ts"],"sourcesContent":["import { InvalidPersistedCollectionConfigError } from '@tanstack/db-sqlite-persistence-core'\nimport {\n DEFAULT_ELECTRON_PERSISTENCE_CHANNEL,\n ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n} from './protocol'\nimport type {\n PersistedCollectionPersistence,\n PersistenceAdapter,\n SQLitePullSinceResult,\n} from '@tanstack/db-sqlite-persistence-core'\nimport type {\n ElectronPersistedKey,\n ElectronPersistedRow,\n ElectronPersistenceRequestEnvelope,\n ElectronPersistenceResponseEnvelope,\n ElectronSerializedError,\n} from './protocol'\n\ntype ElectronMainPersistenceAdapter = PersistenceAdapter<\n ElectronPersistedRow,\n ElectronPersistedKey\n> & {\n loadCollectionMetadata?: (\n collectionId: string,\n ) => Promise<Array<{ key: string; value: unknown }>>\n scanRows?: (\n collectionId: string,\n options?: { metadataOnly?: boolean },\n ) => Promise<\n Array<{\n key: ElectronPersistedKey\n value: ElectronPersistedRow\n metadata?: unknown\n }>\n >\n pullSince?: (\n collectionId: string,\n fromRowVersion: number,\n ) => Promise<SQLitePullSinceResult<ElectronPersistedKey>>\n getStreamPosition?: (collectionId: string) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n}\n\nfunction serializeError(error: unknown): ElectronSerializedError {\n const fallbackMessage = `Unknown electron persistence error`\n\n if (!(error instanceof Error)) {\n return {\n name: `Error`,\n message: fallbackMessage,\n code: undefined,\n }\n }\n\n const codedError = error as Error & { code?: unknown }\n return {\n name: error.name || `Error`,\n message: error.message || fallbackMessage,\n stack: error.stack,\n code: typeof codedError.code === `string` ? codedError.code : undefined,\n }\n}\n\nfunction createErrorResponse(\n request: ElectronPersistenceRequestEnvelope,\n error: unknown,\n): ElectronPersistenceResponseEnvelope {\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: false,\n error: serializeError(error),\n }\n}\n\nfunction assertValidRequest(request: ElectronPersistenceRequestEnvelope): void {\n if (request.v !== ELECTRON_PERSISTENCE_PROTOCOL_VERSION) {\n throw new InvalidPersistedCollectionConfigError(\n `Unsupported electron persistence protocol version \"${request.v}\"`,\n )\n }\n\n if (\n typeof request.requestId !== `string` ||\n request.requestId.trim().length === 0\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Electron persistence requestId cannot be empty`,\n )\n }\n\n if (\n typeof request.collectionId !== `string` ||\n request.collectionId.trim().length === 0\n ) {\n throw new InvalidPersistedCollectionConfigError(\n `Electron persistence collectionId cannot be empty`,\n )\n }\n}\n\nasync function executeRequestAgainstAdapter(\n request: ElectronPersistenceRequestEnvelope,\n adapter: ElectronMainPersistenceAdapter,\n): Promise<ElectronPersistenceResponseEnvelope> {\n switch (request.method) {\n case `loadSubset`: {\n const result = await adapter.loadSubset(\n request.collectionId,\n request.payload.options,\n request.payload.ctx,\n )\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result,\n }\n }\n\n case `loadCollectionMetadata`: {\n if (!adapter.loadCollectionMetadata) {\n throw new InvalidPersistedCollectionConfigError(\n `loadCollectionMetadata is not supported by the configured electron persistence adapter`,\n )\n }\n const result = await adapter.loadCollectionMetadata(request.collectionId)\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result,\n }\n }\n\n case `scanRows`: {\n if (!adapter.scanRows) {\n throw new InvalidPersistedCollectionConfigError(\n `scanRows is not supported by the configured electron persistence adapter`,\n )\n }\n const result = await adapter.scanRows(\n request.collectionId,\n request.payload.options,\n )\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result,\n }\n }\n\n case `applyCommittedTx`: {\n await adapter.applyCommittedTx(request.collectionId, request.payload.tx)\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result: null,\n }\n }\n\n case `ensureIndex`: {\n await adapter.ensureIndex(\n request.collectionId,\n request.payload.signature,\n request.payload.spec,\n )\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result: null,\n }\n }\n\n case `markIndexRemoved`: {\n if (!adapter.markIndexRemoved) {\n throw new InvalidPersistedCollectionConfigError(\n `markIndexRemoved is not supported by the configured electron persistence adapter`,\n )\n }\n await adapter.markIndexRemoved(\n request.collectionId,\n request.payload.signature,\n )\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result: null,\n }\n }\n\n case `pullSince`: {\n if (!adapter.pullSince) {\n throw new InvalidPersistedCollectionConfigError(\n `pullSince is not supported by the configured electron persistence adapter`,\n )\n }\n const result = await adapter.pullSince(\n request.collectionId,\n request.payload.fromRowVersion,\n )\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result,\n }\n }\n\n case `getStreamPosition`: {\n if (!adapter.getStreamPosition) {\n throw new InvalidPersistedCollectionConfigError(\n `getStreamPosition is not supported by the configured electron persistence adapter`,\n )\n }\n const position = await adapter.getStreamPosition(request.collectionId)\n return {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: request.requestId,\n method: request.method,\n ok: true,\n result: position,\n }\n }\n }\n}\n\nfunction resolveModeAwarePersistence(\n persistence: PersistedCollectionPersistence<\n ElectronPersistedRow,\n ElectronPersistedKey\n >,\n request: ElectronPersistenceRequestEnvelope,\n): PersistedCollectionPersistence<ElectronPersistedRow, ElectronPersistedKey> {\n const mode = request.resolution?.mode ?? `sync-absent`\n const schemaVersion = request.resolution?.schemaVersion\n const collectionAwarePersistence =\n persistence.resolvePersistenceForCollection?.({\n collectionId: request.collectionId,\n mode,\n schemaVersion,\n })\n if (collectionAwarePersistence) {\n return collectionAwarePersistence\n }\n\n const modeAwarePersistence = persistence.resolvePersistenceForMode?.(mode)\n return modeAwarePersistence ?? persistence\n}\n\nexport type ElectronIpcMainLike = {\n handle: (\n channel: string,\n listener: (\n event: unknown,\n request: ElectronPersistenceRequestEnvelope,\n ) => Promise<ElectronPersistenceResponseEnvelope>,\n ) => void\n removeHandler?: (channel: string) => void\n}\n\nexport type ElectronSQLiteMainProcessOptions = {\n persistence: PersistedCollectionPersistence<\n ElectronPersistedRow,\n ElectronPersistedKey\n >\n ipcMain: ElectronIpcMainLike\n channel?: string\n}\n\nexport function exposeElectronSQLitePersistence(\n options: ElectronSQLiteMainProcessOptions,\n): () => void {\n const channel = options.channel ?? DEFAULT_ELECTRON_PERSISTENCE_CHANNEL\n options.ipcMain.handle(\n channel,\n async (\n _event,\n request: ElectronPersistenceRequestEnvelope,\n ): Promise<ElectronPersistenceResponseEnvelope> => {\n try {\n assertValidRequest(request)\n const modeAwarePersistence = resolveModeAwarePersistence(\n options.persistence,\n request,\n )\n return await executeRequestAgainstAdapter(\n request,\n modeAwarePersistence.adapter,\n )\n } catch (error) {\n return createErrorResponse(request, error)\n }\n },\n )\n\n return () => {\n options.ipcMain.removeHandler?.(channel)\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { LoadSubsetOptions } from '@tanstack/db';
2
+ import { PersistedCollectionMode, PersistedIndexSpec, PersistedTx, SQLitePullSinceResult } from '@tanstack/db-sqlite-persistence-core';
3
+ export declare const ELECTRON_PERSISTENCE_PROTOCOL_VERSION: 1;
4
+ export declare const DEFAULT_ELECTRON_PERSISTENCE_CHANNEL = "tanstack-db:sqlite-persistence";
5
+ export type ElectronPersistedRow = Record<string, unknown>;
6
+ export type ElectronPersistedKey = string | number;
7
+ export type ElectronPersistenceResolution = {
8
+ mode: PersistedCollectionMode;
9
+ schemaVersion?: number;
10
+ };
11
+ export type ElectronPersistenceMethod = `loadSubset` | `loadCollectionMetadata` | `scanRows` | `applyCommittedTx` | `ensureIndex` | `markIndexRemoved` | `pullSince` | `getStreamPosition`;
12
+ export type ElectronPersistencePayloadMap = {
13
+ loadSubset: {
14
+ options: LoadSubsetOptions;
15
+ ctx?: {
16
+ requiredIndexSignatures?: ReadonlyArray<string>;
17
+ };
18
+ };
19
+ loadCollectionMetadata: {};
20
+ scanRows: {
21
+ options?: {
22
+ metadataOnly?: boolean;
23
+ };
24
+ };
25
+ applyCommittedTx: {
26
+ tx: PersistedTx<ElectronPersistedRow, ElectronPersistedKey>;
27
+ };
28
+ ensureIndex: {
29
+ signature: string;
30
+ spec: PersistedIndexSpec;
31
+ };
32
+ markIndexRemoved: {
33
+ signature: string;
34
+ };
35
+ pullSince: {
36
+ fromRowVersion: number;
37
+ };
38
+ getStreamPosition: {};
39
+ };
40
+ export type ElectronPersistenceResultMap = {
41
+ loadSubset: Array<{
42
+ key: ElectronPersistedKey;
43
+ value: ElectronPersistedRow;
44
+ }>;
45
+ loadCollectionMetadata: Array<{
46
+ key: string;
47
+ value: unknown;
48
+ }>;
49
+ scanRows: Array<{
50
+ key: ElectronPersistedKey;
51
+ value: ElectronPersistedRow;
52
+ metadata?: unknown;
53
+ }>;
54
+ applyCommittedTx: null;
55
+ ensureIndex: null;
56
+ markIndexRemoved: null;
57
+ pullSince: SQLitePullSinceResult<ElectronPersistedKey>;
58
+ getStreamPosition: {
59
+ latestTerm: number;
60
+ latestSeq: number;
61
+ latestRowVersion: number;
62
+ };
63
+ };
64
+ export type ElectronSerializedError = {
65
+ name: string;
66
+ message: string;
67
+ stack?: string;
68
+ code?: string;
69
+ };
70
+ export type ElectronPersistenceRequestByMethod = {
71
+ [Method in ElectronPersistenceMethod]: {
72
+ v: number;
73
+ requestId: string;
74
+ collectionId: string;
75
+ resolution?: ElectronPersistenceResolution;
76
+ method: Method;
77
+ payload: ElectronPersistencePayloadMap[Method];
78
+ };
79
+ };
80
+ export type ElectronPersistenceRequest<TMethod extends ElectronPersistenceMethod = ElectronPersistenceMethod> = ElectronPersistenceRequestByMethod[TMethod];
81
+ export type ElectronPersistenceRequestEnvelope = ElectronPersistenceRequestByMethod[ElectronPersistenceMethod];
82
+ type ElectronPersistenceSuccessResponseByMethod = {
83
+ [Method in ElectronPersistenceMethod]: {
84
+ v: number;
85
+ requestId: string;
86
+ method: Method;
87
+ ok: true;
88
+ result: ElectronPersistenceResultMap[Method];
89
+ };
90
+ };
91
+ type ElectronPersistenceErrorResponseByMethod = {
92
+ [Method in ElectronPersistenceMethod]: {
93
+ v: number;
94
+ requestId: string;
95
+ method: Method;
96
+ ok: false;
97
+ error: ElectronSerializedError;
98
+ };
99
+ };
100
+ export type ElectronPersistenceResponse<TMethod extends ElectronPersistenceMethod = ElectronPersistenceMethod> = ElectronPersistenceSuccessResponseByMethod[TMethod] | ElectronPersistenceErrorResponseByMethod[TMethod];
101
+ export type ElectronPersistenceResponseEnvelope = ElectronPersistenceResponse<ElectronPersistenceMethod>;
102
+ export type ElectronPersistenceRequestHandler = (request: ElectronPersistenceRequestEnvelope) => Promise<ElectronPersistenceResponseEnvelope>;
103
+ export type ElectronPersistenceInvoke = (channel: string, request: ElectronPersistenceRequestEnvelope) => Promise<ElectronPersistenceResponseEnvelope>;
104
+ export {};
@@ -0,0 +1,7 @@
1
+ const ELECTRON_PERSISTENCE_PROTOCOL_VERSION = 1;
2
+ const DEFAULT_ELECTRON_PERSISTENCE_CHANNEL = `tanstack-db:sqlite-persistence`;
3
+ export {
4
+ DEFAULT_ELECTRON_PERSISTENCE_CHANNEL,
5
+ ELECTRON_PERSISTENCE_PROTOCOL_VERSION
6
+ };
7
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sources":["../../src/protocol.ts"],"sourcesContent":["import type { LoadSubsetOptions } from '@tanstack/db'\nimport type {\n PersistedCollectionMode,\n PersistedIndexSpec,\n PersistedTx,\n SQLitePullSinceResult,\n} from '@tanstack/db-sqlite-persistence-core'\n\nexport const ELECTRON_PERSISTENCE_PROTOCOL_VERSION = 1 as const\nexport const DEFAULT_ELECTRON_PERSISTENCE_CHANNEL = `tanstack-db:sqlite-persistence`\n\nexport type ElectronPersistedRow = Record<string, unknown>\nexport type ElectronPersistedKey = string | number\n\nexport type ElectronPersistenceResolution = {\n mode: PersistedCollectionMode\n schemaVersion?: number\n}\n\nexport type ElectronPersistenceMethod =\n | `loadSubset`\n | `loadCollectionMetadata`\n | `scanRows`\n | `applyCommittedTx`\n | `ensureIndex`\n | `markIndexRemoved`\n | `pullSince`\n | `getStreamPosition`\n\nexport type ElectronPersistencePayloadMap = {\n loadSubset: {\n options: LoadSubsetOptions\n ctx?: { requiredIndexSignatures?: ReadonlyArray<string> }\n }\n loadCollectionMetadata: {}\n scanRows: {\n options?: {\n metadataOnly?: boolean\n }\n }\n applyCommittedTx: {\n tx: PersistedTx<ElectronPersistedRow, ElectronPersistedKey>\n }\n ensureIndex: {\n signature: string\n spec: PersistedIndexSpec\n }\n markIndexRemoved: {\n signature: string\n }\n pullSince: {\n fromRowVersion: number\n }\n getStreamPosition: {}\n}\n\nexport type ElectronPersistenceResultMap = {\n loadSubset: Array<{ key: ElectronPersistedKey; value: ElectronPersistedRow }>\n loadCollectionMetadata: Array<{ key: string; value: unknown }>\n scanRows: Array<{\n key: ElectronPersistedKey\n value: ElectronPersistedRow\n metadata?: unknown\n }>\n applyCommittedTx: null\n ensureIndex: null\n markIndexRemoved: null\n pullSince: SQLitePullSinceResult<ElectronPersistedKey>\n getStreamPosition: {\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }\n}\n\nexport type ElectronSerializedError = {\n name: string\n message: string\n stack?: string\n code?: string\n}\n\nexport type ElectronPersistenceRequestByMethod = {\n [Method in ElectronPersistenceMethod]: {\n v: number\n requestId: string\n collectionId: string\n resolution?: ElectronPersistenceResolution\n method: Method\n payload: ElectronPersistencePayloadMap[Method]\n }\n}\n\nexport type ElectronPersistenceRequest<\n TMethod extends ElectronPersistenceMethod = ElectronPersistenceMethod,\n> = ElectronPersistenceRequestByMethod[TMethod]\n\nexport type ElectronPersistenceRequestEnvelope =\n ElectronPersistenceRequestByMethod[ElectronPersistenceMethod]\n\ntype ElectronPersistenceSuccessResponseByMethod = {\n [Method in ElectronPersistenceMethod]: {\n v: number\n requestId: string\n method: Method\n ok: true\n result: ElectronPersistenceResultMap[Method]\n }\n}\n\ntype ElectronPersistenceErrorResponseByMethod = {\n [Method in ElectronPersistenceMethod]: {\n v: number\n requestId: string\n method: Method\n ok: false\n error: ElectronSerializedError\n }\n}\n\nexport type ElectronPersistenceResponse<\n TMethod extends ElectronPersistenceMethod = ElectronPersistenceMethod,\n> =\n | ElectronPersistenceSuccessResponseByMethod[TMethod]\n | ElectronPersistenceErrorResponseByMethod[TMethod]\n\nexport type ElectronPersistenceResponseEnvelope =\n ElectronPersistenceResponse<ElectronPersistenceMethod>\n\nexport type ElectronPersistenceRequestHandler = (\n request: ElectronPersistenceRequestEnvelope,\n) => Promise<ElectronPersistenceResponseEnvelope>\n\nexport type ElectronPersistenceInvoke = (\n channel: string,\n request: ElectronPersistenceRequestEnvelope,\n) => Promise<ElectronPersistenceResponseEnvelope>\n"],"names":[],"mappings":"AAQO,MAAM,wCAAwC;AAC9C,MAAM,uCAAuC;"}
@@ -0,0 +1,13 @@
1
+ import { PersistedCollectionCoordinator, PersistedCollectionPersistence } from '@tanstack/db-sqlite-persistence-core';
2
+ import { ElectronPersistenceInvoke, ElectronPersistenceRequestEnvelope, ElectronPersistenceResponseEnvelope } from './protocol.js';
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,221 @@
1
+ import { SingleProcessCoordinator, InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
2
+ import { ElectronCollectionCoordinator } from "./electron-coordinator.js";
3
+ import { DEFAULT_ELECTRON_PERSISTENCE_CHANNEL, ELECTRON_PERSISTENCE_PROTOCOL_VERSION } from "./protocol.js";
4
+ const DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
5
+ let nextRequestId = 1;
6
+ function createRequestId() {
7
+ const requestId = nextRequestId;
8
+ nextRequestId++;
9
+ return `electron-persistence-${requestId}`;
10
+ }
11
+ function withTimeout(promise, timeoutMs, timeoutMessage) {
12
+ if (timeoutMs <= 0) {
13
+ return promise;
14
+ }
15
+ return new Promise((resolve, reject) => {
16
+ const timer = setTimeout(() => {
17
+ reject(new InvalidPersistedCollectionConfigError(timeoutMessage));
18
+ }, timeoutMs);
19
+ promise.then(
20
+ (value) => {
21
+ clearTimeout(timer);
22
+ resolve(value);
23
+ },
24
+ (error) => {
25
+ clearTimeout(timer);
26
+ reject(error);
27
+ }
28
+ );
29
+ });
30
+ }
31
+ function assertValidResponse(response, request) {
32
+ if (response.v !== ELECTRON_PERSISTENCE_PROTOCOL_VERSION) {
33
+ throw new InvalidPersistedCollectionConfigError(
34
+ `Unexpected electron persistence protocol version "${response.v}" in response`
35
+ );
36
+ }
37
+ if (response.requestId !== request.requestId) {
38
+ throw new InvalidPersistedCollectionConfigError(
39
+ `Mismatched electron persistence response request id. Expected "${request.requestId}", received "${response.requestId}"`
40
+ );
41
+ }
42
+ if (response.method !== request.method) {
43
+ throw new InvalidPersistedCollectionConfigError(
44
+ `Mismatched electron persistence response method. Expected "${request.method}", received "${response.method}"`
45
+ );
46
+ }
47
+ }
48
+ function createSerializableLoadSubsetOptions(subsetOptions) {
49
+ const { subscription: _subscription, ...serializableOptions } = subsetOptions;
50
+ return serializableOptions;
51
+ }
52
+ function createRendererRequestExecutor(options) {
53
+ const channel = options.channel ?? DEFAULT_ELECTRON_PERSISTENCE_CHANNEL;
54
+ const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
55
+ return async (method, collectionId, payload, resolution) => {
56
+ const request = {
57
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
58
+ requestId: createRequestId(),
59
+ collectionId,
60
+ method,
61
+ resolution,
62
+ payload
63
+ };
64
+ const response = await withTimeout(
65
+ options.invoke(channel, request),
66
+ timeoutMs,
67
+ `Electron persistence request timed out (method=${method}, collection=${collectionId}, timeoutMs=${timeoutMs})`
68
+ );
69
+ assertValidResponse(response, request);
70
+ if (!response.ok) {
71
+ const remoteError = new InvalidPersistedCollectionConfigError(
72
+ `${response.error.name}: ${response.error.message}`
73
+ );
74
+ if (typeof response.error.stack === `string`) {
75
+ remoteError.stack = response.error.stack;
76
+ }
77
+ if (typeof response.error.code === `string`) {
78
+ remoteError.code = response.error.code;
79
+ }
80
+ throw remoteError;
81
+ }
82
+ return response.result;
83
+ };
84
+ }
85
+ function createResolvedRendererAdapter(executeRequest, resolution) {
86
+ return {
87
+ loadSubset: async (collectionId, subsetOptions, ctx) => {
88
+ const result = await executeRequest(
89
+ `loadSubset`,
90
+ collectionId,
91
+ {
92
+ options: createSerializableLoadSubsetOptions(subsetOptions),
93
+ ctx
94
+ },
95
+ resolution
96
+ );
97
+ return result;
98
+ },
99
+ applyCommittedTx: async (collectionId, tx) => {
100
+ await executeRequest(
101
+ `applyCommittedTx`,
102
+ collectionId,
103
+ {
104
+ tx
105
+ },
106
+ resolution
107
+ );
108
+ },
109
+ loadCollectionMetadata: async (collectionId) => {
110
+ return executeRequest(
111
+ `loadCollectionMetadata`,
112
+ collectionId,
113
+ {},
114
+ resolution
115
+ );
116
+ },
117
+ scanRows: async (collectionId, options) => {
118
+ const result = await executeRequest(
119
+ `scanRows`,
120
+ collectionId,
121
+ { options },
122
+ resolution
123
+ );
124
+ return result;
125
+ },
126
+ ensureIndex: async (collectionId, signature, spec) => {
127
+ await executeRequest(
128
+ `ensureIndex`,
129
+ collectionId,
130
+ {
131
+ signature,
132
+ spec
133
+ },
134
+ resolution
135
+ );
136
+ },
137
+ markIndexRemoved: async (collectionId, signature) => {
138
+ await executeRequest(
139
+ `markIndexRemoved`,
140
+ collectionId,
141
+ {
142
+ signature
143
+ },
144
+ resolution
145
+ );
146
+ },
147
+ pullSince: async (collectionId, fromRowVersion) => {
148
+ const result = await executeRequest(
149
+ `pullSince`,
150
+ collectionId,
151
+ {
152
+ fromRowVersion
153
+ },
154
+ resolution
155
+ );
156
+ return result;
157
+ },
158
+ getStreamPosition: async (collectionId) => {
159
+ return executeRequest(`getStreamPosition`, collectionId, {}, resolution);
160
+ }
161
+ };
162
+ }
163
+ function resolveInvoke(options) {
164
+ if (options.invoke) {
165
+ return options.invoke;
166
+ }
167
+ if (options.ipcRenderer) {
168
+ return (channel, request) => options.ipcRenderer.invoke(channel, request);
169
+ }
170
+ throw new InvalidPersistedCollectionConfigError(
171
+ `Electron renderer persistence requires either invoke or ipcRenderer`
172
+ );
173
+ }
174
+ function createElectronSQLitePersistence(options) {
175
+ const invoke = resolveInvoke(options);
176
+ const coordinator = options.coordinator ?? new SingleProcessCoordinator();
177
+ const executeRequest = createRendererRequestExecutor({
178
+ invoke,
179
+ channel: options.channel,
180
+ timeoutMs: options.timeoutMs
181
+ });
182
+ const adapterCache = /* @__PURE__ */ new Map();
183
+ const getAdapterForCollection = (mode, schemaVersion) => {
184
+ const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
185
+ const cacheKey = `mode:${mode}|${schemaVersionKey}`;
186
+ const cachedAdapter = adapterCache.get(cacheKey);
187
+ if (cachedAdapter) {
188
+ return cachedAdapter;
189
+ }
190
+ const adapter = createResolvedRendererAdapter(executeRequest, {
191
+ mode,
192
+ schemaVersion
193
+ });
194
+ adapterCache.set(cacheKey, adapter);
195
+ if (coordinator instanceof ElectronCollectionCoordinator) {
196
+ coordinator.setAdapter(adapter);
197
+ }
198
+ return adapter;
199
+ };
200
+ const createCollectionPersistence = (mode, schemaVersion) => ({
201
+ adapter: getAdapterForCollection(
202
+ mode,
203
+ schemaVersion
204
+ ),
205
+ coordinator
206
+ });
207
+ const defaultPersistence = createCollectionPersistence(
208
+ `sync-absent`,
209
+ void 0
210
+ );
211
+ return {
212
+ ...defaultPersistence,
213
+ resolvePersistenceForCollection: ({ mode, schemaVersion }) => createCollectionPersistence(mode, schemaVersion),
214
+ // Backward compatible fallback for older callers.
215
+ resolvePersistenceForMode: (mode) => createCollectionPersistence(mode, void 0)
216
+ };
217
+ }
218
+ export {
219
+ createElectronSQLitePersistence
220
+ };
221
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","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":[],"mappings":"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"}