@twin.org/entity-storage-connector-file 0.9.1 → 0.9.2-next.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.
@@ -2,8 +2,9 @@
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { access, mkdir, readFile, rename, rm, statfs, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
+ import { HealthCategory, HealthStatus } from "@twin.org/api-models";
5
6
  import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
6
- import { BaseError, Coerce, ComponentFactory, GeneralError, Guards, HealthStatus, Is, Mutex, ObjectHelper, Validation } from "@twin.org/core";
7
+ import { BaseError, Coerce, ComponentFactory, ConflictError, GeneralError, Guards, Is, Mutex, ObjectHelper, Validation } from "@twin.org/core";
7
8
  import { ComparisonOperator, EntityConditions, EntitySchemaFactory, EntitySchemaHelper, EntitySorter, LogicalOperator } from "@twin.org/entity";
8
9
  import { EntityStorageHelper } from "@twin.org/entity-storage-models";
9
10
  /**
@@ -54,6 +55,11 @@ export class FileEntityStorageConnector {
54
55
  * @internal
55
56
  */
56
57
  _primaryKey;
58
+ /**
59
+ * The name of the version property, if any.
60
+ * @internal
61
+ */
62
+ _versionKey;
57
63
  /**
58
64
  * The directory to use for storage.
59
65
  * @internal
@@ -87,6 +93,7 @@ export class FileEntityStorageConnector {
87
93
  this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
88
94
  this._partitionContextIds = options.partitionContextIds;
89
95
  this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
96
+ this._versionKey = EntitySchemaHelper.findVersionProperty(this._entitySchema);
90
97
  this._directory = path.resolve(options.config.directory);
91
98
  this._diskErrorThresholdBytes =
92
99
  options.config.diskErrorThresholdBytes ??
@@ -157,7 +164,7 @@ export class FileEntityStorageConnector {
157
164
  }
158
165
  /**
159
166
  * Returns the health status of the component.
160
- * @returns The health status of the component, can return multiple entries for elements within the component.
167
+ * @returns The health status of the component.
161
168
  */
162
169
  async health() {
163
170
  try {
@@ -167,6 +174,7 @@ export class FileEntityStorageConnector {
167
174
  return [
168
175
  {
169
176
  source: FileEntityStorageConnector.CLASS_NAME,
177
+ category: HealthCategory.Connectivity,
170
178
  status: HealthStatus.Error,
171
179
  description: "healthDescription",
172
180
  message: "diskSpaceError",
@@ -182,6 +190,7 @@ export class FileEntityStorageConnector {
182
190
  return [
183
191
  {
184
192
  source: FileEntityStorageConnector.CLASS_NAME,
193
+ category: HealthCategory.Connectivity,
185
194
  status: HealthStatus.Warning,
186
195
  description: "healthDescription",
187
196
  message: "diskSpaceWarning",
@@ -196,6 +205,7 @@ export class FileEntityStorageConnector {
196
205
  return [
197
206
  {
198
207
  source: FileEntityStorageConnector.CLASS_NAME,
208
+ category: HealthCategory.Connectivity,
199
209
  status: HealthStatus.Ok,
200
210
  description: "healthDescription",
201
211
  data: { directory: this._directory, freeBytes }
@@ -206,6 +216,7 @@ export class FileEntityStorageConnector {
206
216
  return [
207
217
  {
208
218
  source: FileEntityStorageConnector.CLASS_NAME,
219
+ category: HealthCategory.Connectivity,
209
220
  status: HealthStatus.Error,
210
221
  description: "healthDescription",
211
222
  message: "diskSpaceCheckFailed",
@@ -234,7 +245,7 @@ export class FileEntityStorageConnector {
234
245
  const contextIds = await ContextIdStore.getContextIds();
235
246
  const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
236
247
  const store = await this.readStoreWithLock();
237
- const finalConditions = conditions ?? [];
248
+ const finalConditions = conditions ? [...conditions] : [];
238
249
  if (Is.stringValue(partitionKey)) {
239
250
  finalConditions.push({
240
251
  property: FileEntityStorageConnector._PARTITION_KEY,
@@ -255,29 +266,59 @@ export class FileEntityStorageConnector {
255
266
  * @param entity The entity to set.
256
267
  * @param conditions The optional conditions to match for the entities.
257
268
  * @returns The id of the entity.
269
+ * @throws ConflictError when the entity exists but the supplied conditions or version do not match the stored state.
258
270
  */
259
271
  async set(entity, conditions) {
260
272
  Guards.object(FileEntityStorageConnector.CLASS_NAME, "entity", entity);
261
273
  EntityStorageHelper.validateConditions(this._entitySchema, conditions);
262
274
  const contextIds = await ContextIdStore.getContextIds();
263
275
  const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
276
+ const submittedVersion = Is.stringValue(this._versionKey)
277
+ ? ObjectHelper.propertyGet(entity, this._versionKey)
278
+ : undefined;
279
+ const hasVersionCheck = !Is.empty(this._versionKey) && !Is.empty(submittedVersion) && submittedVersion > 0;
264
280
  const prepared = EntityStorageHelper.prepareEntity(entity, this._entitySchema, Is.stringValue(partitionKey)
265
281
  ? [{ property: FileEntityStorageConnector._PARTITION_KEY, value: partitionKey }]
266
282
  : undefined, { nullBehavior: "omit" });
267
283
  await this.withLock(async () => {
268
284
  const store = await this.readStore();
269
- const finalConditions = conditions ?? [];
285
+ const baseConditions = [];
270
286
  if (Is.stringValue(partitionKey)) {
271
- finalConditions.push({
287
+ baseConditions.push({
272
288
  property: FileEntityStorageConnector._PARTITION_KEY,
273
289
  value: partitionKey
274
290
  });
275
291
  }
276
- const existingIndex = this.findItem(store, prepared[this._primaryKey.property], undefined, finalConditions);
292
+ const fullConditions = [
293
+ ...baseConditions,
294
+ ...(conditions ?? [])
295
+ ];
296
+ if (hasVersionCheck) {
297
+ fullConditions.push({ property: this._versionKey, value: submittedVersion });
298
+ }
299
+ const entityId = prepared[this._primaryKey.property];
300
+ const existingIndex = this.findItem(store, entityId, undefined, fullConditions);
277
301
  if (existingIndex >= 0) {
302
+ if (Is.stringValue(this._versionKey)) {
303
+ const storedVersion = ObjectHelper.propertyGet(store[existingIndex], this._versionKey) ?? 0;
304
+ ObjectHelper.propertySet(prepared, this._versionKey, storedVersion + 1);
305
+ }
278
306
  store[existingIndex] = prepared;
279
307
  }
280
308
  else {
309
+ const existsIndex = this.findItem(store, entityId, undefined, baseConditions);
310
+ if (existsIndex >= 0) {
311
+ if (Is.stringValue(this._versionKey)) {
312
+ if (hasVersionCheck) {
313
+ throw new ConflictError(FileEntityStorageConnector.CLASS_NAME, "optimisticLockFailed", entityId);
314
+ }
315
+ throw new ConflictError(FileEntityStorageConnector.CLASS_NAME, "conditionFailed", entityId);
316
+ }
317
+ return;
318
+ }
319
+ if (Is.stringValue(this._versionKey)) {
320
+ ObjectHelper.propertySet(prepared, this._versionKey, 1);
321
+ }
281
322
  store.push(prepared);
282
323
  }
283
324
  await this.writeStore(store);
@@ -404,6 +445,7 @@ export class FileEntityStorageConnector {
404
445
  * @param id The id of the entity to remove.
405
446
  * @param conditions The optional conditions to match for the entities.
406
447
  * @returns Nothing.
448
+ * @throws ConflictError when the entity exists but the supplied conditions do not match the stored state.
407
449
  */
408
450
  async remove(id, conditions) {
409
451
  Guards.stringValue(FileEntityStorageConnector.CLASS_NAME, "id", id);
@@ -412,18 +454,28 @@ export class FileEntityStorageConnector {
412
454
  const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
413
455
  await this.withLock(async () => {
414
456
  const store = await this.readStore();
415
- const finalConditions = conditions ?? [];
457
+ const baseConditions = [];
416
458
  if (Is.stringValue(partitionKey)) {
417
- finalConditions.push({
459
+ baseConditions.push({
418
460
  property: FileEntityStorageConnector._PARTITION_KEY,
419
461
  value: partitionKey
420
462
  });
421
463
  }
422
- const index = this.findItem(store, id, undefined, finalConditions);
464
+ const fullConditions = [
465
+ ...baseConditions,
466
+ ...(conditions ?? [])
467
+ ];
468
+ const index = this.findItem(store, id, undefined, fullConditions);
423
469
  if (index >= 0) {
424
470
  store.splice(index, 1);
425
471
  await this.writeStore(store);
426
472
  }
473
+ else if (Is.arrayValue(conditions)) {
474
+ const existsIndex = this.findItem(store, id, undefined, baseConditions);
475
+ if (existsIndex >= 0 && this._versionKey) {
476
+ throw new ConflictError(FileEntityStorageConnector.CLASS_NAME, "conditionFailed", id);
477
+ }
478
+ }
427
479
  });
428
480
  }
429
481
  /**
@@ -521,11 +573,21 @@ export class FileEntityStorageConnector {
521
573
  }
522
574
  return store.filter(item => EntityConditions.check(item, finalConditions)).length;
523
575
  }
576
+ /**
577
+ * Get the connector implementation version.
578
+ * @returns The connector implementation version.
579
+ */
580
+ connectorVersion() {
581
+ return 0;
582
+ }
524
583
  /**
525
584
  * Get a unique list of all the context ids from the storage.
526
585
  * @returns The list of unique context ids.
527
586
  */
528
587
  async getPartitionContextIds() {
588
+ if (!Is.arrayValue(this._partitionContextIds)) {
589
+ return undefined;
590
+ }
529
591
  const contextIds = {};
530
592
  const store = await this.readStoreWithLock();
531
593
  for (const entity of store) {
@@ -592,7 +654,7 @@ export class FileEntityStorageConnector {
592
654
  /**
593
655
  * Read the store from file while holding the directory mutex.
594
656
  * Use this for standalone reads (get, query, count, getPartitionContextIds) where
595
- * no outer lock is held. Do not call from inside a withLock callback
657
+ * no outer lock is held. Do not call from inside a withLock callback -
596
658
  * use readStore instead to avoid a re-entrant deadlock.
597
659
  * @returns The store.
598
660
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"fileEntityStorageConnector.js","sourceRoot":"","sources":["../../src/fileEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { access, mkdir, readFile, rename, rm, statfs, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { ContextIdHelper, ContextIdStore, type IContextIds } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tHealthStatus,\n\ttype IHealth,\n\tIs,\n\ttype IValidationFailure,\n\tMutex,\n\tObjectHelper,\n\tValidation\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntityConditions,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tEntitySorter,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty,\n\tLogicalOperator,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport {\n\tEntityStorageHelper,\n\ttype IEntityStorageConnector,\n\ttype IEntityStorageMigrationConnector,\n\ttype IMigrationOptions\n} from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IFileEntityStorageConnectorConstructorOptions } from \"./models/IFileEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations in file.\n */\nexport class FileEntityStorageConnector<\n\tT = unknown\n> implements IEntityStorageMigrationConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FileEntityStorageConnector>();\n\n\t/**\n\t * Default limit for number of items to return.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 20;\n\n\t/**\n\t * Partition key for the operation.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * Default disk space warning threshold: 500 MB.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_DISK_WARNING_THRESHOLD_BYTES: number = 500 * 1024 * 1024;\n\n\t/**\n\t * Default disk space error threshold: 100 MB.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_DISK_ERROR_THRESHOLD_BYTES: number = 100 * 1024 * 1024;\n\n\t/**\n\t * The name for the schema.\n\t * @internal\n\t */\n\tprivate readonly _entitySchemaName: string;\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprivate readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The primary key.\n\t * @internal\n\t */\n\tprivate readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * The directory to use for storage.\n\t * @internal\n\t */\n\tprivate readonly _directory: string;\n\n\t/**\n\t * Free bytes below which health reports an error.\n\t * @internal\n\t */\n\tprivate readonly _diskErrorThresholdBytes: number;\n\n\t/**\n\t * Free bytes below which health reports a warning.\n\t * @internal\n\t */\n\tprivate readonly _diskWarningThresholdBytes: number;\n\n\t/**\n\t * Milliseconds to wait for the directory lock before throwing.\n\t * @internal\n\t */\n\tprivate readonly _mutexTimeoutMs?: number;\n\n\t/**\n\t * Create a new instance of FileEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IFileEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(FileEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object(FileEntityStorageConnector.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.directory),\n\t\t\toptions.config.directory\n\t\t);\n\t\tthis._entitySchemaName = options.entitySchema;\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey<T>(this._entitySchema);\n\t\tthis._directory = path.resolve(options.config.directory);\n\t\tthis._diskErrorThresholdBytes =\n\t\t\toptions.config.diskErrorThresholdBytes ??\n\t\t\tFileEntityStorageConnector._DEFAULT_DISK_ERROR_THRESHOLD_BYTES;\n\t\tthis._diskWarningThresholdBytes =\n\t\t\toptions.config.diskWarningThresholdBytes ??\n\t\t\tFileEntityStorageConnector._DEFAULT_DISK_WARNING_THRESHOLD_BYTES;\n\t\tthis._mutexTimeoutMs = Coerce.integer(options.config.mutexTimeoutMs);\n\t}\n\n\t/**\n\t * Bootstrap the connector by creating and initializing any resources it needs.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the bootstrapping process was successful.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tif (!(await this.dirExists(this._directory))) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tmessage: \"directoryCreating\",\n\t\t\t\tdata: {\n\t\t\t\t\tdirectory: this._directory\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait mkdir(this._directory, { recursive: true });\n\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"directoryCreated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdirectory: this._directory\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"directoryCreateFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdirectory: this._directory\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tmessage: \"directoryExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tdirectory: this._directory\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FileEntityStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Returns the health status of the component.\n\t * @returns The health status of the component, can return multiple entries for elements within the component.\n\t */\n\tpublic async health(): Promise<IHealth[]> {\n\t\ttry {\n\t\t\tconst stats = await statfs(this._directory);\n\t\t\tconst freeBytes = stats.bavail * stats.bsize;\n\n\t\t\tif (freeBytes < this._diskErrorThresholdBytes) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\t\tmessage: \"diskSpaceError\",\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tdirectory: this._directory,\n\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\tthresholdBytes: this._diskErrorThresholdBytes\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t];\n\t\t\t} else if (freeBytes < this._diskWarningThresholdBytes) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\tstatus: HealthStatus.Warning,\n\t\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\t\tmessage: \"diskSpaceWarning\",\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tdirectory: this._directory,\n\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\tthresholdBytes: this._diskWarningThresholdBytes\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Ok,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tdata: { directory: this._directory, freeBytes }\n\t\t\t\t}\n\t\t\t];\n\t\t} catch {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tmessage: \"diskSpaceCheckFailed\",\n\t\t\t\t\tdata: { directory: this._directory }\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(FileEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tconst finalConditions = conditions ?? [];\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tconst index = this.findItem(store, id, secondaryIndex, finalConditions);\n\t\tconst item = store[index];\n\n\t\tif (Is.objectValue(item)) {\n\t\t\treturn EntityStorageHelper.unPrepareEntity<T>(item, [\n\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t]);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tpublic async set(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void> {\n\t\tGuards.object<T>(FileEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst prepared = EntityStorageHelper.prepareEntity(\n\t\t\tentity,\n\t\t\tthis._entitySchema,\n\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t? [{ property: FileEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t: undefined,\n\t\t\t{ nullBehavior: \"omit\" }\n\t\t);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tconst finalConditions = conditions ?? [];\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfinalConditions.push({\n\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\tvalue: partitionKey\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst existingIndex = this.findItem(\n\t\t\t\tstore,\n\t\t\t\tprepared[this._primaryKey.property] as string,\n\t\t\t\tundefined,\n\t\t\t\tfinalConditions\n\t\t\t);\n\t\t\tif (existingIndex >= 0) {\n\t\t\t\tstore[existingIndex] = prepared;\n\t\t\t} else {\n\t\t\t\tstore.push(prepared);\n\t\t\t}\n\n\t\t\tawait this.writeStore(store);\n\t\t});\n\t}\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tpublic async setBatch(entities: T[]): Promise<void> {\n\t\tGuards.arrayValue(FileEntityStorageConnector.CLASS_NAME, nameof(entities), entities);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tfor (const entity of entities) {\n\t\t\t\tGuards.object<T>(FileEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\t\t\tconst prepared = EntityStorageHelper.prepareEntity(\n\t\t\t\t\tentity,\n\t\t\t\t\tthis._entitySchema,\n\t\t\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t\t\t? [{ property: FileEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\t{ nullBehavior: \"omit\" }\n\t\t\t\t);\n\n\t\t\t\tconst existingIndex = this.findItem(\n\t\t\t\t\tstore,\n\t\t\t\t\tprepared[this._primaryKey.property] as string,\n\t\t\t\t\tundefined,\n\t\t\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\t\t\t\t\tvalue: partitionKey\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: []\n\t\t\t\t);\n\t\t\t\tif (existingIndex >= 0) {\n\t\t\t\t\tstore[existingIndex] = prepared;\n\t\t\t\t} else {\n\t\t\t\t\tstore.push(prepared);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this.writeStore(store);\n\t\t});\n\t}\n\n\t/**\n\t * Remove all entities from the storage.\n\t * @returns Nothing.\n\t */\n\tpublic async empty(): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tawait this.withLock(async () => {\n\t\t\t\tconst store = await this.readStore();\n\t\t\t\tconst remaining = Is.stringValue(partitionKey)\n\t\t\t\t\t? store.filter(\n\t\t\t\t\t\t\titem =>\n\t\t\t\t\t\t\t\tObjectHelper.propertyGet(\n\t\t\t\t\t\t\t\t\titem as object,\n\t\t\t\t\t\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t\t\t\t) !== partitionKey\n\t\t\t\t\t\t)\n\t\t\t\t\t: [];\n\t\t\t\tawait this.writeStore(remaining);\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(FileEntityStorageConnector.CLASS_NAME, \"emptyFailed\", undefined, err);\n\t\t}\n\t}\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeBatch(ids: string[]): Promise<void> {\n\t\tGuards.arrayValue(FileEntityStorageConnector.CLASS_NAME, nameof(ids), ids);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tawait this.withLock(async () => {\n\t\t\t\tconst store = await this.readStore();\n\t\t\t\tconst idSet = new Set(ids);\n\t\t\t\tconst remaining = store.filter(item => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tIs.stringValue(partitionKey) &&\n\t\t\t\t\t\tObjectHelper.propertyGet(item, FileEntityStorageConnector._PARTITION_KEY) !==\n\t\t\t\t\t\t\tpartitionKey\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn !idSet.has(item[this._primaryKey.property] as string);\n\t\t\t\t});\n\t\t\t\tawait this.writeStore(remaining);\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"removeBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Teardown the storage by deleting the underlying store file.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the teardown process was successful.\n\t */\n\tpublic async teardown(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tawait nodeLogging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"storeTearingDown\"\n\t\t});\n\n\t\ttry {\n\t\t\tawait rm(this._directory, { recursive: true, force: true });\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"storeTornDown\"\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"teardownFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(\n\t\tid: string,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<void> {\n\t\tGuards.stringValue(FileEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tconst finalConditions = conditions ?? [];\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tfinalConditions.push({\n\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\tvalue: partitionKey\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst index = this.findItem(store, id, undefined, finalConditions);\n\n\t\t\tif (index >= 0) {\n\t\t\t\tstore.splice(index, 1);\n\t\t\t\tawait this.writeStore(store);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tEntityStorageHelper.validateSortProperties(this._entitySchema, sortProperties);\n\t\tEntityStorageHelper.validateProperties(this._entitySchema, properties);\n\t\tEntityStorageHelper.validateConditionProperties(this._entitySchema, conditions);\n\n\t\tif (!Is.empty(limit)) {\n\t\t\tconst validationFailures: IValidationFailure[] = [];\n\t\t\tValidation.integer(nameof(limit), limit, validationFailures, undefined, { minValue: 1 });\n\t\t\tValidation.asValidationError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"query\",\n\t\t\t\tvalidationFailures\n\t\t\t);\n\t\t}\n\n\t\tlet allEntities = await this.readStoreWithLock();\n\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(EntityStorageHelper.normalizeConditionValues(conditions));\n\t\t}\n\n\t\tconst entities = [];\n\t\tconst finalLimit = limit ?? FileEntityStorageConnector._DEFAULT_LIMIT;\n\t\tlet nextCursor: string | undefined;\n\n\t\tif (allEntities.length > 0) {\n\t\t\tconst finalSortKeys = EntitySchemaHelper.buildSortProperties<T>(\n\t\t\t\tthis._entitySchema,\n\t\t\t\tsortProperties\n\t\t\t);\n\t\t\tallEntities = EntitySorter.sort(allEntities, finalSortKeys);\n\n\t\t\tconst startIndex = Coerce.number(cursor) ?? 0;\n\n\t\t\tfor (let i = startIndex; i < allEntities.length; i++) {\n\t\t\t\tif (\n\t\t\t\t\tEntityConditions.check(allEntities[i], finalConditions) &&\n\t\t\t\t\tentities.length < finalLimit\n\t\t\t\t) {\n\t\t\t\t\tconst entity = Is.arrayValue(properties)\n\t\t\t\t\t\t? ObjectHelper.pick(allEntities[i], properties)\n\t\t\t\t\t\t: allEntities[i];\n\t\t\t\t\tentities.push(\n\t\t\t\t\t\tEntityStorageHelper.unPrepareEntity<T>(entity, [\n\t\t\t\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t\t])\n\t\t\t\t\t);\n\t\t\t\t\tif (entities.length >= finalLimit) {\n\t\t\t\t\t\tif (i < allEntities.length - 1) {\n\t\t\t\t\t\t\tnextCursor = (i + 1).toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tentities,\n\t\t\tcursor: nextCursor\n\t\t};\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(conditions?: EntityCondition<T>): Promise<number> {\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tEntityStorageHelper.validateConditionProperties(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(EntityStorageHelper.normalizeConditionValues(conditions));\n\t\t}\n\n\t\tif (finalConditions.conditions.length === 0) {\n\t\t\treturn store.length;\n\t\t}\n\n\t\treturn store.filter(item => EntityConditions.check(item, finalConditions)).length;\n\t}\n\n\t/**\n\t * Get a unique list of all the context ids from the storage.\n\t * @returns The list of unique context ids.\n\t */\n\tpublic async getPartitionContextIds(): Promise<IContextIds[]> {\n\t\tconst contextIds: { [id: string]: IContextIds } = {};\n\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tfor (const entity of store) {\n\t\t\tconst partitionId = ObjectHelper.propertyGet(\n\t\t\t\tentity,\n\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t);\n\t\t\tif (Is.stringValue(partitionId)) {\n\t\t\t\tcontextIds[partitionId] = ContextIdHelper.shortSplit(\n\t\t\t\t\tthis._partitionContextIds ?? [],\n\t\t\t\t\tpartitionId\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn Object.values(contextIds);\n\t}\n\n\t/**\n\t * Create the target connector for performing the migration it will use a temporary storage location.\n\t * @param newEntitySchema The name of the new entity schema to create the connector for.\n\t * @returns Connector for performing the migration.\n\t */\n\tpublic async createTargetConnector<U>(\n\t\tnewEntitySchema: string\n\t): Promise<IEntityStorageConnector<U>> {\n\t\tconst baseName = path.basename(this._directory);\n\t\tconst parentDir = path.resolve(this._directory, \"..\");\n\t\tconst migrationDir = path.join(parentDir, `${baseName}_migration_${Date.now()}`);\n\n\t\treturn new FileEntityStorageConnector<U>({\n\t\t\tentitySchema: newEntitySchema,\n\t\t\tpartitionContextIds: this._partitionContextIds,\n\t\t\tconfig: {\n\t\t\t\tdirectory: migrationDir,\n\t\t\t\tdiskErrorThresholdBytes: this._diskErrorThresholdBytes,\n\t\t\t\tdiskWarningThresholdBytes: this._diskWarningThresholdBytes\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Finalize the migration by tearing down the old connector and replacing it with the target connector.\n\t * @param targetConnector The target connector to finalize the migration with.\n\t * @param options The options to control how the migration is finalized.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is finalized.\n\t */\n\tpublic async finalizeMigration<U>(\n\t\ttargetConnector: FileEntityStorageConnector<U>,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<IEntityStorageConnector<U>> {\n\t\tconst originalDir = this._directory;\n\t\tconst migrationDir = targetConnector._directory;\n\n\t\t// Teardown the original connector, removing the entire source directory.\n\t\tawait this.teardown(loggingComponentType);\n\n\t\t// Rename the migration directory into the original location.\n\t\tawait rename(migrationDir, originalDir);\n\n\t\treturn new FileEntityStorageConnector<U>({\n\t\t\tentitySchema: targetConnector._entitySchemaName,\n\t\t\tpartitionContextIds: targetConnector._partitionContextIds,\n\t\t\tconfig: {\n\t\t\t\tdirectory: this._directory,\n\t\t\t\tdiskErrorThresholdBytes: targetConnector._diskErrorThresholdBytes,\n\t\t\t\tdiskWarningThresholdBytes: targetConnector._diskWarningThresholdBytes\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Cleanup the migration if a migration fails or needs to be aborted.\n\t * @param targetConnector The target connector to cleanup the migration with.\n\t * @param options The options to control how the migration is cleaned up.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is cleaned up.\n\t */\n\tpublic async cleanupMigration<U>(\n\t\ttargetConnector: IEntityStorageConnector<U> | undefined,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tawait targetConnector?.teardown?.(loggingComponentType);\n\t}\n\n\t/**\n\t * Read the store from file while holding the directory mutex.\n\t * Use this for standalone reads (get, query, count, getPartitionContextIds) where\n\t * no outer lock is held. Do not call from inside a withLock callback —\n\t * use readStore instead to avoid a re-entrant deadlock.\n\t * @returns The store.\n\t * @internal\n\t */\n\tprivate async readStoreWithLock(): Promise<T[]> {\n\t\treturn this.withLock(async () => this.readStore());\n\t}\n\n\t/**\n\t * Read the store from file without acquiring the directory mutex.\n\t * Must only be called from inside a withLock callback, where the mutex\n\t * is already held. Performing the read under the held lock prevents a race with\n\t * the atomic rename(tmp→store.json) window on Windows that would otherwise\n\t * return ENOENT and be misinterpreted as an empty store.\n\t * @returns The store.\n\t * @internal\n\t */\n\tprivate async readStore(): Promise<T[]> {\n\t\tconst filename = path.join(this._directory, \"store.json\");\n\n\t\tlet store;\n\t\ttry {\n\t\t\tstore = await readFile(filename, \"utf8\");\n\t\t} catch (err) {\n\t\t\tif (ObjectHelper.propertyGet(err, \"code\") === \"ENOENT\") {\n\t\t\t\t// The store has not been written yet, which is valid for a new store.\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"readStoreFailed\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(store) as T[];\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"readStoreCorrupt\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Write the store to the file, atomically replacing the previous version so\n\t * a reader can never observe a partially written store.\n\t * @param store The store to write.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async writeStore(store: T[]): Promise<void> {\n\t\ttry {\n\t\t\tconst filename = path.join(this._directory, \"store.json\");\n\t\t\tconst tempFilename = `${filename}.tmp`;\n\t\t\tawait writeFile(tempFilename, JSON.stringify(store, undefined, \"\\t\"), \"utf8\");\n\t\t\tawait rename(tempFilename, filename);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"writeStoreFailed\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Serialize an update so that concurrent modifications cannot interleave\n\t * their read-modify-write cycles, which would lose updates or tear the\n\t * store file. The mutex is keyed on the storage directory, so every\n\t * connector or worker using the same store serializes with the others.\n\t * @param update The update operation to perform.\n\t * @returns The result of the update.\n\t * @internal\n\t */\n\tprivate async withLock<U>(update: () => Promise<U>): Promise<U> {\n\t\tawait Mutex.lock(this._directory, {\n\t\t\tthrowOnTimeout: true,\n\t\t\ttimeoutMs: this._mutexTimeoutMs\n\t\t});\n\t\ttry {\n\t\t\treturn await update();\n\t\t} finally {\n\t\t\tMutex.unlock(this._directory);\n\t\t}\n\t}\n\n\t/**\n\t * Check if the dir exists.\n\t * @param dir The directory to check.\n\t * @returns True if the dir exists.\n\t * @internal\n\t */\n\tprivate async dirExists(dir: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait access(dir);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Find the item in the store.\n\t * @param store The store to search.\n\t * @param id The id to search for.\n\t * @param secondaryIndex The secondary index to search for.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The index of the item if found or -1.\n\t * @internal\n\t */\n\tprivate findItem(\n\t\tstore: T[],\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): number {\n\t\tconst finalConditions: EntityCondition<T>[] = [];\n\n\t\tif (!Is.empty(secondaryIndex)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: secondaryIndex as string,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: id\n\t\t\t});\n\t\t}\n\n\t\tif (Is.arrayValue(conditions)) {\n\t\t\t// If we haven't added a secondary index condition we need to add the primary key condition.\n\t\t\tif (finalConditions.length === 0) {\n\t\t\t\tfinalConditions.push({\n\t\t\t\t\tproperty: this._primaryKey.property as string,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: id\n\t\t\t\t});\n\t\t\t}\n\t\t\tfinalConditions.push(\n\t\t\t\t...conditions.map(c => ({\n\t\t\t\t\tproperty: c.property as string,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: c.value\n\t\t\t\t}))\n\t\t\t);\n\t\t}\n\n\t\tif (finalConditions.length > 0) {\n\t\t\tfor (let i = 0; i < store.length; i++) {\n\t\t\t\tif (EntityConditions.check(store[i], { conditions: finalConditions })) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn store.findIndex(e => e[this._primaryKey.property] === id);\n\t\t}\n\n\t\treturn -1;\n\t}\n}\n"]}
1
+ {"version":3,"file":"fileEntityStorageConnector.js","sourceRoot":"","sources":["../../src/fileEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { access, mkdir, readFile, rename, rm, statfs, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n\tHealthCategory,\n\tHealthStatus,\n\ttype IHealth,\n\ttype IHealthProviderComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdHelper, ContextIdStore, type IContextIds } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConflictError,\n\tGeneralError,\n\tGuards,\n\tIs,\n\ttype IValidationFailure,\n\tMutex,\n\tObjectHelper,\n\tValidation\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntityConditions,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tEntitySorter,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty,\n\tLogicalOperator,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport {\n\tEntityStorageHelper,\n\ttype IEntityStorageConnector,\n\ttype IEntityStorageMigrationConnector,\n\ttype IMigrationOptions\n} from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IFileEntityStorageConnectorConstructorOptions } from \"./models/IFileEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations in file.\n */\nexport class FileEntityStorageConnector<T = unknown>\n\timplements IEntityStorageMigrationConnector<T>, IHealthProviderComponent\n{\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FileEntityStorageConnector>();\n\n\t/**\n\t * Default limit for number of items to return.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 20;\n\n\t/**\n\t * Partition key for the operation.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * Default disk space warning threshold: 500 MB.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_DISK_WARNING_THRESHOLD_BYTES: number = 500 * 1024 * 1024;\n\n\t/**\n\t * Default disk space error threshold: 100 MB.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_DISK_ERROR_THRESHOLD_BYTES: number = 100 * 1024 * 1024;\n\n\t/**\n\t * The name for the schema.\n\t * @internal\n\t */\n\tprivate readonly _entitySchemaName: string;\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprivate readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The primary key.\n\t * @internal\n\t */\n\tprivate readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * The name of the version property, if any.\n\t * @internal\n\t */\n\tprivate readonly _versionKey?: string;\n\n\t/**\n\t * The directory to use for storage.\n\t * @internal\n\t */\n\tprivate readonly _directory: string;\n\n\t/**\n\t * Free bytes below which health reports an error.\n\t * @internal\n\t */\n\tprivate readonly _diskErrorThresholdBytes: number;\n\n\t/**\n\t * Free bytes below which health reports a warning.\n\t * @internal\n\t */\n\tprivate readonly _diskWarningThresholdBytes: number;\n\n\t/**\n\t * Milliseconds to wait for the directory lock before throwing.\n\t * @internal\n\t */\n\tprivate readonly _mutexTimeoutMs?: number;\n\n\t/**\n\t * Create a new instance of FileEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IFileEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(FileEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object(FileEntityStorageConnector.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.directory),\n\t\t\toptions.config.directory\n\t\t);\n\t\tthis._entitySchemaName = options.entitySchema;\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey<T>(this._entitySchema);\n\t\tthis._versionKey = EntitySchemaHelper.findVersionProperty(this._entitySchema);\n\t\tthis._directory = path.resolve(options.config.directory);\n\t\tthis._diskErrorThresholdBytes =\n\t\t\toptions.config.diskErrorThresholdBytes ??\n\t\t\tFileEntityStorageConnector._DEFAULT_DISK_ERROR_THRESHOLD_BYTES;\n\t\tthis._diskWarningThresholdBytes =\n\t\t\toptions.config.diskWarningThresholdBytes ??\n\t\t\tFileEntityStorageConnector._DEFAULT_DISK_WARNING_THRESHOLD_BYTES;\n\t\tthis._mutexTimeoutMs = Coerce.integer(options.config.mutexTimeoutMs);\n\t}\n\n\t/**\n\t * Bootstrap the connector by creating and initializing any resources it needs.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the bootstrapping process was successful.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tif (!(await this.dirExists(this._directory))) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tmessage: \"directoryCreating\",\n\t\t\t\tdata: {\n\t\t\t\t\tdirectory: this._directory\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait mkdir(this._directory, { recursive: true });\n\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"directoryCreated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdirectory: this._directory\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"directoryCreateFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdirectory: this._directory\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tmessage: \"directoryExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tdirectory: this._directory\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FileEntityStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Returns the health status of the component.\n\t * @returns The health status of the component.\n\t */\n\tpublic async health(): Promise<IHealth[]> {\n\t\ttry {\n\t\t\tconst stats = await statfs(this._directory);\n\t\t\tconst freeBytes = stats.bavail * stats.bsize;\n\n\t\t\tif (freeBytes < this._diskErrorThresholdBytes) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\tcategory: HealthCategory.Connectivity,\n\t\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\t\tmessage: \"diskSpaceError\",\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tdirectory: this._directory,\n\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\tthresholdBytes: this._diskErrorThresholdBytes\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t];\n\t\t\t} else if (freeBytes < this._diskWarningThresholdBytes) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\tcategory: HealthCategory.Connectivity,\n\t\t\t\t\t\tstatus: HealthStatus.Warning,\n\t\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\t\tmessage: \"diskSpaceWarning\",\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tdirectory: this._directory,\n\t\t\t\t\t\t\tfreeBytes,\n\t\t\t\t\t\t\tthresholdBytes: this._diskWarningThresholdBytes\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tcategory: HealthCategory.Connectivity,\n\t\t\t\t\tstatus: HealthStatus.Ok,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tdata: { directory: this._directory, freeBytes }\n\t\t\t\t}\n\t\t\t];\n\t\t} catch {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tcategory: HealthCategory.Connectivity,\n\t\t\t\t\tstatus: HealthStatus.Error,\n\t\t\t\t\tdescription: \"healthDescription\",\n\t\t\t\t\tmessage: \"diskSpaceCheckFailed\",\n\t\t\t\t\tdata: { directory: this._directory }\n\t\t\t\t}\n\t\t\t];\n\t\t}\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(FileEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tconst finalConditions = conditions ? [...conditions] : [];\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tconst index = this.findItem(store, id, secondaryIndex, finalConditions);\n\t\tconst item = store[index];\n\n\t\tif (Is.objectValue(item)) {\n\t\t\treturn EntityStorageHelper.unPrepareEntity<T>(item, [\n\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t]);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t * @throws ConflictError when the entity exists but the supplied conditions or version do not match the stored state.\n\t */\n\tpublic async set(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void> {\n\t\tGuards.object<T>(FileEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst submittedVersion = Is.stringValue(this._versionKey)\n\t\t\t? ObjectHelper.propertyGet<number>(entity, this._versionKey)\n\t\t\t: undefined;\n\t\tconst hasVersionCheck =\n\t\t\t!Is.empty(this._versionKey) && !Is.empty(submittedVersion) && submittedVersion > 0;\n\n\t\tconst prepared = EntityStorageHelper.prepareEntity(\n\t\t\tentity,\n\t\t\tthis._entitySchema,\n\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t? [{ property: FileEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t: undefined,\n\t\t\t{ nullBehavior: \"omit\" }\n\t\t);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tconst baseConditions: { property: keyof T; value: unknown }[] = [];\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tbaseConditions.push({\n\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\tvalue: partitionKey\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst fullConditions: { property: keyof T; value: unknown }[] = [\n\t\t\t\t...baseConditions,\n\t\t\t\t...(conditions ?? [])\n\t\t\t];\n\t\t\tif (hasVersionCheck) {\n\t\t\t\tfullConditions.push({ property: this._versionKey as keyof T, value: submittedVersion });\n\t\t\t}\n\n\t\t\tconst entityId = prepared[this._primaryKey.property] as string;\n\t\t\tconst existingIndex = this.findItem(store, entityId, undefined, fullConditions);\n\n\t\t\tif (existingIndex >= 0) {\n\t\t\t\tif (Is.stringValue(this._versionKey)) {\n\t\t\t\t\tconst storedVersion =\n\t\t\t\t\t\tObjectHelper.propertyGet<number>(store[existingIndex], this._versionKey) ?? 0;\n\t\t\t\t\tObjectHelper.propertySet(prepared, this._versionKey, storedVersion + 1);\n\t\t\t\t}\n\t\t\t\tstore[existingIndex] = prepared;\n\t\t\t} else {\n\t\t\t\tconst existsIndex = this.findItem(store, entityId, undefined, baseConditions);\n\t\t\t\tif (existsIndex >= 0) {\n\t\t\t\t\tif (Is.stringValue(this._versionKey)) {\n\t\t\t\t\t\tif (hasVersionCheck) {\n\t\t\t\t\t\t\tthrow new ConflictError(\n\t\t\t\t\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\t\t\t\"optimisticLockFailed\",\n\t\t\t\t\t\t\t\tentityId\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow new ConflictError(\n\t\t\t\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\t\t\"conditionFailed\",\n\t\t\t\t\t\t\tentityId\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (Is.stringValue(this._versionKey)) {\n\t\t\t\t\tObjectHelper.propertySet(prepared, this._versionKey, 1);\n\t\t\t\t}\n\t\t\t\tstore.push(prepared);\n\t\t\t}\n\n\t\t\tawait this.writeStore(store);\n\t\t});\n\t}\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tpublic async setBatch(entities: T[]): Promise<void> {\n\t\tGuards.arrayValue(FileEntityStorageConnector.CLASS_NAME, nameof(entities), entities);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tfor (const entity of entities) {\n\t\t\t\tGuards.object<T>(FileEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\t\t\tconst prepared = EntityStorageHelper.prepareEntity(\n\t\t\t\t\tentity,\n\t\t\t\t\tthis._entitySchema,\n\t\t\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t\t\t? [{ property: FileEntityStorageConnector._PARTITION_KEY, value: partitionKey }]\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\t{ nullBehavior: \"omit\" }\n\t\t\t\t);\n\n\t\t\t\tconst existingIndex = this.findItem(\n\t\t\t\t\tstore,\n\t\t\t\t\tprepared[this._primaryKey.property] as string,\n\t\t\t\t\tundefined,\n\t\t\t\t\tIs.stringValue(partitionKey)\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\t\t\t\t\tvalue: partitionKey\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: []\n\t\t\t\t);\n\t\t\t\tif (existingIndex >= 0) {\n\t\t\t\t\tstore[existingIndex] = prepared;\n\t\t\t\t} else {\n\t\t\t\t\tstore.push(prepared);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this.writeStore(store);\n\t\t});\n\t}\n\n\t/**\n\t * Remove all entities from the storage.\n\t * @returns Nothing.\n\t */\n\tpublic async empty(): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tawait this.withLock(async () => {\n\t\t\t\tconst store = await this.readStore();\n\t\t\t\tconst remaining = Is.stringValue(partitionKey)\n\t\t\t\t\t? store.filter(\n\t\t\t\t\t\t\titem =>\n\t\t\t\t\t\t\t\tObjectHelper.propertyGet(\n\t\t\t\t\t\t\t\t\titem as object,\n\t\t\t\t\t\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t\t\t\t) !== partitionKey\n\t\t\t\t\t\t)\n\t\t\t\t\t: [];\n\t\t\t\tawait this.writeStore(remaining);\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(FileEntityStorageConnector.CLASS_NAME, \"emptyFailed\", undefined, err);\n\t\t}\n\t}\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeBatch(ids: string[]): Promise<void> {\n\t\tGuards.arrayValue(FileEntityStorageConnector.CLASS_NAME, nameof(ids), ids);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tawait this.withLock(async () => {\n\t\t\t\tconst store = await this.readStore();\n\t\t\t\tconst idSet = new Set(ids);\n\t\t\t\tconst remaining = store.filter(item => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tIs.stringValue(partitionKey) &&\n\t\t\t\t\t\tObjectHelper.propertyGet(item, FileEntityStorageConnector._PARTITION_KEY) !==\n\t\t\t\t\t\t\tpartitionKey\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn !idSet.has(item[this._primaryKey.property] as string);\n\t\t\t\t});\n\t\t\t\tawait this.writeStore(remaining);\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"removeBatchFailed\",\n\t\t\t\tundefined,\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Teardown the storage by deleting the underlying store file.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the teardown process was successful.\n\t */\n\tpublic async teardown(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\tawait nodeLogging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"storeTearingDown\"\n\t\t});\n\n\t\ttry {\n\t\t\tawait rm(this._directory, { recursive: true, force: true });\n\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"storeTornDown\"\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"teardownFailed\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t * @throws ConflictError when the entity exists but the supplied conditions do not match the stored state.\n\t */\n\tpublic async remove(\n\t\tid: string,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<void> {\n\t\tGuards.stringValue(FileEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tEntityStorageHelper.validateConditions(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tawait this.withLock(async () => {\n\t\t\tconst store = await this.readStore();\n\n\t\t\tconst baseConditions: { property: keyof T; value: unknown }[] = [];\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tbaseConditions.push({\n\t\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\t\tvalue: partitionKey\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst fullConditions: { property: keyof T; value: unknown }[] = [\n\t\t\t\t...baseConditions,\n\t\t\t\t...(conditions ?? [])\n\t\t\t];\n\t\t\tconst index = this.findItem(store, id, undefined, fullConditions);\n\n\t\t\tif (index >= 0) {\n\t\t\t\tstore.splice(index, 1);\n\t\t\t\tawait this.writeStore(store);\n\t\t\t} else if (Is.arrayValue(conditions)) {\n\t\t\t\tconst existsIndex = this.findItem(store, id, undefined, baseConditions);\n\t\t\t\tif (existsIndex >= 0 && this._versionKey) {\n\t\t\t\t\tthrow new ConflictError(FileEntityStorageConnector.CLASS_NAME, \"conditionFailed\", id);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tEntityStorageHelper.validateSortProperties(this._entitySchema, sortProperties);\n\t\tEntityStorageHelper.validateProperties(this._entitySchema, properties);\n\t\tEntityStorageHelper.validateConditionProperties(this._entitySchema, conditions);\n\n\t\tif (!Is.empty(limit)) {\n\t\t\tconst validationFailures: IValidationFailure[] = [];\n\t\t\tValidation.integer(nameof(limit), limit, validationFailures, undefined, { minValue: 1 });\n\t\t\tValidation.asValidationError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"query\",\n\t\t\t\tvalidationFailures\n\t\t\t);\n\t\t}\n\n\t\tlet allEntities = await this.readStoreWithLock();\n\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(EntityStorageHelper.normalizeConditionValues(conditions));\n\t\t}\n\n\t\tconst entities = [];\n\t\tconst finalLimit = limit ?? FileEntityStorageConnector._DEFAULT_LIMIT;\n\t\tlet nextCursor: string | undefined;\n\n\t\tif (allEntities.length > 0) {\n\t\t\tconst finalSortKeys = EntitySchemaHelper.buildSortProperties<T>(\n\t\t\t\tthis._entitySchema,\n\t\t\t\tsortProperties\n\t\t\t);\n\t\t\tallEntities = EntitySorter.sort(allEntities, finalSortKeys);\n\n\t\t\tconst startIndex = Coerce.number(cursor) ?? 0;\n\n\t\t\tfor (let i = startIndex; i < allEntities.length; i++) {\n\t\t\t\tif (\n\t\t\t\t\tEntityConditions.check(allEntities[i], finalConditions) &&\n\t\t\t\t\tentities.length < finalLimit\n\t\t\t\t) {\n\t\t\t\t\tconst entity = Is.arrayValue(properties)\n\t\t\t\t\t\t? ObjectHelper.pick(allEntities[i], properties)\n\t\t\t\t\t\t: allEntities[i];\n\t\t\t\t\tentities.push(\n\t\t\t\t\t\tEntityStorageHelper.unPrepareEntity<T>(entity, [\n\t\t\t\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t\t])\n\t\t\t\t\t);\n\t\t\t\t\tif (entities.length >= finalLimit) {\n\t\t\t\t\t\tif (i < allEntities.length - 1) {\n\t\t\t\t\t\t\tnextCursor = (i + 1).toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tentities,\n\t\t\tcursor: nextCursor\n\t\t};\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(conditions?: EntityCondition<T>): Promise<number> {\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tEntityStorageHelper.validateConditionProperties(this._entitySchema, conditions);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst finalConditions: EntityCondition<T> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.conditions.push({\n\t\t\t\tproperty: FileEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tif (!Is.empty(conditions)) {\n\t\t\tfinalConditions.conditions.push(EntityStorageHelper.normalizeConditionValues(conditions));\n\t\t}\n\n\t\tif (finalConditions.conditions.length === 0) {\n\t\t\treturn store.length;\n\t\t}\n\n\t\treturn store.filter(item => EntityConditions.check(item, finalConditions)).length;\n\t}\n\n\t/**\n\t * Get the connector implementation version.\n\t * @returns The connector implementation version.\n\t */\n\tpublic connectorVersion(): number {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get a unique list of all the context ids from the storage.\n\t * @returns The list of unique context ids.\n\t */\n\tpublic async getPartitionContextIds(): Promise<IContextIds[] | undefined> {\n\t\tif (!Is.arrayValue(this._partitionContextIds)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst contextIds: { [id: string]: IContextIds } = {};\n\n\t\tconst store = await this.readStoreWithLock();\n\n\t\tfor (const entity of store) {\n\t\t\tconst partitionId = ObjectHelper.propertyGet(\n\t\t\t\tentity,\n\t\t\t\tFileEntityStorageConnector._PARTITION_KEY\n\t\t\t);\n\t\t\tif (Is.stringValue(partitionId)) {\n\t\t\t\tcontextIds[partitionId] = ContextIdHelper.shortSplit(\n\t\t\t\t\tthis._partitionContextIds ?? [],\n\t\t\t\t\tpartitionId\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn Object.values(contextIds);\n\t}\n\n\t/**\n\t * Create the target connector for performing the migration it will use a temporary storage location.\n\t * @param newEntitySchema The name of the new entity schema to create the connector for.\n\t * @returns Connector for performing the migration.\n\t */\n\tpublic async createTargetConnector<U>(\n\t\tnewEntitySchema: string\n\t): Promise<IEntityStorageConnector<U>> {\n\t\tconst baseName = path.basename(this._directory);\n\t\tconst parentDir = path.resolve(this._directory, \"..\");\n\t\tconst migrationDir = path.join(parentDir, `${baseName}_migration_${Date.now()}`);\n\n\t\treturn new FileEntityStorageConnector<U>({\n\t\t\tentitySchema: newEntitySchema,\n\t\t\tpartitionContextIds: this._partitionContextIds,\n\t\t\tconfig: {\n\t\t\t\tdirectory: migrationDir,\n\t\t\t\tdiskErrorThresholdBytes: this._diskErrorThresholdBytes,\n\t\t\t\tdiskWarningThresholdBytes: this._diskWarningThresholdBytes\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Finalize the migration by tearing down the old connector and replacing it with the target connector.\n\t * @param targetConnector The target connector to finalize the migration with.\n\t * @param options The options to control how the migration is finalized.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is finalized.\n\t */\n\tpublic async finalizeMigration<U>(\n\t\ttargetConnector: FileEntityStorageConnector<U>,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<IEntityStorageConnector<U>> {\n\t\tconst originalDir = this._directory;\n\t\tconst migrationDir = targetConnector._directory;\n\n\t\t// Teardown the original connector, removing the entire source directory.\n\t\tawait this.teardown(loggingComponentType);\n\n\t\t// Rename the migration directory into the original location.\n\t\tawait rename(migrationDir, originalDir);\n\n\t\treturn new FileEntityStorageConnector<U>({\n\t\t\tentitySchema: targetConnector._entitySchemaName,\n\t\t\tpartitionContextIds: targetConnector._partitionContextIds,\n\t\t\tconfig: {\n\t\t\t\tdirectory: this._directory,\n\t\t\t\tdiskErrorThresholdBytes: targetConnector._diskErrorThresholdBytes,\n\t\t\t\tdiskWarningThresholdBytes: targetConnector._diskWarningThresholdBytes\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Cleanup the migration if a migration fails or needs to be aborted.\n\t * @param targetConnector The target connector to cleanup the migration with.\n\t * @param options The options to control how the migration is cleaned up.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is cleaned up.\n\t */\n\tpublic async cleanupMigration<U>(\n\t\ttargetConnector: IEntityStorageConnector<U> | undefined,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tawait targetConnector?.teardown?.(loggingComponentType);\n\t}\n\n\t/**\n\t * Read the store from file while holding the directory mutex.\n\t * Use this for standalone reads (get, query, count, getPartitionContextIds) where\n\t * no outer lock is held. Do not call from inside a withLock callback -\n\t * use readStore instead to avoid a re-entrant deadlock.\n\t * @returns The store.\n\t * @internal\n\t */\n\tprivate async readStoreWithLock(): Promise<T[]> {\n\t\treturn this.withLock(async () => this.readStore());\n\t}\n\n\t/**\n\t * Read the store from file without acquiring the directory mutex.\n\t * Must only be called from inside a withLock callback, where the mutex\n\t * is already held. Performing the read under the held lock prevents a race with\n\t * the atomic rename(tmp→store.json) window on Windows that would otherwise\n\t * return ENOENT and be misinterpreted as an empty store.\n\t * @returns The store.\n\t * @internal\n\t */\n\tprivate async readStore(): Promise<T[]> {\n\t\tconst filename = path.join(this._directory, \"store.json\");\n\n\t\tlet store;\n\t\ttry {\n\t\t\tstore = await readFile(filename, \"utf8\");\n\t\t} catch (err) {\n\t\t\tif (ObjectHelper.propertyGet(err, \"code\") === \"ENOENT\") {\n\t\t\t\t// The store has not been written yet, which is valid for a new store.\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"readStoreFailed\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(store) as T[];\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"readStoreCorrupt\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Write the store to the file, atomically replacing the previous version so\n\t * a reader can never observe a partially written store.\n\t * @param store The store to write.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async writeStore(store: T[]): Promise<void> {\n\t\ttry {\n\t\t\tconst filename = path.join(this._directory, \"store.json\");\n\t\t\tconst tempFilename = `${filename}.tmp`;\n\t\t\tawait writeFile(tempFilename, JSON.stringify(store, undefined, \"\\t\"), \"utf8\");\n\t\t\tawait rename(tempFilename, filename);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tFileEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"writeStoreFailed\",\n\t\t\t\t{ directory: this._directory },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Serialize an update so that concurrent modifications cannot interleave\n\t * their read-modify-write cycles, which would lose updates or tear the\n\t * store file. The mutex is keyed on the storage directory, so every\n\t * connector or worker using the same store serializes with the others.\n\t * @param update The update operation to perform.\n\t * @returns The result of the update.\n\t * @internal\n\t */\n\tprivate async withLock<U>(update: () => Promise<U>): Promise<U> {\n\t\tawait Mutex.lock(this._directory, {\n\t\t\tthrowOnTimeout: true,\n\t\t\ttimeoutMs: this._mutexTimeoutMs\n\t\t});\n\t\ttry {\n\t\t\treturn await update();\n\t\t} finally {\n\t\t\tMutex.unlock(this._directory);\n\t\t}\n\t}\n\n\t/**\n\t * Check if the dir exists.\n\t * @param dir The directory to check.\n\t * @returns True if the dir exists.\n\t * @internal\n\t */\n\tprivate async dirExists(dir: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait access(dir);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Find the item in the store.\n\t * @param store The store to search.\n\t * @param id The id to search for.\n\t * @param secondaryIndex The secondary index to search for.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The index of the item if found or -1.\n\t * @internal\n\t */\n\tprivate findItem(\n\t\tstore: T[],\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): number {\n\t\tconst finalConditions: EntityCondition<T>[] = [];\n\n\t\tif (!Is.empty(secondaryIndex)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: secondaryIndex as string,\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: id\n\t\t\t});\n\t\t}\n\n\t\tif (Is.arrayValue(conditions)) {\n\t\t\t// If we haven't added a secondary index condition we need to add the primary key condition.\n\t\t\tif (finalConditions.length === 0) {\n\t\t\t\tfinalConditions.push({\n\t\t\t\t\tproperty: this._primaryKey.property as string,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: id\n\t\t\t\t});\n\t\t\t}\n\t\t\tfinalConditions.push(\n\t\t\t\t...conditions.map(c => ({\n\t\t\t\t\tproperty: c.property as string,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: c.value\n\t\t\t\t}))\n\t\t\t);\n\t\t}\n\n\t\tif (finalConditions.length > 0) {\n\t\t\tfor (let i = 0; i < store.length; i++) {\n\t\t\t\tif (EntityConditions.check(store[i], { conditions: finalConditions })) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn store.findIndex(e => e[this._primaryKey.property] === id);\n\t\t}\n\n\t\treturn -1;\n\t}\n}\n"]}
@@ -1,12 +1,12 @@
1
+ import { type IHealth, type IHealthProviderComponent } from "@twin.org/api-models";
1
2
  import { type IContextIds } from "@twin.org/context";
2
- import { type IHealth } from "@twin.org/core";
3
3
  import { type EntityCondition, type IEntitySchema, type SortDirection } from "@twin.org/entity";
4
4
  import { type IEntityStorageConnector, type IEntityStorageMigrationConnector, type IMigrationOptions } from "@twin.org/entity-storage-models";
5
5
  import type { IFileEntityStorageConnectorConstructorOptions } from "./models/IFileEntityStorageConnectorConstructorOptions.js";
6
6
  /**
7
7
  * Class for performing entity storage operations in file.
8
8
  */
9
- export declare class FileEntityStorageConnector<T = unknown> implements IEntityStorageMigrationConnector<T> {
9
+ export declare class FileEntityStorageConnector<T = unknown> implements IEntityStorageMigrationConnector<T>, IHealthProviderComponent {
10
10
  /**
11
11
  * Runtime name for the class.
12
12
  */
@@ -29,7 +29,7 @@ export declare class FileEntityStorageConnector<T = unknown> implements IEntityS
29
29
  className(): string;
30
30
  /**
31
31
  * Returns the health status of the component.
32
- * @returns The health status of the component, can return multiple entries for elements within the component.
32
+ * @returns The health status of the component.
33
33
  */
34
34
  health(): Promise<IHealth[]>;
35
35
  /**
@@ -53,6 +53,7 @@ export declare class FileEntityStorageConnector<T = unknown> implements IEntityS
53
53
  * @param entity The entity to set.
54
54
  * @param conditions The optional conditions to match for the entities.
55
55
  * @returns The id of the entity.
56
+ * @throws ConflictError when the entity exists but the supplied conditions or version do not match the stored state.
56
57
  */
57
58
  set(entity: T, conditions?: {
58
59
  property: keyof T;
@@ -86,6 +87,7 @@ export declare class FileEntityStorageConnector<T = unknown> implements IEntityS
86
87
  * @param id The id of the entity to remove.
87
88
  * @param conditions The optional conditions to match for the entities.
88
89
  * @returns Nothing.
90
+ * @throws ConflictError when the entity exists but the supplied conditions do not match the stored state.
89
91
  */
90
92
  remove(id: string, conditions?: {
91
93
  property: keyof T;
@@ -120,11 +122,16 @@ export declare class FileEntityStorageConnector<T = unknown> implements IEntityS
120
122
  * @returns The total count of entities in the storage.
121
123
  */
122
124
  count(conditions?: EntityCondition<T>): Promise<number>;
125
+ /**
126
+ * Get the connector implementation version.
127
+ * @returns The connector implementation version.
128
+ */
129
+ connectorVersion(): number;
123
130
  /**
124
131
  * Get a unique list of all the context ids from the storage.
125
132
  * @returns The list of unique context ids.
126
133
  */
127
- getPartitionContextIds(): Promise<IContextIds[]>;
134
+ getPartitionContextIds(): Promise<IContextIds[] | undefined>;
128
135
  /**
129
136
  * Create the target connector for performing the migration it will use a temporary storage location.
130
137
  * @param newEntitySchema The name of the new entity schema to create the connector for.
package/docs/changelog.md CHANGED
@@ -1,5 +1,202 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.2-next.10](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.9...entity-storage-connector-file-v0.9.2-next.10) (2026-08-14)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **entity-storage-connector-file:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.9 to 0.9.2-next.10
16
+ * devDependencies
17
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.9 to 0.9.2-next.10
18
+
19
+ ## [0.9.2-next.9](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.8...entity-storage-connector-file-v0.9.2-next.9) (2026-08-12)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * use hashing to restrict index name length ([757d572](https://github.com/iotaledger/twin-entity-storage/commit/757d5728161a00c1d1865ad6df3231eecfad16f2))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.8 to 0.9.2-next.9
32
+ * devDependencies
33
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.8 to 0.9.2-next.9
34
+
35
+ ## [0.9.2-next.8](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.7...entity-storage-connector-file-v0.9.2-next.8) (2026-08-12)
36
+
37
+
38
+ ### Features
39
+
40
+ * indexing ([#207](https://github.com/iotaledger/twin-entity-storage/issues/207)) ([2fd1f0d](https://github.com/iotaledger/twin-entity-storage/commit/2fd1f0d992344905c9dd1a44addfc4f9d5b5c168))
41
+
42
+
43
+ ### Dependencies
44
+
45
+ * The following workspace dependencies were updated
46
+ * dependencies
47
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.7 to 0.9.2-next.8
48
+ * devDependencies
49
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.7 to 0.9.2-next.8
50
+
51
+ ## [0.9.2-next.7](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.6...entity-storage-connector-file-v0.9.2-next.7) (2026-08-11)
52
+
53
+
54
+ ### Features
55
+
56
+ * optimistic locking ([#201](https://github.com/iotaledger/twin-entity-storage/issues/201)) ([80cbe3b](https://github.com/iotaledger/twin-entity-storage/commit/80cbe3b611c47b16328bceab02cfb8423fe1bbcd))
57
+
58
+
59
+ ### Dependencies
60
+
61
+ * The following workspace dependencies were updated
62
+ * dependencies
63
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.6 to 0.9.2-next.7
64
+ * devDependencies
65
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.6 to 0.9.2-next.7
66
+
67
+ ## [0.9.2-next.6](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.5...entity-storage-connector-file-v0.9.2-next.6) (2026-08-07)
68
+
69
+
70
+ ### Bug Fixes
71
+
72
+ * allow multi-property sort on DynamoDB and CosmosDB connectors ([#196](https://github.com/iotaledger/twin-entity-storage/issues/196)) ([f1bb582](https://github.com/iotaledger/twin-entity-storage/commit/f1bb5826d75dae331ad6b42df5de330a308c5405))
73
+
74
+
75
+ ### Dependencies
76
+
77
+ * The following workspace dependencies were updated
78
+ * dependencies
79
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.5 to 0.9.2-next.6
80
+ * devDependencies
81
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.5 to 0.9.2-next.6
82
+
83
+ ## [0.9.2-next.5](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.4...entity-storage-connector-file-v0.9.2-next.5) (2026-08-07)
84
+
85
+
86
+ ### Features
87
+
88
+ * linting and dependency update ([c307b60](https://github.com/iotaledger/twin-entity-storage/commit/c307b606d03ea436b7c43d4e1764b5c08f415555))
89
+
90
+
91
+ ### Dependencies
92
+
93
+ * The following workspace dependencies were updated
94
+ * dependencies
95
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.4 to 0.9.2-next.5
96
+ * devDependencies
97
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.4 to 0.9.2-next.5
98
+
99
+ ## [0.9.2-next.4](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.3...entity-storage-connector-file-v0.9.2-next.4) (2026-08-04)
100
+
101
+
102
+ ### Bug Fixes
103
+
104
+ * schema version check crashes in multi-tenant mode ([#192](https://github.com/iotaledger/twin-entity-storage/issues/192)) ([a816341](https://github.com/iotaledger/twin-entity-storage/commit/a8163415ce116582f3c3c23294f7f4062099d8f3))
105
+
106
+
107
+ ### Dependencies
108
+
109
+ * The following workspace dependencies were updated
110
+ * dependencies
111
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.3 to 0.9.2-next.4
112
+ * devDependencies
113
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.3 to 0.9.2-next.4
114
+
115
+ ## [0.9.2-next.3](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.2...entity-storage-connector-file-v0.9.2-next.3) (2026-08-03)
116
+
117
+
118
+ ### Features
119
+
120
+ * update health signatures ([#188](https://github.com/iotaledger/twin-entity-storage/issues/188)) ([0159094](https://github.com/iotaledger/twin-entity-storage/commit/015909423958a7a20505a92b23793a044d26f6a4))
121
+
122
+
123
+ ### Dependencies
124
+
125
+ * The following workspace dependencies were updated
126
+ * dependencies
127
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.2 to 0.9.2-next.3
128
+ * devDependencies
129
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.2 to 0.9.2-next.3
130
+
131
+ ## [0.9.2-next.2](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.1...entity-storage-connector-file-v0.9.2-next.2) (2026-07-29)
132
+
133
+
134
+ ### Miscellaneous Chores
135
+
136
+ * **entity-storage-connector-file:** Synchronize repo versions
137
+
138
+
139
+ ### Dependencies
140
+
141
+ * The following workspace dependencies were updated
142
+ * dependencies
143
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.1 to 0.9.2-next.2
144
+ * devDependencies
145
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.1 to 0.9.2-next.2
146
+
147
+ ## [0.9.2-next.1](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.2-next.0...entity-storage-connector-file-v0.9.2-next.1) (2026-07-29)
148
+
149
+
150
+ ### Features
151
+
152
+ * add context id features ([#55](https://github.com/iotaledger/twin-entity-storage/issues/55)) ([99c15a2](https://github.com/iotaledger/twin-entity-storage/commit/99c15a257539b61d9da63649ce573ebf47699fc9))
153
+ * add ISchemaMigration chain, SchemaVersionMigrator runner and version store ([#110](https://github.com/iotaledger/twin-entity-storage/issues/110)) ([2dac924](https://github.com/iotaledger/twin-entity-storage/commit/2dac9244a752cb58304d1649ff03c3a2469783dd))
154
+ * add production release automation ([1eb4c8e](https://github.com/iotaledger/twin-entity-storage/commit/1eb4c8ee3eb099defdfc2d063ae44935276dcae8))
155
+ * add support for object comparison conditions ([eb505a1](https://github.com/iotaledger/twin-entity-storage/commit/eb505a17a3642e95c4e3cf137a77a0a8fb388c97))
156
+ * add validate-locales ([e66ef0d](https://github.com/iotaledger/twin-entity-storage/commit/e66ef0de26ca2f82b3fe89bb5c7a15a0978a9644))
157
+ * adding schema migration functionality to all the connectors ([#85](https://github.com/iotaledger/twin-entity-storage/issues/85)) ([fd1555a](https://github.com/iotaledger/twin-entity-storage/commit/fd1555a34380158214a577586dafae821e72a578))
158
+ * additional information in health ([1e658b7](https://github.com/iotaledger/twin-entity-storage/commit/1e658b74288e9411538286d25b81823df80703e9))
159
+ * disable endpoint discovery for cosmos ([49df2a2](https://github.com/iotaledger/twin-entity-storage/commit/49df2a254f385dc42e2e1f7e26e71f89b5bf5bcf))
160
+ * entity storage conditions ([#115](https://github.com/iotaledger/twin-entity-storage/issues/115)) ([7a53884](https://github.com/iotaledger/twin-entity-storage/commit/7a53884f6acb856d77733e4e0f23ec1c00b74cb4))
161
+ * entity storage enhancements ([#86](https://github.com/iotaledger/twin-entity-storage/issues/86)) ([1279af4](https://github.com/iotaledger/twin-entity-storage/commit/1279af42615c6497bb06539842cee44842dd1f75))
162
+ * eslint migration to flat config ([f033b64](https://github.com/iotaledger/twin-entity-storage/commit/f033b64984c0e6a8129d929c9dd816dcc1b8dab0))
163
+ * input validation ([#162](https://github.com/iotaledger/twin-entity-storage/issues/162)) ([3e1e428](https://github.com/iotaledger/twin-entity-storage/commit/3e1e42887955cf079efd5989e197ddf8e0fa8c47))
164
+ * logging naming consistency ([f99d12d](https://github.com/iotaledger/twin-entity-storage/commit/f99d12dea04b6d4f2b5632ff5473e9ec7d5f9055))
165
+ * migration progress ([#121](https://github.com/iotaledger/twin-entity-storage/issues/121)) ([d032162](https://github.com/iotaledger/twin-entity-storage/commit/d032162768b6b7d4ccca7e39b80f8bc3ba46440e))
166
+ * mysql non offset paging ([#172](https://github.com/iotaledger/twin-entity-storage/issues/172)) ([0633165](https://github.com/iotaledger/twin-entity-storage/commit/063316563fa8abe221251cd34fdd6c03538b9bb3))
167
+ * typescript 6 update ([995a0c6](https://github.com/iotaledger/twin-entity-storage/commit/995a0c6fa9a6813bfdc7200779ce3664236e59e9))
168
+ * update components ([97aad24](https://github.com/iotaledger/twin-entity-storage/commit/97aad24fd5d76528a95f09143b79587ab71e536d))
169
+ * update dependencies ([9bf04a4](https://github.com/iotaledger/twin-entity-storage/commit/9bf04a42080c7e2163d118a9c87102312dc9659b))
170
+ * update dependencies ([7ccc0c4](https://github.com/iotaledger/twin-entity-storage/commit/7ccc0c429125d073dc60b3de6cf101abc8cc6cba))
171
+ * update framework core ([b59a380](https://github.com/iotaledger/twin-entity-storage/commit/b59a380bb7fba2b43610f69074dcdee24a4737da))
172
+ * use shared store mechanism ([#34](https://github.com/iotaledger/twin-entity-storage/issues/34)) ([68b6b71](https://github.com/iotaledger/twin-entity-storage/commit/68b6b71e7a96d7d016cd57bfff36775b56bf3f93))
173
+
174
+
175
+ ### Bug Fixes
176
+
177
+ * adding tests and fixes for dot notation ([#76](https://github.com/iotaledger/twin-entity-storage/issues/76)) ([3879337](https://github.com/iotaledger/twin-entity-storage/commit/387933797e33543e4d8b2d49b8beeb792512a4ff))
178
+ * adding tests and support when neccesary for string include operator when needed ([#72](https://github.com/iotaledger/twin-entity-storage/issues/72)) ([3c723dd](https://github.com/iotaledger/twin-entity-storage/commit/3c723dd5694814398099d9d4594089dc6c66ba97))
179
+ * correct handling of pick with no properties ([b18dae6](https://github.com/iotaledger/twin-entity-storage/commit/b18dae64ba635cd72705d735934c13ca1481bd8f))
180
+ * dynamodb get with condition ignores primary key ([d2d0ec2](https://github.com/iotaledger/twin-entity-storage/commit/d2d0ec21023bc22f0e5a35c2d49396d90b42a4ce))
181
+ * dynamodb query gsi ([#140](https://github.com/iotaledger/twin-entity-storage/issues/140)) ([45b56d6](https://github.com/iotaledger/twin-entity-storage/commit/45b56d6260c9876012030cc6c85026ea84aebff5))
182
+ * guard against empty IN list in all SQL-style connectors ([#101](https://github.com/iotaledger/twin-entity-storage/issues/101)) ([fb2bf8b](https://github.com/iotaledger/twin-entity-storage/commit/fb2bf8beb148f0c9b92661c4899e28cd4559f39a))
183
+ * handle empty conditions ([#159](https://github.com/iotaledger/twin-entity-storage/issues/159)) ([ae3319c](https://github.com/iotaledger/twin-entity-storage/commit/ae3319c3136bccc94244b2d79b3baef7a1b037d7))
184
+ * migration partitions ([#182](https://github.com/iotaledger/twin-entity-storage/issues/182)) ([bcbaf26](https://github.com/iotaledger/twin-entity-storage/commit/bcbaf26f11d34beabefdfcaf8101b7c234ca5ac9))
185
+ * null secondary indexes ([#103](https://github.com/iotaledger/twin-entity-storage/issues/103)) ([5e44f11](https://github.com/iotaledger/twin-entity-storage/commit/5e44f11bb5af5bf2c27d6f1d56aba5851116ff89))
186
+ * query params force coercion ([dd6aa87](https://github.com/iotaledger/twin-entity-storage/commit/dd6aa87efdfb60bab7d6756a86888863c45c51a7))
187
+ * route GSI sort-key conditions to KeyConditionExpression and cross-connector cursor-walk tests ([#127](https://github.com/iotaledger/twin-entity-storage/issues/127)) ([6a24e1b](https://github.com/iotaledger/twin-entity-storage/commit/6a24e1b5f3b8b426987e43da3af6766d8cb68afb))
188
+ * serialize and atomically write file entity storage stores ([#132](https://github.com/iotaledger/twin-entity-storage/issues/132)) ([4b65edb](https://github.com/iotaledger/twin-entity-storage/commit/4b65edbceeff1b3d9bee29e5451a2cc78f46f577))
189
+ * tests and fixes for the comparisons for null and undefined ([#79](https://github.com/iotaledger/twin-entity-storage/issues/79)) ([e7ffd62](https://github.com/iotaledger/twin-entity-storage/commit/e7ffd62e9ec40ef31498e6e2350bb25d9c84638a))
190
+
191
+
192
+ ### Dependencies
193
+
194
+ * The following workspace dependencies were updated
195
+ * dependencies
196
+ * @twin.org/entity-storage-models bumped from 0.9.2-next.0 to 0.9.2-next.1
197
+ * devDependencies
198
+ * @twin.org/entity-storage-connector-memory bumped from 0.9.2-next.0 to 0.9.2-next.1
199
+
3
200
  ## [0.9.1](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.9.1...entity-storage-connector-file-v0.9.1) (2026-07-27)
4
201
 
5
202
 
@@ -11,6 +11,7 @@ Class for performing entity storage operations in file.
11
11
  ## Implements
12
12
 
13
13
  - `IEntityStorageMigrationConnector`\<`T`\>
14
+ - `IHealthProviderComponent`
14
15
 
15
16
  ## Constructors
16
17
 
@@ -96,11 +97,11 @@ Returns the health status of the component.
96
97
 
97
98
  `Promise`\<`IHealth`[]\>
98
99
 
99
- The health status of the component, can return multiple entries for elements within the component.
100
+ The health status of the component.
100
101
 
101
102
  #### Implementation of
102
103
 
103
- `IEntityStorageMigrationConnector.health`
104
+ `IHealthProviderComponent.health`
104
105
 
105
106
  ***
106
107
 
@@ -186,6 +187,10 @@ The optional conditions to match for the entities.
186
187
 
187
188
  The id of the entity.
188
189
 
190
+ #### Throws
191
+
192
+ ConflictError when the entity exists but the supplied conditions or version do not match the stored state.
193
+
189
194
  #### Implementation of
190
195
 
191
196
  `IEntityStorageMigrationConnector.set`
@@ -314,6 +319,10 @@ The optional conditions to match for the entities.
314
319
 
315
320
  Nothing.
316
321
 
322
+ #### Throws
323
+
324
+ ConflictError when the entity exists but the supplied conditions do not match the stored state.
325
+
317
326
  #### Implementation of
318
327
 
319
328
  `IEntityStorageMigrationConnector.remove`
@@ -397,15 +406,33 @@ The total count of entities in the storage.
397
406
 
398
407
  ***
399
408
 
409
+ ### connectorVersion() {#connectorversion}
410
+
411
+ > **connectorVersion**(): `number`
412
+
413
+ Get the connector implementation version.
414
+
415
+ #### Returns
416
+
417
+ `number`
418
+
419
+ The connector implementation version.
420
+
421
+ #### Implementation of
422
+
423
+ `IEntityStorageMigrationConnector.connectorVersion`
424
+
425
+ ***
426
+
400
427
  ### getPartitionContextIds() {#getpartitioncontextids}
401
428
 
402
- > **getPartitionContextIds**(): `Promise`\<`IContextIds`[]\>
429
+ > **getPartitionContextIds**(): `Promise`\<`IContextIds`[] \| `undefined`\>
403
430
 
404
431
  Get a unique list of all the context ids from the storage.
405
432
 
406
433
  #### Returns
407
434
 
408
- `Promise`\<`IContextIds`[]\>
435
+ `Promise`\<`IContextIds`[] \| `undefined`\>
409
436
 
410
437
  The list of unique context ids.
411
438
 
package/locales/en.json CHANGED
@@ -10,6 +10,8 @@
10
10
  },
11
11
  "error": {
12
12
  "fileEntityStorageConnector": {
13
+ "optimisticLockFailed": "The entity \"{conflictId}\" could not be written because the optimistic lock check failed",
14
+ "conditionFailed": "The entity \"{conflictId}\" could not be updated because the supplied conditions did not match the stored state",
13
15
  "directoryCreateFailed": "Creating directory \"{directory}\" failed",
14
16
  "emptyFailed": "Unable to empty entity storage",
15
17
  "readStoreCorrupt": "The entity storage store file in directory \"{directory}\" contains corrupt JSON",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-connector-file",
3
- "version": "0.9.1",
3
+ "version": "0.9.2-next.10",
4
4
  "description": "File-based connector that stores entities on disk for straightforward deployments.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,12 +14,13 @@
14
14
  "node": ">=24.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/context": "^0.9.1",
18
- "@twin.org/core": "^0.9.1",
19
- "@twin.org/entity": "^0.9.1",
20
- "@twin.org/entity-storage-models": "^0.9.1",
21
- "@twin.org/logging-models": "^0.9.1",
22
- "@twin.org/nameof": "^0.9.1"
17
+ "@twin.org/api-models": "next",
18
+ "@twin.org/context": "next",
19
+ "@twin.org/core": "next",
20
+ "@twin.org/entity": "next",
21
+ "@twin.org/entity-storage-models": "0.9.2-next.10",
22
+ "@twin.org/logging-models": "next",
23
+ "@twin.org/nameof": "next"
23
24
  },
24
25
  "main": "./dist/es/index.js",
25
26
  "types": "./dist/types/index.d.ts",