@tanstack/electron-db-sqlite-persistence 0.2.9 → 0.2.10

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.
@@ -54,13 +54,24 @@ function createSerializableLoadSubsetOptions(subsetOptions) {
54
54
  function createRendererRequestExecutor(options) {
55
55
  const channel = options.channel ?? protocol.DEFAULT_ELECTRON_PERSISTENCE_CHANNEL;
56
56
  const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
57
- return async (method, collectionId, payload, resolution) => {
57
+ return async (method, target, payload, resolution) => {
58
+ const storageTarget = typeof target === `string` ? void 0 : target;
59
+ const collectionId = storageTarget ? dbSqlitePersistenceCore.resolvePersistedStorageTarget(storageTarget).collectionId : target;
60
+ const isGenerationRequest = method === `claimCacheGeneration` || method === `rotateCacheGeneration` || method === `renewCacheGenerationClaim` || method === `releaseCacheGenerationClaim`;
61
+ const managedTarget = storageTarget?.kind === `managed`;
62
+ const logicalCollectionId = resolution?.logicalCollectionId ?? (managedTarget ? options.claimCollections.get(storageTarget.claimId) : isGenerationRequest ? collectionId : void 0);
63
+ if ((managedTarget || isGenerationRequest) && !logicalCollectionId) {
64
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
65
+ `Electron managed cache request requires a known logical Collection ID`
66
+ );
67
+ }
58
68
  const request = {
59
69
  v: protocol.ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
60
70
  requestId: createRequestId(),
61
71
  collectionId,
72
+ storageTarget,
62
73
  method,
63
- resolution,
74
+ resolution: managedTarget || isGenerationRequest ? { ...resolution, mode: `sync-present`, logicalCollectionId } : resolution,
64
75
  payload
65
76
  };
66
77
  const response = await withTimeout(
@@ -87,105 +98,219 @@ function createRendererRequestExecutor(options) {
87
98
  return response.result;
88
99
  };
89
100
  }
90
- function createResolvedRendererAdapter(executeRequest, resolution) {
91
- return {
92
- loadSubset: async (collectionId, subsetOptions, ctx) => {
101
+ function createResolvedRendererAdapter(executeRequest, claimCollections, managedCacheGenerations, resolution) {
102
+ const adapter = {
103
+ loadSubset: async (target, subsetOptions, ctx) => {
104
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
105
+ target,
106
+ ctx?.cacheGenerationClaimId
107
+ );
93
108
  const result = await executeRequest(
94
109
  `loadSubset`,
95
- collectionId,
110
+ target,
96
111
  {
97
112
  options: createSerializableLoadSubsetOptions(subsetOptions),
98
- ctx
113
+ ctx: { ...ctx, cacheGenerationClaimId: access.claimId }
99
114
  },
100
115
  resolution
101
116
  );
102
117
  return result;
103
118
  },
104
- loadResumeSnapshot: async (collectionId, ctx) => {
119
+ loadResumeSnapshot: async (target, ctx) => {
120
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
121
+ target,
122
+ ctx?.cacheGenerationClaimId
123
+ );
105
124
  return executeRequest(
106
125
  `loadResumeSnapshot`,
107
- collectionId,
108
- { ctx },
126
+ target,
127
+ { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },
109
128
  resolution
110
129
  );
111
130
  },
112
- applyCommittedTx: async (collectionId, tx) => {
131
+ applyCommittedTx: async (target, tx) => {
132
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
133
+ target,
134
+ tx.cacheGenerationClaimId
135
+ );
113
136
  await executeRequest(
114
137
  `applyCommittedTx`,
115
- collectionId,
138
+ target,
116
139
  {
117
- tx
140
+ tx: { ...tx, cacheGenerationClaimId: access.claimId }
118
141
  },
119
142
  resolution
120
143
  );
121
144
  },
122
- reconcileCommittedTx: async (collectionId, tx, anchor) => {
145
+ reconcileCommittedTx: async (target, tx, anchor) => {
146
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
147
+ target,
148
+ tx.cacheGenerationClaimId
149
+ );
123
150
  return executeRequest(
124
151
  `reconcileCommittedTx`,
125
- collectionId,
126
- { tx, anchor },
152
+ target,
153
+ { tx: { ...tx, cacheGenerationClaimId: access.claimId }, anchor },
127
154
  resolution
128
155
  );
129
156
  },
130
- loadCollectionMetadata: async (collectionId) => {
157
+ loadCollectionMetadata: async (target, ctx) => {
158
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
159
+ target,
160
+ ctx?.cacheGenerationClaimId
161
+ );
131
162
  return executeRequest(
132
163
  `loadCollectionMetadata`,
133
- collectionId,
134
- {},
164
+ target,
165
+ { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },
135
166
  resolution
136
167
  );
137
168
  },
138
- scanRows: async (collectionId, options) => {
169
+ scanRows: async (target, options, ctx) => {
170
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
171
+ target,
172
+ ctx?.cacheGenerationClaimId
173
+ );
139
174
  const result = await executeRequest(
140
175
  `scanRows`,
141
- collectionId,
142
- { options },
176
+ target,
177
+ { options, ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },
143
178
  resolution
144
179
  );
145
180
  return result;
146
181
  },
147
- ensureIndex: async (collectionId, signature, spec) => {
182
+ ensureIndex: async (target, signature, spec, ctx) => {
183
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
184
+ target,
185
+ ctx?.cacheGenerationClaimId
186
+ );
148
187
  await executeRequest(
149
188
  `ensureIndex`,
150
- collectionId,
189
+ target,
151
190
  {
152
191
  signature,
153
- spec
192
+ spec,
193
+ ctx: { ...ctx, cacheGenerationClaimId: access.claimId }
154
194
  },
155
195
  resolution
156
196
  );
157
197
  },
158
- markIndexRemoved: async (collectionId, signature) => {
198
+ markIndexRemoved: async (target, signature, ctx) => {
199
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
200
+ target,
201
+ ctx?.cacheGenerationClaimId
202
+ );
159
203
  await executeRequest(
160
204
  `markIndexRemoved`,
161
- collectionId,
205
+ target,
162
206
  {
163
- signature
207
+ signature,
208
+ ctx: { ...ctx, cacheGenerationClaimId: access.claimId }
164
209
  },
165
210
  resolution
166
211
  );
167
212
  },
168
- pullSince: async (collectionId, fromRowVersion) => {
213
+ pullSince: async (target, fromRowVersion, ctx) => {
214
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
215
+ target,
216
+ ctx?.cacheGenerationClaimId
217
+ );
169
218
  const result = await executeRequest(
170
219
  `pullSince`,
171
- collectionId,
220
+ target,
172
221
  {
173
- fromRowVersion
222
+ fromRowVersion,
223
+ ctx: { ...ctx, cacheGenerationClaimId: access.claimId }
174
224
  },
175
225
  resolution
176
226
  );
177
227
  return result;
178
228
  },
179
- getStreamPosition: async (collectionId) => {
180
- return executeRequest(`getStreamPosition`, collectionId, {}, resolution);
229
+ getStreamPosition: async (target, ctx) => {
230
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
231
+ target,
232
+ ctx?.cacheGenerationClaimId
233
+ );
234
+ return executeRequest(
235
+ `getStreamPosition`,
236
+ target,
237
+ { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },
238
+ resolution
239
+ );
181
240
  },
182
- reserveLeadershipTerm: async (collectionId, observedTerm) => executeRequest(
183
- `reserveLeadershipTerm`,
184
- collectionId,
185
- { observedTerm },
186
- resolution
187
- )
241
+ claimCacheGeneration: async (collectionId) => {
242
+ const claim = await executeRequest(
243
+ `claimCacheGeneration`,
244
+ collectionId,
245
+ {},
246
+ resolution
247
+ );
248
+ claimCollections.set(claim.claimId, collectionId);
249
+ return claim;
250
+ },
251
+ rotateCacheGeneration: async (collectionId, claimId, resetMetadata, expectedStorageCollectionId) => {
252
+ const claim = await executeRequest(
253
+ `rotateCacheGeneration`,
254
+ collectionId,
255
+ { claimId, resetMetadata, expectedStorageCollectionId },
256
+ resolution
257
+ );
258
+ claimCollections.delete(claimId);
259
+ claimCollections.set(claim.claimId, collectionId);
260
+ return claim;
261
+ },
262
+ renewCacheGenerationClaim: (storageCollectionId, claimId) => {
263
+ const collectionId = claimCollections.get(claimId);
264
+ if (!collectionId) {
265
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
266
+ `Unknown electron persisted cache claim "${claimId}"`
267
+ );
268
+ }
269
+ return executeRequest(
270
+ `renewCacheGenerationClaim`,
271
+ collectionId,
272
+ { storageCollectionId, claimId },
273
+ resolution
274
+ );
275
+ },
276
+ releaseCacheGenerationClaim: async (claimId) => {
277
+ const collectionId = claimCollections.get(claimId);
278
+ if (!collectionId) {
279
+ throw new dbSqlitePersistenceCore.InvalidPersistedCollectionConfigError(
280
+ `Unknown electron persisted cache claim "${claimId}"`
281
+ );
282
+ }
283
+ await executeRequest(
284
+ `releaseCacheGenerationClaim`,
285
+ collectionId,
286
+ { claimId },
287
+ resolution
288
+ );
289
+ claimCollections.delete(claimId);
290
+ },
291
+ reserveLeadershipTerm: async (target, observedTerm, ctx) => {
292
+ const access = dbSqlitePersistenceCore.resolvePersistedStorageTarget(
293
+ target,
294
+ ctx?.cacheGenerationClaimId
295
+ );
296
+ return executeRequest(
297
+ `reserveLeadershipTerm`,
298
+ target,
299
+ {
300
+ observedTerm,
301
+ ctx: { ...ctx, cacheGenerationClaimId: access.claimId }
302
+ },
303
+ resolution
304
+ );
305
+ }
188
306
  };
307
+ if (!managedCacheGenerations) {
308
+ adapter.claimCacheGeneration = void 0;
309
+ adapter.rotateCacheGeneration = void 0;
310
+ adapter.renewCacheGenerationClaim = void 0;
311
+ adapter.releaseCacheGenerationClaim = void 0;
312
+ }
313
+ return adapter;
189
314
  }
190
315
  function resolveInvoke(options) {
191
316
  if (options.invoke) {
@@ -201,28 +326,31 @@ function resolveInvoke(options) {
201
326
  function createElectronSQLitePersistence(options) {
202
327
  const invoke = resolveInvoke(options);
203
328
  const coordinator = options.coordinator ?? new dbSqlitePersistenceCore.SingleProcessCoordinator();
329
+ const claimCollections = /* @__PURE__ */ new Map();
204
330
  const executeRequest = createRendererRequestExecutor({
205
331
  invoke,
206
332
  channel: options.channel,
207
- timeoutMs: options.timeoutMs
333
+ timeoutMs: options.timeoutMs,
334
+ claimCollections
208
335
  });
209
336
  const adapterCache = /* @__PURE__ */ new Map();
210
- const getAdapterForCollection = (mode, schemaVersion) => {
211
- const schemaVersionKey = schemaVersion === void 0 ? `schema:default` : `schema:${schemaVersion}`;
212
- const cacheKey = `mode:${mode}|${schemaVersionKey}`;
337
+ const getAdapterForCollection = (collectionId, mode, schemaVersion) => {
338
+ const cacheKey = JSON.stringify([collectionId, mode, schemaVersion]);
213
339
  const cachedAdapter = adapterCache.get(cacheKey);
214
340
  if (cachedAdapter) {
215
341
  return cachedAdapter;
216
342
  }
217
- const adapter = createResolvedRendererAdapter(executeRequest, {
218
- mode,
219
- schemaVersion
220
- });
343
+ const adapter = createResolvedRendererAdapter(
344
+ executeRequest,
345
+ claimCollections,
346
+ options.managedCacheGenerations ?? true,
347
+ { mode, schemaVersion, logicalCollectionId: collectionId }
348
+ );
221
349
  adapterCache.set(cacheKey, adapter);
222
350
  return adapter;
223
351
  };
224
352
  const createCollectionPersistence = (collectionId, mode, schemaVersion) => {
225
- const adapter = getAdapterForCollection(mode, schemaVersion);
353
+ const adapter = getAdapterForCollection(collectionId, mode, schemaVersion);
226
354
  if (coordinator instanceof electronCoordinator.ElectronCollectionCoordinator) {
227
355
  if (collectionId === void 0) {
228
356
  coordinator.setAdapter(adapter);
@@ -1 +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 CommittedTxAnchor,\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n PersistedIndexSpec,\n PersistedTx,\n ReconciledCommittedTx,\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 (\n typeof response.error.code === `string` ||\n typeof response.error.code === `number`\n ) {\n ;(remoteError as Error & { code?: string | number }).code =\n response.error.code\n }\n if (response.error.path !== undefined) {\n ;(\n remoteError as Error & {\n path?: string | ReadonlyArray<string | number>\n }\n ).path = response.error.path\n }\n throw remoteError\n }\n\n return response.result as ElectronPersistenceResultMap[TMethod]\n }\n}\n\ntype ElectronRendererResolvedAdapter =\n PersistedCollectionPersistence[`adapter`] & {\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: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\n >\n pullSince: (\n collectionId: string,\n fromRowVersion: number,\n ) => Promise<SQLitePullSinceResult<string | number>>\n getStreamPosition: (collectionId: string) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n reserveLeadershipTerm: (\n collectionId: string,\n observedTerm: number,\n ) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n }\n\nfunction createResolvedRendererAdapter(\n executeRequest: RendererRequestExecutor,\n resolution?: ElectronPersistenceResolution,\n): ElectronRendererResolvedAdapter {\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<{\n key: string | number\n value: Record<string, unknown>\n }>\n },\n loadResumeSnapshot: async (\n collectionId: string,\n ctx?: {\n requiredIndexSignatures?: ReadonlyArray<string>\n includeRows?: boolean\n },\n ) => {\n return executeRequest(\n `loadResumeSnapshot`,\n collectionId,\n { ctx },\n resolution,\n )\n },\n applyCommittedTx: async (\n collectionId: string,\n tx: PersistedTx<Record<string, unknown>, string | number>,\n ): Promise<void> => {\n await executeRequest(\n `applyCommittedTx`,\n collectionId,\n {\n tx: tx as PersistedTx<ElectronPersistedRow, ElectronPersistedKey>,\n },\n resolution,\n )\n },\n reconcileCommittedTx: async (\n collectionId: string,\n tx: PersistedTx<Record<string, unknown>, string | number>,\n anchor: CommittedTxAnchor,\n ): Promise<ReconciledCommittedTx> => {\n return executeRequest(\n `reconcileCommittedTx`,\n collectionId,\n { tx, anchor },\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<\n Array<{\n key: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\n > => {\n const result = await executeRequest(\n `scanRows`,\n collectionId,\n { options },\n resolution,\n )\n return result as Array<{\n key: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\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<string | number>> => {\n const result = await executeRequest(\n `pullSince`,\n collectionId,\n {\n fromRowVersion,\n },\n resolution,\n )\n return result as SQLitePullSinceResult<string | number>\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 reserveLeadershipTerm: async (collectionId, observedTerm) =>\n executeRequest(\n `reserveLeadershipTerm`,\n collectionId,\n { observedTerm },\n 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 options: ElectronSQLitePersistenceOptions,\n): PersistedCollectionPersistence {\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<string, ElectronRendererResolvedAdapter>()\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(executeRequest, {\n mode,\n schemaVersion,\n })\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(mode, schemaVersion)\n if (coordinator instanceof ElectronCollectionCoordinator) {\n if (collectionId === undefined) {\n coordinator.setAdapter(adapter)\n } else {\n coordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return { adapter, coordinator }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (coordinator instanceof ElectronCollectionCoordinator) {\n coordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n // Backward compatible fallback for older callers.\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":["InvalidPersistedCollectionConfigError","ELECTRON_PERSISTENCE_PROTOCOL_VERSION","DEFAULT_ELECTRON_PERSISTENCE_CHANNEL","SingleProcessCoordinator","ElectronCollectionCoordinator"],"mappings":"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"}
1
+ {"version":3,"file":"renderer.cjs","sources":["../../src/renderer.ts"],"sourcesContent":["import {\n InvalidPersistedCollectionConfigError,\n SingleProcessCoordinator,\n resolvePersistedStorageTarget,\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 CommittedTxAnchor,\n PersistedCacheGenerationClaim,\n PersistedCollectionCoordinator,\n PersistedCollectionMode,\n PersistedCollectionPersistence,\n PersistedIndexSpec,\n PersistedStorageTarget,\n PersistedTx,\n ReconciledCommittedTx,\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 target: string | PersistedStorageTarget,\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 claimCollections: Map<string, string>\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 target: string | PersistedStorageTarget,\n payload: ElectronPersistencePayloadMap[TMethod],\n resolution?: ElectronPersistenceResolution,\n ) => {\n const storageTarget = typeof target === `string` ? undefined : target\n const collectionId = storageTarget\n ? resolvePersistedStorageTarget(storageTarget).collectionId\n : target\n const isGenerationRequest =\n method === `claimCacheGeneration` ||\n method === `rotateCacheGeneration` ||\n method === `renewCacheGenerationClaim` ||\n method === `releaseCacheGenerationClaim`\n const managedTarget = storageTarget?.kind === `managed`\n const logicalCollectionId =\n resolution?.logicalCollectionId ??\n (managedTarget\n ? options.claimCollections.get(storageTarget.claimId)\n : isGenerationRequest\n ? collectionId\n : undefined)\n if ((managedTarget || isGenerationRequest) && !logicalCollectionId) {\n throw new InvalidPersistedCollectionConfigError(\n `Electron managed cache request requires a known logical Collection ID`,\n )\n }\n const request = {\n v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,\n requestId: createRequestId(),\n collectionId,\n storageTarget,\n method,\n resolution:\n managedTarget || isGenerationRequest\n ? { ...resolution, mode: `sync-present`, logicalCollectionId }\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 (\n typeof response.error.code === `string` ||\n typeof response.error.code === `number`\n ) {\n ;(remoteError as Error & { code?: string | number }).code =\n response.error.code\n }\n if (response.error.path !== undefined) {\n ;(\n remoteError as Error & {\n path?: string | ReadonlyArray<string | number>\n }\n ).path = response.error.path\n }\n throw remoteError\n }\n\n return response.result as ElectronPersistenceResultMap[TMethod]\n }\n}\n\ntype ElectronRendererResolvedAdapter =\n PersistedCollectionPersistence[`adapter`] & {\n loadCollectionMetadata: (\n target: PersistedStorageTarget,\n ctx?: { cacheGenerationClaimId?: string },\n ) => Promise<Array<{ key: string; value: unknown }>>\n scanRows: (\n target: PersistedStorageTarget,\n options?: { metadataOnly?: boolean },\n ctx?: { cacheGenerationClaimId?: string },\n ) => Promise<\n Array<{\n key: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\n >\n pullSince: (\n target: PersistedStorageTarget,\n fromRowVersion: number,\n ctx?: { cacheGenerationClaimId?: string },\n ) => Promise<SQLitePullSinceResult<string | number>>\n getStreamPosition: (\n target: PersistedStorageTarget,\n ctx?: { cacheGenerationClaimId?: string },\n ) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n reserveLeadershipTerm: (\n target: PersistedStorageTarget,\n observedTerm: number,\n ctx?: { cacheGenerationClaimId?: string },\n ) => Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }>\n }\n\nfunction createResolvedRendererAdapter(\n executeRequest: RendererRequestExecutor,\n claimCollections: Map<string, string>,\n managedCacheGenerations: boolean,\n resolution?: ElectronPersistenceResolution,\n): ElectronRendererResolvedAdapter {\n const adapter: ElectronRendererResolvedAdapter = {\n loadSubset: async (\n target: PersistedStorageTarget,\n subsetOptions: LoadSubsetOptions,\n ctx?: {\n requiredIndexSignatures?: ReadonlyArray<string>\n cacheGenerationClaimId?: string\n },\n ) => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n const result = await executeRequest(\n `loadSubset`,\n target,\n {\n options: createSerializableLoadSubsetOptions(subsetOptions),\n ctx: { ...ctx, cacheGenerationClaimId: access.claimId },\n },\n resolution,\n )\n\n return result as Array<{\n key: string | number\n value: Record<string, unknown>\n }>\n },\n loadResumeSnapshot: async (\n target: PersistedStorageTarget,\n ctx?: {\n requiredIndexSignatures?: ReadonlyArray<string>\n includeRows?: boolean\n cacheGenerationClaimId?: string\n },\n ) => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n return executeRequest(\n `loadResumeSnapshot`,\n target,\n { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },\n resolution,\n )\n },\n applyCommittedTx: async (\n target: PersistedStorageTarget,\n tx: PersistedTx<Record<string, unknown>, string | number>,\n ): Promise<void> => {\n const access = resolvePersistedStorageTarget(\n target,\n tx.cacheGenerationClaimId,\n )\n await executeRequest(\n `applyCommittedTx`,\n target,\n {\n tx: { ...tx, cacheGenerationClaimId: access.claimId } as PersistedTx<\n ElectronPersistedRow,\n ElectronPersistedKey\n >,\n },\n resolution,\n )\n },\n reconcileCommittedTx: async (\n target: PersistedStorageTarget,\n tx: PersistedTx<Record<string, unknown>, string | number>,\n anchor: CommittedTxAnchor,\n ): Promise<ReconciledCommittedTx> => {\n const access = resolvePersistedStorageTarget(\n target,\n tx.cacheGenerationClaimId,\n )\n return executeRequest(\n `reconcileCommittedTx`,\n target,\n { tx: { ...tx, cacheGenerationClaimId: access.claimId }, anchor },\n resolution,\n )\n },\n loadCollectionMetadata: async (\n target: PersistedStorageTarget,\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<Array<{ key: string; value: unknown }>> => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n return executeRequest(\n `loadCollectionMetadata`,\n target,\n { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },\n resolution,\n )\n },\n scanRows: async (\n target: PersistedStorageTarget,\n options?: { metadataOnly?: boolean },\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<\n Array<{\n key: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\n > => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n const result = await executeRequest(\n `scanRows`,\n target,\n { options, ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },\n resolution,\n )\n return result as Array<{\n key: string | number\n value: Record<string, unknown>\n metadata?: unknown\n }>\n },\n ensureIndex: async (\n target: PersistedStorageTarget,\n signature: string,\n spec: PersistedIndexSpec,\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<void> => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n await executeRequest(\n `ensureIndex`,\n target,\n {\n signature,\n spec,\n ctx: { ...ctx, cacheGenerationClaimId: access.claimId },\n },\n resolution,\n )\n },\n markIndexRemoved: async (\n target: PersistedStorageTarget,\n signature: string,\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<void> => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n await executeRequest(\n `markIndexRemoved`,\n target,\n {\n signature,\n ctx: { ...ctx, cacheGenerationClaimId: access.claimId },\n },\n resolution,\n )\n },\n pullSince: async (\n target: PersistedStorageTarget,\n fromRowVersion: number,\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<SQLitePullSinceResult<string | number>> => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n const result = await executeRequest(\n `pullSince`,\n target,\n {\n fromRowVersion,\n ctx: { ...ctx, cacheGenerationClaimId: access.claimId },\n },\n resolution,\n )\n return result as SQLitePullSinceResult<string | number>\n },\n getStreamPosition: async (\n target: PersistedStorageTarget,\n ctx?: { cacheGenerationClaimId?: string },\n ): Promise<{\n latestTerm: number\n latestSeq: number\n latestRowVersion: number\n }> => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n return executeRequest(\n `getStreamPosition`,\n target,\n { ctx: { ...ctx, cacheGenerationClaimId: access.claimId } },\n resolution,\n )\n },\n claimCacheGeneration: async (\n collectionId: string,\n ): Promise<PersistedCacheGenerationClaim> => {\n const claim = await executeRequest(\n `claimCacheGeneration`,\n collectionId,\n {},\n resolution,\n )\n claimCollections.set(claim.claimId, collectionId)\n return claim\n },\n rotateCacheGeneration: async (\n collectionId: string,\n claimId: string,\n resetMetadata?: { key: string; value: unknown },\n expectedStorageCollectionId?: string,\n ): Promise<PersistedCacheGenerationClaim> => {\n const claim = await executeRequest(\n `rotateCacheGeneration`,\n collectionId,\n { claimId, resetMetadata, expectedStorageCollectionId },\n resolution,\n )\n claimCollections.delete(claimId)\n claimCollections.set(claim.claimId, collectionId)\n return claim\n },\n renewCacheGenerationClaim: (\n storageCollectionId: string,\n claimId: string,\n ): Promise<number | undefined> => {\n const collectionId = claimCollections.get(claimId)\n if (!collectionId) {\n throw new InvalidPersistedCollectionConfigError(\n `Unknown electron persisted cache claim \"${claimId}\"`,\n )\n }\n return executeRequest(\n `renewCacheGenerationClaim`,\n collectionId,\n { storageCollectionId, claimId },\n resolution,\n )\n },\n releaseCacheGenerationClaim: async (claimId: string): Promise<void> => {\n const collectionId = claimCollections.get(claimId)\n if (!collectionId) {\n throw new InvalidPersistedCollectionConfigError(\n `Unknown electron persisted cache claim \"${claimId}\"`,\n )\n }\n await executeRequest(\n `releaseCacheGenerationClaim`,\n collectionId,\n { claimId },\n resolution,\n )\n claimCollections.delete(claimId)\n },\n reserveLeadershipTerm: async (target, observedTerm, ctx) => {\n const access = resolvePersistedStorageTarget(\n target,\n ctx?.cacheGenerationClaimId,\n )\n return executeRequest(\n `reserveLeadershipTerm`,\n target,\n {\n observedTerm,\n ctx: { ...ctx, cacheGenerationClaimId: access.claimId },\n },\n resolution,\n )\n },\n }\n if (!managedCacheGenerations) {\n adapter.claimCacheGeneration = undefined\n adapter.rotateCacheGeneration = undefined\n adapter.renewCacheGenerationClaim = undefined\n adapter.releaseCacheGenerationClaim = undefined\n }\n return adapter\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 /** Set false when a custom main-process adapter does not manage cache generations. */\n managedCacheGenerations?: boolean\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 options: ElectronSQLitePersistenceOptions,\n): PersistedCollectionPersistence {\n const invoke = resolveInvoke(options)\n const coordinator = options.coordinator ?? new SingleProcessCoordinator()\n const claimCollections = new Map<string, string>()\n const executeRequest = createRendererRequestExecutor({\n invoke,\n channel: options.channel,\n timeoutMs: options.timeoutMs,\n claimCollections,\n })\n const adapterCache = new Map<string, ElectronRendererResolvedAdapter>()\n\n const getAdapterForCollection = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ) => {\n const cacheKey = JSON.stringify([collectionId, mode, schemaVersion])\n const cachedAdapter = adapterCache.get(cacheKey)\n if (cachedAdapter) {\n return cachedAdapter\n }\n\n const adapter = createResolvedRendererAdapter(\n executeRequest,\n claimCollections,\n options.managedCacheGenerations ?? true,\n { mode, schemaVersion, logicalCollectionId: collectionId },\n )\n adapterCache.set(cacheKey, adapter)\n return adapter\n }\n\n const createCollectionPersistence = (\n collectionId: string | undefined,\n mode: PersistedCollectionMode,\n schemaVersion: number | undefined,\n ): PersistedCollectionPersistence => {\n const adapter = getAdapterForCollection(collectionId, mode, schemaVersion)\n if (coordinator instanceof ElectronCollectionCoordinator) {\n if (collectionId === undefined) {\n coordinator.setAdapter(adapter)\n } else {\n coordinator.setAdapterForCollection(collectionId, adapter)\n }\n }\n return { adapter, coordinator }\n }\n\n const defaultPersistence = createCollectionPersistence(\n undefined,\n `sync-absent`,\n undefined,\n )\n if (coordinator instanceof ElectronCollectionCoordinator) {\n coordinator.setAdapter(defaultPersistence.adapter)\n }\n\n return {\n ...defaultPersistence,\n resolvePersistenceForCollection: ({ collectionId, mode, schemaVersion }) =>\n createCollectionPersistence(collectionId, mode, schemaVersion),\n // Backward compatible fallback for older callers.\n resolvePersistenceForMode: (mode) =>\n createCollectionPersistence(undefined, mode, undefined),\n }\n}\n"],"names":["InvalidPersistedCollectionConfigError","ELECTRON_PERSISTENCE_PROTOCOL_VERSION","DEFAULT_ELECTRON_PERSISTENCE_CHANNEL","resolvePersistedStorageTarget","SingleProcessCoordinator","ElectronCollectionCoordinator"],"mappings":"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"}
@@ -9,5 +9,7 @@ export type ElectronSQLitePersistenceOptions = {
9
9
  channel?: string;
10
10
  timeoutMs?: number;
11
11
  coordinator?: PersistedCollectionCoordinator;
12
+ /** Set false when a custom main-process adapter does not manage cache generations. */
13
+ managedCacheGenerations?: boolean;
12
14
  };
13
15
  export declare function createElectronSQLitePersistence(options: ElectronSQLitePersistenceOptions): PersistedCollectionPersistence;
package/dist/esm/main.js CHANGED
@@ -1,4 +1,4 @@
1
- import { InvalidPersistedCollectionConfigError } from "@tanstack/db-sqlite-persistence-core";
1
+ import { InvalidPersistedCollectionConfigError, resolvePersistedStorageTarget } from "@tanstack/db-sqlite-persistence-core";
2
2
  import { DEFAULT_ELECTRON_PERSISTENCE_CHANNEL, ELECTRON_PERSISTENCE_PROTOCOL_VERSION } from "./protocol.js";
3
3
  function serializeError(error) {
4
4
  const fallbackMessage = `Unknown electron persistence error`;
@@ -47,11 +47,25 @@ function assertValidRequest(request) {
47
47
  );
48
48
  }
49
49
  }
50
+ function requireStorageTarget(request) {
51
+ const target = request.storageTarget;
52
+ if (!target) {
53
+ throw new InvalidPersistedCollectionConfigError(
54
+ `Electron persistence data request requires an explicit storage target`
55
+ );
56
+ }
57
+ if (resolvePersistedStorageTarget(target).collectionId !== request.collectionId) {
58
+ throw new InvalidPersistedCollectionConfigError(
59
+ `Electron persistence storage target does not match its collectionId`
60
+ );
61
+ }
62
+ return target;
63
+ }
50
64
  async function executeRequestAgainstAdapter(request, adapter) {
51
65
  switch (request.method) {
52
66
  case `loadSubset`: {
53
67
  const result = await adapter.loadSubset(
54
- request.collectionId,
68
+ requireStorageTarget(request),
55
69
  request.payload.options,
56
70
  request.payload.ctx
57
71
  );
@@ -65,7 +79,7 @@ async function executeRequestAgainstAdapter(request, adapter) {
65
79
  }
66
80
  case `loadResumeSnapshot`: {
67
81
  const result = await adapter.loadResumeSnapshot(
68
- request.collectionId,
82
+ requireStorageTarget(request),
69
83
  request.payload.ctx
70
84
  );
71
85
  return {
@@ -82,7 +96,10 @@ async function executeRequestAgainstAdapter(request, adapter) {
82
96
  `loadCollectionMetadata is not supported by the configured electron persistence adapter`
83
97
  );
84
98
  }
85
- const result = await adapter.loadCollectionMetadata(request.collectionId);
99
+ const result = await adapter.loadCollectionMetadata(
100
+ requireStorageTarget(request),
101
+ request.payload.ctx
102
+ );
86
103
  return {
87
104
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
88
105
  requestId: request.requestId,
@@ -98,8 +115,9 @@ async function executeRequestAgainstAdapter(request, adapter) {
98
115
  );
99
116
  }
100
117
  const result = await adapter.scanRows(
101
- request.collectionId,
102
- request.payload.options
118
+ requireStorageTarget(request),
119
+ request.payload.options,
120
+ request.payload.ctx
103
121
  );
104
122
  return {
105
123
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
@@ -110,7 +128,10 @@ async function executeRequestAgainstAdapter(request, adapter) {
110
128
  };
111
129
  }
112
130
  case `applyCommittedTx`: {
113
- await adapter.applyCommittedTx(request.collectionId, request.payload.tx);
131
+ await adapter.applyCommittedTx(
132
+ requireStorageTarget(request),
133
+ request.payload.tx
134
+ );
114
135
  return {
115
136
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
116
137
  requestId: request.requestId,
@@ -121,7 +142,7 @@ async function executeRequestAgainstAdapter(request, adapter) {
121
142
  }
122
143
  case `reconcileCommittedTx`: {
123
144
  const result = adapter.reconcileCommittedTx ? await adapter.reconcileCommittedTx(
124
- request.collectionId,
145
+ requireStorageTarget(request),
125
146
  request.payload.tx,
126
147
  request.payload.anchor
127
148
  ) : { kind: `unknown` };
@@ -135,9 +156,10 @@ async function executeRequestAgainstAdapter(request, adapter) {
135
156
  }
136
157
  case `ensureIndex`: {
137
158
  await adapter.ensureIndex(
138
- request.collectionId,
159
+ requireStorageTarget(request),
139
160
  request.payload.signature,
140
- request.payload.spec
161
+ request.payload.spec,
162
+ request.payload.ctx
141
163
  );
142
164
  return {
143
165
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
@@ -154,8 +176,9 @@ async function executeRequestAgainstAdapter(request, adapter) {
154
176
  );
155
177
  }
156
178
  await adapter.markIndexRemoved(
157
- request.collectionId,
158
- request.payload.signature
179
+ requireStorageTarget(request),
180
+ request.payload.signature,
181
+ request.payload.ctx
159
182
  );
160
183
  return {
161
184
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
@@ -172,8 +195,9 @@ async function executeRequestAgainstAdapter(request, adapter) {
172
195
  );
173
196
  }
174
197
  const result = await adapter.pullSince(
175
- request.collectionId,
176
- request.payload.fromRowVersion
198
+ requireStorageTarget(request),
199
+ request.payload.fromRowVersion,
200
+ request.payload.ctx
177
201
  );
178
202
  return {
179
203
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
@@ -190,8 +214,9 @@ async function executeRequestAgainstAdapter(request, adapter) {
190
214
  );
191
215
  }
192
216
  const position = await adapter.reserveLeadershipTerm(
193
- request.collectionId,
194
- request.payload.observedTerm
217
+ requireStorageTarget(request),
218
+ request.payload.observedTerm,
219
+ request.payload.ctx
195
220
  );
196
221
  return {
197
222
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
@@ -207,7 +232,10 @@ async function executeRequestAgainstAdapter(request, adapter) {
207
232
  `getStreamPosition is not supported by the configured electron persistence adapter`
208
233
  );
209
234
  }
210
- const position = await adapter.getStreamPosition(request.collectionId);
235
+ const position = await adapter.getStreamPosition(
236
+ requireStorageTarget(request),
237
+ request.payload.ctx
238
+ );
211
239
  return {
212
240
  v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
213
241
  requestId: request.requestId,
@@ -216,13 +244,86 @@ async function executeRequestAgainstAdapter(request, adapter) {
216
244
  result: position
217
245
  };
218
246
  }
247
+ case `claimCacheGeneration`: {
248
+ if (!adapter.claimCacheGeneration) {
249
+ throw new InvalidPersistedCollectionConfigError(
250
+ `claimCacheGeneration is not supported by the configured electron persistence adapter`
251
+ );
252
+ }
253
+ const result = await adapter.claimCacheGeneration(request.collectionId);
254
+ return {
255
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
256
+ requestId: request.requestId,
257
+ method: request.method,
258
+ ok: true,
259
+ result
260
+ };
261
+ }
262
+ case `rotateCacheGeneration`: {
263
+ if (!adapter.rotateCacheGeneration) {
264
+ throw new InvalidPersistedCollectionConfigError(
265
+ `rotateCacheGeneration is not supported by the configured electron persistence adapter`
266
+ );
267
+ }
268
+ const result = await adapter.rotateCacheGeneration(
269
+ request.collectionId,
270
+ request.payload.claimId,
271
+ request.payload.resetMetadata,
272
+ request.payload.expectedStorageCollectionId
273
+ );
274
+ return {
275
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
276
+ requestId: request.requestId,
277
+ method: request.method,
278
+ ok: true,
279
+ result
280
+ };
281
+ }
282
+ case `renewCacheGenerationClaim`: {
283
+ if (!adapter.renewCacheGenerationClaim) {
284
+ throw new InvalidPersistedCollectionConfigError(
285
+ `renewCacheGenerationClaim is not supported by the configured electron persistence adapter`
286
+ );
287
+ }
288
+ const result = await adapter.renewCacheGenerationClaim(
289
+ request.payload.storageCollectionId,
290
+ request.payload.claimId
291
+ );
292
+ return {
293
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
294
+ requestId: request.requestId,
295
+ method: request.method,
296
+ ok: true,
297
+ result
298
+ };
299
+ }
300
+ case `releaseCacheGenerationClaim`: {
301
+ if (!adapter.releaseCacheGenerationClaim) {
302
+ throw new InvalidPersistedCollectionConfigError(
303
+ `releaseCacheGenerationClaim is not supported by the configured electron persistence adapter`
304
+ );
305
+ }
306
+ await adapter.releaseCacheGenerationClaim(request.payload.claimId);
307
+ return {
308
+ v: ELECTRON_PERSISTENCE_PROTOCOL_VERSION,
309
+ requestId: request.requestId,
310
+ method: request.method,
311
+ ok: true,
312
+ result: null
313
+ };
314
+ }
219
315
  }
220
316
  }
221
317
  function resolveModeAwarePersistence(persistence, request) {
318
+ if (request.storageTarget?.kind === `managed` && !request.resolution?.logicalCollectionId) {
319
+ throw new InvalidPersistedCollectionConfigError(
320
+ `Electron managed cache request requires a logical Collection ID`
321
+ );
322
+ }
222
323
  const mode = request.resolution?.mode ?? `sync-absent`;
223
324
  const schemaVersion = request.resolution?.schemaVersion;
224
325
  const collectionAwarePersistence = persistence.resolvePersistenceForCollection?.({
225
- collectionId: request.collectionId,
326
+ collectionId: request.resolution?.logicalCollectionId ?? request.collectionId,
226
327
  mode,
227
328
  schemaVersion
228
329
  });