@twin.org/entity-storage-connector-file 0.10.1-next.9 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This package provides a file-backed backend for persisting entities on local or mounted disks in straightforward environments. It is designed to work with the wider storage ecosystem so applications can keep behaviour consistent across connectors and environments.
|
|
4
4
|
|
|
5
|
+
## Intended Use
|
|
6
|
+
|
|
7
|
+
This connector is intended as a testing and debugging aid, not for production workloads. Each table is kept as a single human-readable `store.json` in the configured directory, so its contents can be inspected and edited by hand.
|
|
8
|
+
|
|
9
|
+
Every operation reads and parses the whole file, and every write serialises and rewrites it, all while holding a lock on the directory. The cost of each call therefore grows with the number of stored entities, and concurrent callers on the same directory wait for each other. As a guide, a table of around 14,000 entities of 500 bytes each takes roughly 10 ms per read and 25 ms per write. For large or busy tables, such as log entries, use one of the database-backed connectors instead.
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
13
|
```shell
|
|
@@ -605,14 +605,14 @@ export class FileEntityStorageConnector {
|
|
|
605
605
|
return undefined;
|
|
606
606
|
}
|
|
607
607
|
const contextIds = {};
|
|
608
|
-
const skipped = new
|
|
608
|
+
const skipped = new Map();
|
|
609
609
|
const store = await this.readStoreWithLock();
|
|
610
610
|
for (const entity of store) {
|
|
611
611
|
const partitionId = ObjectHelper.propertyGet(entity, FileEntityStorageConnector._PARTITION_KEY);
|
|
612
612
|
if (Is.stringValue(partitionId)) {
|
|
613
613
|
const split = EntityStorageHelper.tryShortSplit(this._partitionContextIds ?? [], partitionId);
|
|
614
614
|
if (Is.undefined(split)) {
|
|
615
|
-
skipped.
|
|
615
|
+
skipped.set(partitionId, (skipped.get(partitionId) ?? 0) + 1);
|
|
616
616
|
}
|
|
617
617
|
else {
|
|
618
618
|
contextIds[partitionId] = split;
|
|
@@ -628,7 +628,7 @@ export class FileEntityStorageConnector {
|
|
|
628
628
|
message: "partitionIdsSkipped",
|
|
629
629
|
data: {
|
|
630
630
|
expected: this._partitionContextIds?.length,
|
|
631
|
-
partitionIds: Array.from(skipped).join(", ")
|
|
631
|
+
partitionIds: Array.from(skipped, ([id, count]) => `${id}: ${count}`).join(", ")
|
|
632
632
|
}
|
|
633
633
|
});
|
|
634
634
|
}
|
|
@@ -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 {\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\tEntityStorageCommon,\n\tEntityStorageHelper,\n\ttype IEntityStorageConnector,\n\ttype IEntityStorageJoinOptions,\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 * Find all the entities which match the conditions, attaching to each one the entities from a\n\t * second storage connector whose join property matches. The join behaves like a left join by\n\t * default, a primary entity with no matches is still returned with an empty joined list, unless\n\t * joinRequired asks for an inner join and those entities are left out altogether.\n\t * @param joinConnector The connector holding the entities to join to.\n\t * @param joinOptions The properties to join on, the conditions, sort order, projection and\n\t * paging for the primary entities, the optional grouping and group conditions, and the optional\n\t * conditions, sort order and projection for the joined entities.\n\t * @returns All the entities for the storage matching the conditions with their joined entities,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async queryJoin<U>(\n\t\tjoinConnector: IEntityStorageConnector<U>,\n\t\tjoinOptions: IEntityStorageJoinOptions<T, U>\n\t): Promise<{ entities: (Partial<T> & { joined: Partial<U>[] })[]; cursor?: string }> {\n\t\treturn EntityStorageCommon.queryJoin(this, joinConnector, joinOptions);\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 * @param loggingComponentType The optional component type to use for logging skipped partition ids.\n\t * @returns The list of unique context ids.\n\t */\n\tpublic async getPartitionContextIds(\n\t\tloggingComponentType?: string\n\t): 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\t\tconst skipped = new Set<string>();\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\tconst split = EntityStorageHelper.tryShortSplit(\n\t\t\t\t\tthis._partitionContextIds ?? [],\n\t\t\t\t\tpartitionId\n\t\t\t\t);\n\t\t\t\tif (Is.undefined(split)) {\n\t\t\t\t\tskipped.add(partitionId);\n\t\t\t\t} else {\n\t\t\t\t\tcontextIds[partitionId] = split;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (skipped.size > 0) {\n\t\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(loggingComponentType);\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"partitionIdsSkipped\",\n\t\t\t\tdata: {\n\t\t\t\t\texpected: this._partitionContextIds?.length,\n\t\t\t\t\tpartitionIds: Array.from(skipped).join(\", \")\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 replacing the source store with the migrated store and removing the migration directory.\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\t// writeStore replaces the source store atomically, so a failure or a process death leaves\n\t\t// either the untouched source or the complete migrated store in place.\n\t\tconst migratedStore = await targetConnector.readStoreWithLock();\n\t\tawait this.withLock(async () => this.writeStore(migratedStore as unknown as T[]));\n\t\tawait rm(targetConnector._directory, { recursive: true, force: true });\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\tEntityStorageCommon,\n\tEntityStorageHelper,\n\ttype IEntityStorageConnector,\n\ttype IEntityStorageJoinOptions,\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 * Find all the entities which match the conditions, attaching to each one the entities from a\n\t * second storage connector whose join property matches. The join behaves like a left join by\n\t * default, a primary entity with no matches is still returned with an empty joined list, unless\n\t * joinRequired asks for an inner join and those entities are left out altogether.\n\t * @param joinConnector The connector holding the entities to join to.\n\t * @param joinOptions The properties to join on, the conditions, sort order, projection and\n\t * paging for the primary entities, the optional grouping and group conditions, and the optional\n\t * conditions, sort order and projection for the joined entities.\n\t * @returns All the entities for the storage matching the conditions with their joined entities,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async queryJoin<U>(\n\t\tjoinConnector: IEntityStorageConnector<U>,\n\t\tjoinOptions: IEntityStorageJoinOptions<T, U>\n\t): Promise<{ entities: (Partial<T> & { joined: Partial<U>[] })[]; cursor?: string }> {\n\t\treturn EntityStorageCommon.queryJoin(this, joinConnector, joinOptions);\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 * @param loggingComponentType The optional component type to use for logging skipped partition ids.\n\t * @returns The list of unique context ids.\n\t */\n\tpublic async getPartitionContextIds(\n\t\tloggingComponentType?: string\n\t): 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\t\tconst skipped = new Map<string, number>();\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\tconst split = EntityStorageHelper.tryShortSplit(\n\t\t\t\t\tthis._partitionContextIds ?? [],\n\t\t\t\t\tpartitionId\n\t\t\t\t);\n\t\t\t\tif (Is.undefined(split)) {\n\t\t\t\t\tskipped.set(partitionId, (skipped.get(partitionId) ?? 0) + 1);\n\t\t\t\t} else {\n\t\t\t\t\tcontextIds[partitionId] = split;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (skipped.size > 0) {\n\t\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(loggingComponentType);\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: FileEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"partitionIdsSkipped\",\n\t\t\t\tdata: {\n\t\t\t\t\texpected: this._partitionContextIds?.length,\n\t\t\t\t\tpartitionIds: Array.from(skipped, ([id, count]) => `${id}: ${count}`).join(\", \")\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 replacing the source store with the migrated store and removing the migration directory.\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\t// writeStore replaces the source store atomically, so a failure or a process death leaves\n\t\t// either the untouched source or the complete migrated store in place.\n\t\tconst migratedStore = await targetConnector.readStoreWithLock();\n\t\tawait this.withLock(async () => this.writeStore(migratedStore as unknown as T[]));\n\t\tawait rm(targetConnector._directory, { recursive: true, force: true });\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"]}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,110 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.0](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.11.0...entity-storage-connector-file-v0.11.0) (2026-09-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add production release automation ([1eb4c8e](https://github.com/iotaledger/twin-entity-storage/commit/1eb4c8ee3eb099defdfc2d063ae44935276dcae8))
|
|
9
|
+
* release to production ([a309051](https://github.com/iotaledger/twin-entity-storage/commit/a3090519adebf7943232b4df12e4c6bd5afe7eed))
|
|
10
|
+
* release to production ([#150](https://github.com/iotaledger/twin-entity-storage/issues/150)) ([4275601](https://github.com/iotaledger/twin-entity-storage/commit/42756015e853a837240f8aafdb0a8ebce2d836c1))
|
|
11
|
+
* release to production ([#176](https://github.com/iotaledger/twin-entity-storage/issues/176)) ([475d899](https://github.com/iotaledger/twin-entity-storage/commit/475d89965931d62cd3ed42f134fb567fc7972705))
|
|
12
|
+
* release to production ([#227](https://github.com/iotaledger/twin-entity-storage/issues/227)) ([c935228](https://github.com/iotaledger/twin-entity-storage/commit/c935228c14b7f0e8880088a102a76fcbecd5bf5a))
|
|
13
|
+
* release to production [skip ci] ([#268](https://github.com/iotaledger/twin-entity-storage/issues/268)) ([c63c37e](https://github.com/iotaledger/twin-entity-storage/commit/c63c37ef96281f7543cc1dddc80ab7deed4d91d4))
|
|
14
|
+
* release to production [skip ci] ([#326](https://github.com/iotaledger/twin-entity-storage/issues/326)) ([29dda4b](https://github.com/iotaledger/twin-entity-storage/commit/29dda4b6aa3e4979d529931efbb521bdc3020f56))
|
|
15
|
+
* update dependencies ([7ccc0c4](https://github.com/iotaledger/twin-entity-storage/commit/7ccc0c429125d073dc60b3de6cf101abc8cc6cba))
|
|
16
|
+
* use shared store mechanism ([#34](https://github.com/iotaledger/twin-entity-storage/issues/34)) ([68b6b71](https://github.com/iotaledger/twin-entity-storage/commit/68b6b71e7a96d7d016cd57bfff36775b56bf3f93))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* query params force coercion ([dd6aa87](https://github.com/iotaledger/twin-entity-storage/commit/dd6aa87efdfb60bab7d6756a86888863c45c51a7))
|
|
22
|
+
|
|
23
|
+
## [0.10.1-next.14](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.13...entity-storage-connector-file-v0.10.1-next.14) (2026-09-26)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Miscellaneous Chores
|
|
27
|
+
|
|
28
|
+
* **entity-storage-connector-file:** Synchronize repo versions
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Dependencies
|
|
32
|
+
|
|
33
|
+
* The following workspace dependencies were updated
|
|
34
|
+
* dependencies
|
|
35
|
+
* @twin.org/entity-storage-models bumped from 0.10.1-next.13 to 0.10.1-next.14
|
|
36
|
+
* devDependencies
|
|
37
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.10.1-next.13 to 0.10.1-next.14
|
|
38
|
+
|
|
39
|
+
## [0.10.1-next.13](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.12...entity-storage-connector-file-v0.10.1-next.13) (2026-09-24)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Bug Fixes
|
|
43
|
+
|
|
44
|
+
* keep the primary key last in the key set so cursor values stay aligned ([#316](https://github.com/iotaledger/twin-entity-storage/issues/316)) ([c1a5158](https://github.com/iotaledger/twin-entity-storage/commit/c1a51588326a8041898f6ba2db6a7e8e67370fb0))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Dependencies
|
|
48
|
+
|
|
49
|
+
* The following workspace dependencies were updated
|
|
50
|
+
* dependencies
|
|
51
|
+
* @twin.org/entity-storage-models bumped from 0.10.1-next.12 to 0.10.1-next.13
|
|
52
|
+
* devDependencies
|
|
53
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.10.1-next.12 to 0.10.1-next.13
|
|
54
|
+
|
|
55
|
+
## [0.10.1-next.12](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.11...entity-storage-connector-file-v0.10.1-next.12) (2026-09-23)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Features
|
|
59
|
+
|
|
60
|
+
* state that skipped partitions are removed by the rebuild and report their entity counts ([#310](https://github.com/iotaledger/twin-entity-storage/issues/310)) ([921baa3](https://github.com/iotaledger/twin-entity-storage/commit/921baa3e5855670b0f46eb105a54d33335738625))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Bug Fixes
|
|
64
|
+
|
|
65
|
+
* return rows with a null sort property when paging mysql and postgresql ([#313](https://github.com/iotaledger/twin-entity-storage/issues/313)) ([7ab339b](https://github.com/iotaledger/twin-entity-storage/commit/7ab339b516eb6a6591caebe559842d970918e09b))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Dependencies
|
|
69
|
+
|
|
70
|
+
* The following workspace dependencies were updated
|
|
71
|
+
* dependencies
|
|
72
|
+
* @twin.org/entity-storage-models bumped from 0.10.1-next.11 to 0.10.1-next.12
|
|
73
|
+
* devDependencies
|
|
74
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.10.1-next.11 to 0.10.1-next.12
|
|
75
|
+
|
|
76
|
+
## [0.10.1-next.11](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.10...entity-storage-connector-file-v0.10.1-next.11) (2026-09-23)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
### Miscellaneous Chores
|
|
80
|
+
|
|
81
|
+
* **entity-storage-connector-file:** Synchronize repo versions
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
### Dependencies
|
|
85
|
+
|
|
86
|
+
* The following workspace dependencies were updated
|
|
87
|
+
* dependencies
|
|
88
|
+
* @twin.org/entity-storage-models bumped from 0.10.1-next.10 to 0.10.1-next.11
|
|
89
|
+
* devDependencies
|
|
90
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.10.1-next.10 to 0.10.1-next.11
|
|
91
|
+
|
|
92
|
+
## [0.10.1-next.10](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.9...entity-storage-connector-file-v0.10.1-next.10) (2026-09-23)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Miscellaneous Chores
|
|
96
|
+
|
|
97
|
+
* **entity-storage-connector-file:** Synchronize repo versions
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
### Dependencies
|
|
101
|
+
|
|
102
|
+
* The following workspace dependencies were updated
|
|
103
|
+
* dependencies
|
|
104
|
+
* @twin.org/entity-storage-models bumped from 0.10.1-next.9 to 0.10.1-next.10
|
|
105
|
+
* devDependencies
|
|
106
|
+
* @twin.org/entity-storage-connector-memory bumped from 0.10.1-next.9 to 0.10.1-next.10
|
|
107
|
+
|
|
3
108
|
## [0.10.1-next.9](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-connector-file-v0.10.1-next.8...entity-storage-connector-file-v0.10.1-next.9) (2026-09-22)
|
|
4
109
|
|
|
5
110
|
|
package/locales/en.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"warn": {
|
|
12
12
|
"fileEntityStorageConnector": {
|
|
13
|
-
"partitionIdsSkipped": "
|
|
13
|
+
"partitionIdsSkipped": "Partition ids whose depth does not match the {expected} configured partition keys are not migrated and their entities are removed when the rebuilt storage replaces the original (partition id: entity count): {partitionIds}"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"error": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/entity-storage-connector-file",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "File-based connector that stores entities on disk for straightforward deployments.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"node": ">=24.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@twin.org/api-models": "
|
|
18
|
-
"@twin.org/context": "
|
|
19
|
-
"@twin.org/core": "
|
|
20
|
-
"@twin.org/entity": "
|
|
21
|
-
"@twin.org/entity-storage-models": "0.
|
|
22
|
-
"@twin.org/logging-models": "
|
|
23
|
-
"@twin.org/nameof": "
|
|
17
|
+
"@twin.org/api-models": "^0.10.0",
|
|
18
|
+
"@twin.org/context": "^0.11.0",
|
|
19
|
+
"@twin.org/core": "^0.11.0",
|
|
20
|
+
"@twin.org/entity": "^0.11.0",
|
|
21
|
+
"@twin.org/entity-storage-models": "^0.11.0",
|
|
22
|
+
"@twin.org/logging-models": "^0.10.0",
|
|
23
|
+
"@twin.org/nameof": "^0.11.0"
|
|
24
24
|
},
|
|
25
25
|
"main": "./dist/es/index.js",
|
|
26
26
|
"types": "./dist/types/index.d.ts",
|