@salesforce/lds-runtime-mobile 1.112.1 → 1.113.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import type { SqliteStorePlugin } from 'packages/lds-mobile-nimbus-plugins/dist';
1
+ import type { SqliteStorePlugin } from '@salesforce/nimbus-plugin-lds';
2
2
  import { AbstractKeyValueDataTable } from './AbstractKeyValueDataTable';
3
3
  export declare class LdsDraftIdMapDataTable extends AbstractKeyValueDataTable {
4
4
  constructor(plugin: SqliteStorePlugin);
@@ -1,4 +1,4 @@
1
- import type { SqliteStorePlugin } from 'packages/lds-mobile-nimbus-plugins/dist';
1
+ import type { SqliteStorePlugin } from '@salesforce/nimbus-plugin-lds';
2
2
  import { AbstractKeyValueDataTable } from './AbstractKeyValueDataTable';
3
3
  export declare class LdsDraftsDataTable extends AbstractKeyValueDataTable {
4
4
  constructor(plugin: SqliteStorePlugin);
package/dist/main.js CHANGED
@@ -15003,8 +15003,14 @@ class PrimingSession extends EventEmitter {
15003
15003
  }
15004
15004
  // pop any pending work off the queue
15005
15005
  const batches = typedBatches.splice(0, typedBatches.length);
15006
+ const before = Date.now();
15006
15007
  // fetch the metadata for the batches
15007
- const { availableBatches, unavailableTypes, unavailableIds } = await this.fetchMetadata(batches);
15008
+ const { availableBatches, unavailableTypes, unavailableIds, availableTypes } = await this.fetchMetadata(batches);
15009
+ this.emit('metadata-fetched', {
15010
+ duration: Date.now() - before,
15011
+ availableTypes,
15012
+ unavailableTypes,
15013
+ });
15008
15014
  if (unavailableIds.length > 0) {
15009
15015
  this.emit('error', {
15010
15016
  ids: unavailableIds,
@@ -15023,14 +15029,21 @@ class PrimingSession extends EventEmitter {
15023
15029
  // parallelizes batches of priming work
15024
15030
  enqueueBatches(batches) {
15025
15031
  for (const batch of batches) {
15032
+ const queuedTime = Date.now();
15026
15033
  this.networkWorkerPool.push({
15027
15034
  workFn: (abortController) => {
15035
+ const workTime = Date.now();
15036
+ this.emit('batch-starting', { queuedTime: workTime - queuedTime });
15028
15037
  return this.recordLoader
15029
15038
  .fetchRecordData(batch, abortController)
15030
15039
  .then(async (result) => {
15031
15040
  if (abortController.signal.aborted) {
15032
15041
  return;
15033
15042
  }
15043
+ this.emit('batch-fetched', {
15044
+ ids: batch.ids,
15045
+ duration: Date.now() - workTime,
15046
+ });
15034
15047
  if (result.ok === false) {
15035
15048
  const { error } = result;
15036
15049
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -15050,13 +15063,31 @@ class PrimingSession extends EventEmitter {
15050
15063
  });
15051
15064
  }
15052
15065
  const { records } = result;
15066
+ const beforeWrite = Date.now();
15053
15067
  // dispatch the write but DO NOT wait on it to unblock the network pool
15054
15068
  this.recordIngestor
15055
15069
  .insertRecords(records)
15056
- .then(({ written, conflicted }) => {
15070
+ .then(({ written, conflicted, errors }) => {
15071
+ this.emit('batch-written', {
15072
+ written,
15073
+ conflicted,
15074
+ errors: errors
15075
+ .map((e) => e.ids)
15076
+ .reduce((a, b) => a.concat(b), []),
15077
+ duration: Date.now() - beforeWrite,
15078
+ });
15057
15079
  if (abortController.signal.aborted) {
15058
15080
  return;
15059
15081
  }
15082
+ if (errors.length > 0) {
15083
+ errors.forEach(({ ids, message }) => {
15084
+ this.emit('error', {
15085
+ ids,
15086
+ code: 'unknown',
15087
+ message: message,
15088
+ });
15089
+ });
15090
+ }
15060
15091
  // now that the records are persisted, emit the primed event
15061
15092
  if (written.length > 0) {
15062
15093
  this.emit('primed', Array.from(written));
@@ -15089,6 +15120,7 @@ class PrimingSession extends EventEmitter {
15089
15120
  }, new Set()));
15090
15121
  const objectInfoMap = await this.objectInfoLoader.getObjectInfos(apiNames);
15091
15122
  const unavailableTypes = apiNames.filter((x) => !objectInfoMap[x]);
15123
+ const availableTypes = apiNames.filter((x) => objectInfoMap[x]);
15092
15124
  const unavilableBatches = batches.filter((x) => unavailableTypes.includes(x.type));
15093
15125
  const availableBatches = batches.filter((x) => !unavailableTypes.includes(x.type));
15094
15126
  const unavailableIds = unavilableBatches.reduce((acc, x) => {
@@ -15096,9 +15128,11 @@ class PrimingSession extends EventEmitter {
15096
15128
  return acc;
15097
15129
  }, []);
15098
15130
  return {
15131
+ apiNames,
15099
15132
  availableBatches,
15100
15133
  unavilableBatches,
15101
15134
  unavailableTypes,
15135
+ availableTypes,
15102
15136
  unavailableIds,
15103
15137
  };
15104
15138
  }
@@ -15258,45 +15292,30 @@ class RecordIngestor {
15258
15292
  */
15259
15293
  async insertRecords(syntheticRecords) {
15260
15294
  if (syntheticRecords.length === 0) {
15261
- return { written: [], conflicted: [] };
15295
+ return { written: [], conflicted: [], errors: [] };
15262
15296
  }
15263
15297
  const luvio = this.getLuvio();
15264
15298
  const ingestionTimestamp = Date.now();
15265
15299
  const ttlOverride = await luvio.storeGetTTLOverride(UiApiNamespace, RecordRepresentationType);
15266
- const metadata = JSON.stringify({
15300
+ const metadata = {
15267
15301
  ingestionTimestamp,
15268
15302
  expirationTimestamp: (ttlOverride !== null && ttlOverride !== void 0 ? ttlOverride : RecordRepresentationTTL) + ingestionTimestamp,
15269
15303
  namespace: UiApiNamespace,
15270
15304
  representationName: RecordRepresentationType,
15271
15305
  metadataVersion: DURABLE_METADATA_VERSION,
15272
15306
  version: RecordRepresentationVersion,
15273
- });
15274
- const idsToPrime = new Set();
15275
- const written = [];
15276
- const rows = syntheticRecords.map((record) => {
15277
- idsToPrime.add(record.id);
15278
- return `('${keyBuilderRecord(luvio, { recordId: record.id })}', '${JSON.stringify(record)}', '${metadata}')`;
15279
- });
15280
- const dml = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${rows.join(',\n')} returning key;`;
15281
- const result = await this.store.query(dml, []);
15282
- for (const row of result.rows) {
15283
- const id = extractRecordIdFromStoreKey(row[0]);
15284
- if (id) {
15285
- written.push(id);
15286
- idsToPrime.delete(id);
15287
- }
15288
- }
15289
- return { written: Array.from(written), conflicted: Array.from(idsToPrime) };
15307
+ };
15308
+ return this.store.writeRecords(syntheticRecords.map((record) => ({ record, metadata })));
15290
15309
  }
15291
15310
  }
15292
15311
 
15293
15312
  function instrumentPrimingSession(session) {
15294
15313
  reportPrimingSessionCreated();
15295
- session.on('error', (payload) => {
15296
- reportPrimingError(payload.code, payload.ids.length);
15314
+ session.on('error', ({ code, ids }) => {
15315
+ reportPrimingError(code, ids.length);
15297
15316
  });
15298
- session.on('primed', (payload) => {
15299
- reportPrimingSuccess(payload.length);
15317
+ session.on('primed', ({ length }) => {
15318
+ reportPrimingSuccess(length);
15300
15319
  });
15301
15320
  return session;
15302
15321
  }
@@ -15349,11 +15368,78 @@ class NimbusPrimingNetworkAdapter {
15349
15368
  }
15350
15369
  }
15351
15370
 
15371
+ // ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
15372
+ const SQLITE_MAX_VARIABLE_NUMBER = 999;
15373
+ const PARAMS_PER_RECORD = 3;
15374
+ // We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
15375
+ const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
15376
+ class SqlitePrimingStore {
15377
+ constructor(getLuvio, store) {
15378
+ this.getLuvio = getLuvio;
15379
+ this.store = store;
15380
+ }
15381
+ async writeRecords(records) {
15382
+ const batches = batchArray(records);
15383
+ const writeResult = { written: [], conflicted: [], errors: [] };
15384
+ return (await Promise.all(batches.map((batch) => this.writeBatch(batch)))).reduce((acc, curr) => {
15385
+ acc.written.push(...curr.written);
15386
+ acc.conflicted.push(...curr.conflicted);
15387
+ acc.errors.push(...curr.errors);
15388
+ return acc;
15389
+ }, writeResult);
15390
+ }
15391
+ async writeBatch(records) {
15392
+ const idsToPrime = new Set();
15393
+ const written = [];
15394
+ const statement = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${records
15395
+ .map((_) => `(?,?,?)`)
15396
+ .join(',')} returning key;`;
15397
+ const params = [];
15398
+ records.forEach(({ record, metadata }) => {
15399
+ idsToPrime.add(record.id);
15400
+ const key = keyBuilderRecord(this.getLuvio(), { recordId: record.id });
15401
+ params.push(key);
15402
+ params.push(JSON.stringify(record));
15403
+ params.push(JSON.stringify(metadata));
15404
+ });
15405
+ try {
15406
+ const result = await this.store.query(statement, params);
15407
+ for (const row of result.rows) {
15408
+ const id = extractRecordIdFromStoreKey(row[0]);
15409
+ if (id) {
15410
+ written.push(id);
15411
+ idsToPrime.delete(id);
15412
+ }
15413
+ }
15414
+ return { written, conflicted: Array.from(idsToPrime), errors: [] };
15415
+ }
15416
+ catch (e) {
15417
+ return {
15418
+ written: [],
15419
+ conflicted: [],
15420
+ errors: [{ ids: Array.from(idsToPrime), message: e }],
15421
+ };
15422
+ }
15423
+ }
15424
+ }
15425
+ function batchArray(arr, batchSize = BATCH_SIZE) {
15426
+ const batches = [];
15427
+ // If the array length is less than or equal to the batch size, return the array as a single batch
15428
+ if (arr.length <= batchSize) {
15429
+ return [arr];
15430
+ }
15431
+ // Split the array into batches of size batchSize
15432
+ for (let i = 0; i < arr.length; i += batchSize) {
15433
+ batches.push(arr.slice(i, i + batchSize));
15434
+ }
15435
+ return batches;
15436
+ }
15437
+
15352
15438
  function primingSessionFactory(config) {
15353
15439
  const { store, objectInfoService, getLuvio } = config;
15354
15440
  const networkAdapter = new NimbusPrimingNetworkAdapter();
15355
15441
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
15356
- const recordIngestor = new RecordIngestor(store, getLuvio);
15442
+ const recordIngestor = new RecordIngestor(new SqlitePrimingStore(getLuvio, store), getLuvio);
15357
15443
  const session = new PrimingSession({
15358
15444
  recordLoader,
15359
15445
  recordIngestor,
@@ -15499,4 +15585,4 @@ register({
15499
15585
  });
15500
15586
 
15501
15587
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15502
- // version: 1.112.1-1a0875b1d
15588
+ // version: 1.113.0-a8af103c9
@@ -0,0 +1 @@
1
+ export { MockNimbusBinaryStorePlugin, mockNimbusBinaryStorePlugin, } from './__tests__/MockNimbusBinaryStorePlugin.js';
@@ -0,0 +1,10 @@
1
+ import type { PrimingStore, RecordWithMetadata, WriteResult } from '@salesforce/lds-priming';
2
+ import type { Luvio } from '@luvio/engine';
3
+ import type { SqliteStore } from '@salesforce/lds-store-sql';
4
+ export declare class SqlitePrimingStore implements PrimingStore {
5
+ private readonly getLuvio;
6
+ private readonly store;
7
+ constructor(getLuvio: () => Luvio, store: SqliteStore);
8
+ writeRecords(records: RecordWithMetadata[]): Promise<WriteResult>;
9
+ private writeBatch;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.112.1",
3
+ "version": "1.113.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -32,10 +32,10 @@
32
32
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-mobile"
33
33
  },
34
34
  "dependencies": {
35
- "@salesforce/lds-adapters-uiapi": "^1.112.1",
36
- "@salesforce/lds-bindings": "^1.112.1",
37
- "@salesforce/lds-instrumentation": "^1.112.1",
38
- "@salesforce/lds-priming": "^1.112.1",
35
+ "@salesforce/lds-adapters-uiapi": "^1.113.0",
36
+ "@salesforce/lds-bindings": "^1.113.0",
37
+ "@salesforce/lds-instrumentation": "^1.113.0",
38
+ "@salesforce/lds-priming": "^1.113.0",
39
39
  "@salesforce/user": "0.0.12",
40
40
  "o11y": "244.0.0"
41
41
  },
@@ -43,16 +43,16 @@
43
43
  "@luvio/engine": "0.137.1",
44
44
  "@luvio/environments": "0.137.1",
45
45
  "@luvio/graphql-parser": "0.137.1",
46
- "@salesforce/lds-adapters-graphql": "^1.112.1",
47
- "@salesforce/lds-drafts": "^1.112.1",
48
- "@salesforce/lds-drafts-adapters-uiapi": "^1.112.1",
49
- "@salesforce/lds-graphql-eval": "^1.112.1",
50
- "@salesforce/lds-network-adapter": "^1.112.1",
51
- "@salesforce/lds-network-nimbus": "^1.112.1",
52
- "@salesforce/lds-store-binary": "^1.112.1",
53
- "@salesforce/lds-store-sql": "^1.112.1",
54
- "@salesforce/lds-utils-adapters": "^1.112.1",
55
- "@salesforce/nimbus-plugin-lds": "^1.112.1",
46
+ "@salesforce/lds-adapters-graphql": "^1.113.0",
47
+ "@salesforce/lds-drafts": "^1.113.0",
48
+ "@salesforce/lds-drafts-adapters-uiapi": "^1.113.0",
49
+ "@salesforce/lds-graphql-eval": "^1.113.0",
50
+ "@salesforce/lds-network-adapter": "^1.113.0",
51
+ "@salesforce/lds-network-nimbus": "^1.113.0",
52
+ "@salesforce/lds-store-binary": "^1.113.0",
53
+ "@salesforce/lds-store-sql": "^1.113.0",
54
+ "@salesforce/lds-utils-adapters": "^1.113.0",
55
+ "@salesforce/nimbus-plugin-lds": "^1.113.0",
56
56
  "babel-plugin-dynamic-import-node": "^2.3.3",
57
57
  "wait-for-expect": "^3.0.2"
58
58
  },
@@ -1,4 +1,4 @@
1
- import type { SqliteStorePlugin } from 'packages/lds-mobile-nimbus-plugins/dist';
1
+ import type { SqliteStorePlugin } from '@salesforce/nimbus-plugin-lds';
2
2
  import { AbstractKeyValueDataTable } from './AbstractKeyValueDataTable';
3
3
  export declare class LdsDraftIdMapDataTable extends AbstractKeyValueDataTable {
4
4
  constructor(plugin: SqliteStorePlugin);
@@ -1,4 +1,4 @@
1
- import type { SqliteStorePlugin } from 'packages/lds-mobile-nimbus-plugins/dist';
1
+ import type { SqliteStorePlugin } from '@salesforce/nimbus-plugin-lds';
2
2
  import { AbstractKeyValueDataTable } from './AbstractKeyValueDataTable';
3
3
  export declare class LdsDraftsDataTable extends AbstractKeyValueDataTable {
4
4
  constructor(plugin: SqliteStorePlugin);
package/sfdc/main.js CHANGED
@@ -15003,8 +15003,14 @@ class PrimingSession extends EventEmitter {
15003
15003
  }
15004
15004
  // pop any pending work off the queue
15005
15005
  const batches = typedBatches.splice(0, typedBatches.length);
15006
+ const before = Date.now();
15006
15007
  // fetch the metadata for the batches
15007
- const { availableBatches, unavailableTypes, unavailableIds } = await this.fetchMetadata(batches);
15008
+ const { availableBatches, unavailableTypes, unavailableIds, availableTypes } = await this.fetchMetadata(batches);
15009
+ this.emit('metadata-fetched', {
15010
+ duration: Date.now() - before,
15011
+ availableTypes,
15012
+ unavailableTypes,
15013
+ });
15008
15014
  if (unavailableIds.length > 0) {
15009
15015
  this.emit('error', {
15010
15016
  ids: unavailableIds,
@@ -15023,14 +15029,21 @@ class PrimingSession extends EventEmitter {
15023
15029
  // parallelizes batches of priming work
15024
15030
  enqueueBatches(batches) {
15025
15031
  for (const batch of batches) {
15032
+ const queuedTime = Date.now();
15026
15033
  this.networkWorkerPool.push({
15027
15034
  workFn: (abortController) => {
15035
+ const workTime = Date.now();
15036
+ this.emit('batch-starting', { queuedTime: workTime - queuedTime });
15028
15037
  return this.recordLoader
15029
15038
  .fetchRecordData(batch, abortController)
15030
15039
  .then(async (result) => {
15031
15040
  if (abortController.signal.aborted) {
15032
15041
  return;
15033
15042
  }
15043
+ this.emit('batch-fetched', {
15044
+ ids: batch.ids,
15045
+ duration: Date.now() - workTime,
15046
+ });
15034
15047
  if (result.ok === false) {
15035
15048
  const { error } = result;
15036
15049
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -15050,13 +15063,31 @@ class PrimingSession extends EventEmitter {
15050
15063
  });
15051
15064
  }
15052
15065
  const { records } = result;
15066
+ const beforeWrite = Date.now();
15053
15067
  // dispatch the write but DO NOT wait on it to unblock the network pool
15054
15068
  this.recordIngestor
15055
15069
  .insertRecords(records)
15056
- .then(({ written, conflicted }) => {
15070
+ .then(({ written, conflicted, errors }) => {
15071
+ this.emit('batch-written', {
15072
+ written,
15073
+ conflicted,
15074
+ errors: errors
15075
+ .map((e) => e.ids)
15076
+ .reduce((a, b) => a.concat(b), []),
15077
+ duration: Date.now() - beforeWrite,
15078
+ });
15057
15079
  if (abortController.signal.aborted) {
15058
15080
  return;
15059
15081
  }
15082
+ if (errors.length > 0) {
15083
+ errors.forEach(({ ids, message }) => {
15084
+ this.emit('error', {
15085
+ ids,
15086
+ code: 'unknown',
15087
+ message: message,
15088
+ });
15089
+ });
15090
+ }
15060
15091
  // now that the records are persisted, emit the primed event
15061
15092
  if (written.length > 0) {
15062
15093
  this.emit('primed', Array.from(written));
@@ -15089,6 +15120,7 @@ class PrimingSession extends EventEmitter {
15089
15120
  }, new Set()));
15090
15121
  const objectInfoMap = await this.objectInfoLoader.getObjectInfos(apiNames);
15091
15122
  const unavailableTypes = apiNames.filter((x) => !objectInfoMap[x]);
15123
+ const availableTypes = apiNames.filter((x) => objectInfoMap[x]);
15092
15124
  const unavilableBatches = batches.filter((x) => unavailableTypes.includes(x.type));
15093
15125
  const availableBatches = batches.filter((x) => !unavailableTypes.includes(x.type));
15094
15126
  const unavailableIds = unavilableBatches.reduce((acc, x) => {
@@ -15096,9 +15128,11 @@ class PrimingSession extends EventEmitter {
15096
15128
  return acc;
15097
15129
  }, []);
15098
15130
  return {
15131
+ apiNames,
15099
15132
  availableBatches,
15100
15133
  unavilableBatches,
15101
15134
  unavailableTypes,
15135
+ availableTypes,
15102
15136
  unavailableIds,
15103
15137
  };
15104
15138
  }
@@ -15258,45 +15292,30 @@ class RecordIngestor {
15258
15292
  */
15259
15293
  async insertRecords(syntheticRecords) {
15260
15294
  if (syntheticRecords.length === 0) {
15261
- return { written: [], conflicted: [] };
15295
+ return { written: [], conflicted: [], errors: [] };
15262
15296
  }
15263
15297
  const luvio = this.getLuvio();
15264
15298
  const ingestionTimestamp = Date.now();
15265
15299
  const ttlOverride = await luvio.storeGetTTLOverride(UiApiNamespace, RecordRepresentationType);
15266
- const metadata = JSON.stringify({
15300
+ const metadata = {
15267
15301
  ingestionTimestamp,
15268
15302
  expirationTimestamp: (ttlOverride !== null && ttlOverride !== void 0 ? ttlOverride : RecordRepresentationTTL) + ingestionTimestamp,
15269
15303
  namespace: UiApiNamespace,
15270
15304
  representationName: RecordRepresentationType,
15271
15305
  metadataVersion: DURABLE_METADATA_VERSION,
15272
15306
  version: RecordRepresentationVersion,
15273
- });
15274
- const idsToPrime = new Set();
15275
- const written = [];
15276
- const rows = syntheticRecords.map((record) => {
15277
- idsToPrime.add(record.id);
15278
- return `('${keyBuilderRecord(luvio, { recordId: record.id })}', '${JSON.stringify(record)}', '${metadata}')`;
15279
- });
15280
- const dml = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${rows.join(',\n')} returning key;`;
15281
- const result = await this.store.query(dml, []);
15282
- for (const row of result.rows) {
15283
- const id = extractRecordIdFromStoreKey(row[0]);
15284
- if (id) {
15285
- written.push(id);
15286
- idsToPrime.delete(id);
15287
- }
15288
- }
15289
- return { written: Array.from(written), conflicted: Array.from(idsToPrime) };
15307
+ };
15308
+ return this.store.writeRecords(syntheticRecords.map((record) => ({ record, metadata })));
15290
15309
  }
15291
15310
  }
15292
15311
 
15293
15312
  function instrumentPrimingSession(session) {
15294
15313
  reportPrimingSessionCreated();
15295
- session.on('error', (payload) => {
15296
- reportPrimingError(payload.code, payload.ids.length);
15314
+ session.on('error', ({ code, ids }) => {
15315
+ reportPrimingError(code, ids.length);
15297
15316
  });
15298
- session.on('primed', (payload) => {
15299
- reportPrimingSuccess(payload.length);
15317
+ session.on('primed', ({ length }) => {
15318
+ reportPrimingSuccess(length);
15300
15319
  });
15301
15320
  return session;
15302
15321
  }
@@ -15349,11 +15368,78 @@ class NimbusPrimingNetworkAdapter {
15349
15368
  }
15350
15369
  }
15351
15370
 
15371
+ // ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
15372
+ const SQLITE_MAX_VARIABLE_NUMBER = 999;
15373
+ const PARAMS_PER_RECORD = 3;
15374
+ // We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
15375
+ const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
15376
+ class SqlitePrimingStore {
15377
+ constructor(getLuvio, store) {
15378
+ this.getLuvio = getLuvio;
15379
+ this.store = store;
15380
+ }
15381
+ async writeRecords(records) {
15382
+ const batches = batchArray(records);
15383
+ const writeResult = { written: [], conflicted: [], errors: [] };
15384
+ return (await Promise.all(batches.map((batch) => this.writeBatch(batch)))).reduce((acc, curr) => {
15385
+ acc.written.push(...curr.written);
15386
+ acc.conflicted.push(...curr.conflicted);
15387
+ acc.errors.push(...curr.errors);
15388
+ return acc;
15389
+ }, writeResult);
15390
+ }
15391
+ async writeBatch(records) {
15392
+ const idsToPrime = new Set();
15393
+ const written = [];
15394
+ const statement = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${records
15395
+ .map((_) => `(?,?,?)`)
15396
+ .join(',')} returning key;`;
15397
+ const params = [];
15398
+ records.forEach(({ record, metadata }) => {
15399
+ idsToPrime.add(record.id);
15400
+ const key = keyBuilderRecord(this.getLuvio(), { recordId: record.id });
15401
+ params.push(key);
15402
+ params.push(JSON.stringify(record));
15403
+ params.push(JSON.stringify(metadata));
15404
+ });
15405
+ try {
15406
+ const result = await this.store.query(statement, params);
15407
+ for (const row of result.rows) {
15408
+ const id = extractRecordIdFromStoreKey(row[0]);
15409
+ if (id) {
15410
+ written.push(id);
15411
+ idsToPrime.delete(id);
15412
+ }
15413
+ }
15414
+ return { written, conflicted: Array.from(idsToPrime), errors: [] };
15415
+ }
15416
+ catch (e) {
15417
+ return {
15418
+ written: [],
15419
+ conflicted: [],
15420
+ errors: [{ ids: Array.from(idsToPrime), message: e }],
15421
+ };
15422
+ }
15423
+ }
15424
+ }
15425
+ function batchArray(arr, batchSize = BATCH_SIZE) {
15426
+ const batches = [];
15427
+ // If the array length is less than or equal to the batch size, return the array as a single batch
15428
+ if (arr.length <= batchSize) {
15429
+ return [arr];
15430
+ }
15431
+ // Split the array into batches of size batchSize
15432
+ for (let i = 0; i < arr.length; i += batchSize) {
15433
+ batches.push(arr.slice(i, i + batchSize));
15434
+ }
15435
+ return batches;
15436
+ }
15437
+
15352
15438
  function primingSessionFactory(config) {
15353
15439
  const { store, objectInfoService, getLuvio } = config;
15354
15440
  const networkAdapter = new NimbusPrimingNetworkAdapter();
15355
15441
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
15356
- const recordIngestor = new RecordIngestor(store, getLuvio);
15442
+ const recordIngestor = new RecordIngestor(new SqlitePrimingStore(getLuvio, store), getLuvio);
15357
15443
  const session = new PrimingSession({
15358
15444
  recordLoader,
15359
15445
  recordIngestor,
@@ -15499,4 +15585,4 @@ register({
15499
15585
  });
15500
15586
 
15501
15587
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15502
- // version: 1.112.1-1a0875b1d
15588
+ // version: 1.113.0-a8af103c9
@@ -0,0 +1 @@
1
+ export { MockNimbusBinaryStorePlugin, mockNimbusBinaryStorePlugin, } from './__tests__/MockNimbusBinaryStorePlugin.js';
@@ -0,0 +1,10 @@
1
+ import type { PrimingStore, RecordWithMetadata, WriteResult } from '@salesforce/lds-priming';
2
+ import type { Luvio } from '@luvio/engine';
3
+ import type { SqliteStore } from '@salesforce/lds-store-sql';
4
+ export declare class SqlitePrimingStore implements PrimingStore {
5
+ private readonly getLuvio;
6
+ private readonly store;
7
+ constructor(getLuvio: () => Luvio, store: SqliteStore);
8
+ writeRecords(records: RecordWithMetadata[]): Promise<WriteResult>;
9
+ private writeBatch;
10
+ }