@twin.org/entity-storage-connector-memory 0.0.2-next.9 → 0.0.3-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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # TWIN Entity Storage Connector Memory
1
+ # Entity Storage Connector Memory
2
2
 
3
- Entity Storage connector implementation using in-memory storage.
3
+ This package provides an in-memory backend suited to local development, automated testing and short-lived workloads. It is designed to work with the wider storage ecosystem so applications can keep behaviour consistent across connectors and environments.
4
4
 
5
5
  ## Installation
6
6
 
@@ -0,0 +1,5 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./memoryEntityStorageConnector.js";
4
+ export * from "./models/IMemoryEntityStorageConnectorConstructorOptions.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,mCAAmC,CAAC;AAClD,cAAc,6DAA6D,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./memoryEntityStorageConnector.js\";\nexport * from \"./models/IMemoryEntityStorageConnectorConstructorOptions.js\";\n"]}
@@ -0,0 +1,400 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
4
+ import { Coerce, ComponentFactory, Guards, HealthStatus, Is, ObjectHelper } from "@twin.org/core";
5
+ import { ComparisonOperator, EntityConditions, EntitySchemaFactory, EntitySchemaHelper, EntitySorter, LogicalOperator } from "@twin.org/entity";
6
+ /**
7
+ * Class for performing entity storage operations in-memory.
8
+ */
9
+ export class MemoryEntityStorageConnector {
10
+ /**
11
+ * Runtime name for the class.
12
+ */
13
+ static CLASS_NAME = "MemoryEntityStorageConnector";
14
+ /**
15
+ * Default limit for the number of items to return.
16
+ * @internal
17
+ */
18
+ static _DEFAULT_LIMIT = 40;
19
+ /**
20
+ * Partition key for the operation.
21
+ * @internal
22
+ */
23
+ static _PARTITION_KEY = "partitionId";
24
+ /**
25
+ * The schema for the entity.
26
+ * @internal
27
+ */
28
+ _entitySchema;
29
+ /**
30
+ * The keys to use from the context ids to create partitions.
31
+ * @internal
32
+ */
33
+ _partitionContextIds;
34
+ /**
35
+ * The primary key.
36
+ * @internal
37
+ */
38
+ _primaryKey;
39
+ /**
40
+ * The storage for the in-memory items.
41
+ * @internal
42
+ */
43
+ _store;
44
+ /**
45
+ * Create a new instance of MemoryEntityStorageConnector.
46
+ * @param options The options for the connector.
47
+ */
48
+ constructor(options) {
49
+ Guards.object(MemoryEntityStorageConnector.CLASS_NAME, "options", options);
50
+ Guards.stringValue(MemoryEntityStorageConnector.CLASS_NAME, "options.entitySchema", options.entitySchema);
51
+ this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
52
+ this._partitionContextIds = options.partitionContextIds;
53
+ this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
54
+ this._store = [];
55
+ }
56
+ /**
57
+ * Deep-clone condition tree and map `null` to `undefined` on Equals/NotEquals leaves
58
+ * so in-memory evaluation matches SQL-style "IS NULL" / "IS NOT NULL" semantics.
59
+ * @param condition The user-supplied condition (not mutated).
60
+ * @returns A clone safe to pass to {@link EntityConditions.check}.
61
+ * @internal
62
+ */
63
+ static normalizeNullToUndefined(condition) {
64
+ if ("conditions" in condition) {
65
+ return {
66
+ ...condition,
67
+ conditions: condition.conditions.map(c => MemoryEntityStorageConnector.normalizeNullToUndefined(c))
68
+ };
69
+ }
70
+ // In the non-group branch, `condition` is the leaf comparator.
71
+ const leaf = condition;
72
+ if ((leaf.comparison === ComparisonOperator.Equals ||
73
+ leaf.comparison === ComparisonOperator.NotEquals) &&
74
+ leaf.value === null) {
75
+ return { ...leaf, value: undefined };
76
+ }
77
+ return { ...leaf };
78
+ }
79
+ /**
80
+ * Returns the class name of the component.
81
+ * @returns The class name of the component.
82
+ */
83
+ className() {
84
+ return MemoryEntityStorageConnector.CLASS_NAME;
85
+ }
86
+ /**
87
+ * Returns the health status of the component.
88
+ * @returns The health status of the component.
89
+ */
90
+ async health() {
91
+ return [
92
+ {
93
+ source: MemoryEntityStorageConnector.CLASS_NAME,
94
+ status: HealthStatus.Ok,
95
+ description: "healthDescription"
96
+ }
97
+ ];
98
+ }
99
+ /**
100
+ * Get the schema for the entities.
101
+ * @returns The schema for the entities.
102
+ */
103
+ getSchema() {
104
+ return this._entitySchema;
105
+ }
106
+ /**
107
+ * Get an entity.
108
+ * @param id The id of the entity to get, or the index value if secondaryIndex is set.
109
+ * @param secondaryIndex Get the item using a secondary index.
110
+ * @param conditions The optional conditions to match for the entities.
111
+ * @returns The object if it can be found or undefined.
112
+ */
113
+ async get(id, secondaryIndex, conditions) {
114
+ Guards.stringValue(MemoryEntityStorageConnector.CLASS_NAME, "id", id);
115
+ const contextIds = await ContextIdStore.getContextIds();
116
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
117
+ const finalConditions = conditions ?? [];
118
+ if (Is.stringValue(partitionKey)) {
119
+ finalConditions.push({
120
+ property: MemoryEntityStorageConnector._PARTITION_KEY,
121
+ value: partitionKey
122
+ });
123
+ }
124
+ const index = this.findItem(id, secondaryIndex, finalConditions);
125
+ const item = index >= 0 ? ObjectHelper.clone(this._store[index]) : undefined;
126
+ if (Is.objectValue(item)) {
127
+ ObjectHelper.propertyDelete(item, MemoryEntityStorageConnector._PARTITION_KEY);
128
+ }
129
+ return item;
130
+ }
131
+ /**
132
+ * Set an entity.
133
+ * @param entity The entity to set.
134
+ * @param conditions The optional conditions to match for the entities.
135
+ * @returns The id of the entity.
136
+ */
137
+ async set(entity, conditions) {
138
+ Guards.object(MemoryEntityStorageConnector.CLASS_NAME, "entity", entity);
139
+ const contextIds = await ContextIdStore.getContextIds();
140
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
141
+ EntitySchemaHelper.validateEntity(entity, this.getSchema());
142
+ const finalConditions = conditions ?? [];
143
+ const finalEntity = ObjectHelper.clone(entity);
144
+ if (Is.stringValue(partitionKey)) {
145
+ finalConditions.push({
146
+ property: MemoryEntityStorageConnector._PARTITION_KEY,
147
+ value: partitionKey
148
+ });
149
+ ObjectHelper.propertySet(finalEntity, MemoryEntityStorageConnector._PARTITION_KEY, partitionKey);
150
+ }
151
+ const existingIndex = this.findItem(finalEntity[this._primaryKey.property], undefined, finalConditions);
152
+ if (existingIndex >= 0) {
153
+ this._store[existingIndex] = finalEntity;
154
+ }
155
+ else {
156
+ this._store.push(finalEntity);
157
+ }
158
+ }
159
+ /**
160
+ * Set multiple entities in a batch.
161
+ * @param entities The entities to set.
162
+ * @returns Nothing.
163
+ */
164
+ async setBatch(entities) {
165
+ Guards.arrayValue(MemoryEntityStorageConnector.CLASS_NAME, "entities", entities);
166
+ const contextIds = await ContextIdStore.getContextIds();
167
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
168
+ for (const entity of entities) {
169
+ EntitySchemaHelper.validateEntity(entity, this.getSchema());
170
+ }
171
+ const indexMap = new Map();
172
+ for (let i = 0; i < this._store.length; i++) {
173
+ const stored = this._store[i];
174
+ const storedPartition = ObjectHelper.propertyGet(stored, MemoryEntityStorageConnector._PARTITION_KEY);
175
+ if (!Is.stringValue(partitionKey) || storedPartition === partitionKey) {
176
+ indexMap.set(stored[this._primaryKey.property], i);
177
+ }
178
+ }
179
+ for (const entity of entities) {
180
+ const finalEntity = ObjectHelper.clone(entity);
181
+ if (Is.stringValue(partitionKey)) {
182
+ ObjectHelper.propertySet(finalEntity, MemoryEntityStorageConnector._PARTITION_KEY, partitionKey);
183
+ }
184
+ const id = finalEntity[this._primaryKey.property];
185
+ const existingIndex = indexMap.get(id);
186
+ if (existingIndex !== undefined) {
187
+ this._store[existingIndex] = finalEntity;
188
+ }
189
+ else {
190
+ const newIndex = this._store.push(finalEntity) - 1;
191
+ indexMap.set(id, newIndex);
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Remove the entity.
197
+ * @param id The id of the entity to remove.
198
+ * @param conditions The optional conditions to match for the entities.
199
+ * @returns Nothing.
200
+ */
201
+ async remove(id, conditions) {
202
+ Guards.stringValue(MemoryEntityStorageConnector.CLASS_NAME, "id", id);
203
+ const contextIds = await ContextIdStore.getContextIds();
204
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
205
+ const finalConditions = conditions ?? [];
206
+ if (Is.stringValue(partitionKey)) {
207
+ finalConditions.push({
208
+ property: MemoryEntityStorageConnector._PARTITION_KEY,
209
+ value: partitionKey
210
+ });
211
+ }
212
+ const index = this.findItem(id, undefined, finalConditions);
213
+ if (index >= 0) {
214
+ this._store.splice(index, 1);
215
+ }
216
+ }
217
+ /**
218
+ * Find all the entities which match the conditions.
219
+ * @param conditions The conditions to match for the entities.
220
+ * @param sortProperties The optional sort order.
221
+ * @param properties The optional properties to return, defaults to all.
222
+ * @param cursor The cursor to request the next chunk of entities.
223
+ * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
224
+ * @returns All the entities for the storage matching the conditions,
225
+ * and a cursor which can be used to request more entities.
226
+ */
227
+ async query(conditions, sortProperties, properties, cursor, limit) {
228
+ const contextIds = await ContextIdStore.getContextIds();
229
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
230
+ let allEntities = this._store.slice();
231
+ const finalConditions = {
232
+ conditions: [],
233
+ logicalOperator: LogicalOperator.And
234
+ };
235
+ if (Is.stringValue(partitionKey)) {
236
+ finalConditions.conditions.push({
237
+ property: MemoryEntityStorageConnector._PARTITION_KEY,
238
+ comparison: ComparisonOperator.Equals,
239
+ value: partitionKey
240
+ });
241
+ }
242
+ if (!Is.empty(conditions)) {
243
+ finalConditions.conditions.push(MemoryEntityStorageConnector.normalizeNullToUndefined(conditions));
244
+ }
245
+ const entities = [];
246
+ const finalLimit = limit ?? MemoryEntityStorageConnector._DEFAULT_LIMIT;
247
+ let nextCursor;
248
+ if (allEntities.length > 0) {
249
+ const finalSortKeys = EntitySchemaHelper.buildSortProperties(this._entitySchema, sortProperties);
250
+ allEntities = EntitySorter.sort(allEntities, finalSortKeys);
251
+ const startIndex = Coerce.number(cursor) ?? 0;
252
+ for (let i = startIndex; i < allEntities.length; i++) {
253
+ if (EntityConditions.check(allEntities[i], finalConditions) &&
254
+ entities.length < finalLimit) {
255
+ const entity = ObjectHelper.clone(ObjectHelper.pick(allEntities[i], properties));
256
+ ObjectHelper.propertyDelete(entity, MemoryEntityStorageConnector._PARTITION_KEY);
257
+ entities.push(entity);
258
+ if (entities.length >= finalLimit) {
259
+ if (i < allEntities.length - 1) {
260
+ nextCursor = (i + 1).toString();
261
+ }
262
+ break;
263
+ }
264
+ }
265
+ }
266
+ }
267
+ return {
268
+ entities,
269
+ cursor: nextCursor
270
+ };
271
+ }
272
+ /**
273
+ * Remove all entities from the storage.
274
+ * @returns Nothing.
275
+ */
276
+ async empty() {
277
+ const contextIds = await ContextIdStore.getContextIds();
278
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
279
+ if (Is.stringValue(partitionKey)) {
280
+ for (let i = this._store.length - 1; i >= 0; i--) {
281
+ if (ObjectHelper.propertyGet(this._store[i], MemoryEntityStorageConnector._PARTITION_KEY) === partitionKey) {
282
+ this._store.splice(i, 1);
283
+ }
284
+ }
285
+ }
286
+ else {
287
+ this._store.splice(0, this._store.length);
288
+ }
289
+ }
290
+ /**
291
+ * Remove multiple entities by id.
292
+ * @param ids The ids of the entities to remove.
293
+ * @returns Nothing.
294
+ */
295
+ async removeBatch(ids) {
296
+ Guards.arrayValue(MemoryEntityStorageConnector.CLASS_NAME, "ids", ids);
297
+ const contextIds = await ContextIdStore.getContextIds();
298
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
299
+ const finalConditions = [];
300
+ if (Is.stringValue(partitionKey)) {
301
+ finalConditions.push({
302
+ property: MemoryEntityStorageConnector._PARTITION_KEY,
303
+ value: partitionKey
304
+ });
305
+ }
306
+ for (const id of ids) {
307
+ const index = this.findItem(id, undefined, finalConditions);
308
+ if (index >= 0) {
309
+ this._store.splice(index, 1);
310
+ }
311
+ }
312
+ }
313
+ /**
314
+ * Teardown the storage by clearing the underlying store.
315
+ * @param nodeLoggingComponentType The node logging component type.
316
+ * @returns True if the teardown process was successful.
317
+ */
318
+ async teardown(nodeLoggingComponentType) {
319
+ const nodeLogging = ComponentFactory.getIfExists(nodeLoggingComponentType);
320
+ await nodeLogging?.log({
321
+ level: "info",
322
+ source: MemoryEntityStorageConnector.CLASS_NAME,
323
+ ts: Date.now(),
324
+ message: "storeTearingDown"
325
+ });
326
+ this._store.splice(0, this._store.length);
327
+ await nodeLogging?.log({
328
+ level: "info",
329
+ source: MemoryEntityStorageConnector.CLASS_NAME,
330
+ ts: Date.now(),
331
+ message: "storeTornDown"
332
+ });
333
+ return true;
334
+ }
335
+ /**
336
+ * Count all the entities which match the conditions.
337
+ * @returns The total count of entities in the storage.
338
+ */
339
+ async count() {
340
+ const contextIds = await ContextIdStore.getContextIds();
341
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
342
+ if (!Is.stringValue(partitionKey)) {
343
+ return this._store.length;
344
+ }
345
+ return this._store.filter(item => ObjectHelper.propertyGet(item, MemoryEntityStorageConnector._PARTITION_KEY) ===
346
+ partitionKey).length;
347
+ }
348
+ /**
349
+ * Get the memory store.
350
+ * @returns The store.
351
+ */
352
+ getStore() {
353
+ return this._store;
354
+ }
355
+ /**
356
+ * Find the item in the store.
357
+ * @param id The id to search for.
358
+ * @param secondaryIndex The secondary index to search for.
359
+ * @param conditions The optional conditions to match for the entities.
360
+ * @returns The index of the item if found or -1.
361
+ * @internal
362
+ */
363
+ findItem(id, secondaryIndex, conditions) {
364
+ const finalConditions = [];
365
+ if (!Is.empty(secondaryIndex)) {
366
+ finalConditions.push({
367
+ property: secondaryIndex,
368
+ comparison: ComparisonOperator.Equals,
369
+ value: id
370
+ });
371
+ }
372
+ if (Is.arrayValue(conditions)) {
373
+ // If we haven't added a secondary index condition we need to add the primary key condition.
374
+ if (finalConditions.length === 0) {
375
+ finalConditions.push({
376
+ property: this._primaryKey.property,
377
+ comparison: ComparisonOperator.Equals,
378
+ value: id
379
+ });
380
+ }
381
+ finalConditions.push(...conditions.map(c => ({
382
+ property: c.property,
383
+ comparison: ComparisonOperator.Equals,
384
+ value: c.value
385
+ })));
386
+ }
387
+ if (finalConditions.length > 0) {
388
+ for (let i = 0; i < this._store.length; i++) {
389
+ if (EntityConditions.check(this._store[i], { conditions: finalConditions })) {
390
+ return i;
391
+ }
392
+ }
393
+ }
394
+ else {
395
+ return this._store.findIndex(e => e[this._primaryKey.property] === id);
396
+ }
397
+ return -1;
398
+ }
399
+ }
400
+ //# sourceMappingURL=memoryEntityStorageConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryEntityStorageConnector.js","sourceRoot":"","sources":["../../src/memoryEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tCoerce,\n\tComponentFactory,\n\tGuards,\n\tHealthStatus,\n\ttype IHealth,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\tEntityConditions,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\tEntitySorter,\n\tLogicalOperator,\n\ttype EntityCondition,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport type { IEntityStorageConnector } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IMemoryEntityStorageConnectorConstructorOptions } from \"./models/IMemoryEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations in-memory.\n */\nexport class MemoryEntityStorageConnector<T = unknown> implements IEntityStorageConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MemoryEntityStorageConnector>();\n\n\t/**\n\t * Default limit for the number of items to return.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 40;\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 * 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 storage for the in-memory items.\n\t * @internal\n\t */\n\tprivate readonly _store: T[];\n\n\t/**\n\t * Create a new instance of MemoryEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IMemoryEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(MemoryEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tMemoryEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\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._store = [];\n\t}\n\n\t/**\n\t * Deep-clone condition tree and map `null` to `undefined` on Equals/NotEquals leaves\n\t * so in-memory evaluation matches SQL-style \"IS NULL\" / \"IS NOT NULL\" semantics.\n\t * @param condition The user-supplied condition (not mutated).\n\t * @returns A clone safe to pass to {@link EntityConditions.check}.\n\t * @internal\n\t */\n\tprivate static normalizeNullToUndefined<T>(condition: EntityCondition<T>): EntityCondition<T> {\n\t\tif (\"conditions\" in condition) {\n\t\t\treturn {\n\t\t\t\t...condition,\n\t\t\t\tconditions: condition.conditions.map(c =>\n\t\t\t\t\tMemoryEntityStorageConnector.normalizeNullToUndefined(c)\n\t\t\t\t)\n\t\t\t};\n\t\t}\n\n\t\t// In the non-group branch, `condition` is the leaf comparator.\n\t\tconst leaf = condition;\n\t\tif (\n\t\t\t(leaf.comparison === ComparisonOperator.Equals ||\n\t\t\t\tleaf.comparison === ComparisonOperator.NotEquals) &&\n\t\t\tleaf.value === null\n\t\t) {\n\t\t\treturn { ...leaf, value: undefined };\n\t\t}\n\t\treturn { ...leaf };\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 MemoryEntityStorageConnector.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\treturn [\n\t\t\t{\n\t\t\t\tsource: MemoryEntityStorageConnector.CLASS_NAME,\n\t\t\t\tstatus: HealthStatus.Ok,\n\t\t\t\tdescription: \"healthDescription\"\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(MemoryEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst finalConditions = conditions ?? [];\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: MemoryEntityStorageConnector._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(id, secondaryIndex, finalConditions);\n\t\tconst item = index >= 0 ? ObjectHelper.clone(this._store[index]) : undefined;\n\n\t\tif (Is.objectValue(item)) {\n\t\t\tObjectHelper.propertyDelete(item, MemoryEntityStorageConnector._PARTITION_KEY);\n\t\t}\n\n\t\treturn item;\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>(MemoryEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tEntitySchemaHelper.validateEntity(entity, this.getSchema());\n\n\t\tconst finalConditions = conditions ?? [];\n\t\tconst finalEntity = ObjectHelper.clone(entity);\n\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: MemoryEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t\tObjectHelper.propertySet(\n\t\t\t\tfinalEntity,\n\t\t\t\tMemoryEntityStorageConnector._PARTITION_KEY,\n\t\t\t\tpartitionKey\n\t\t\t);\n\t\t}\n\n\t\tconst existingIndex = this.findItem(\n\t\t\tfinalEntity[this._primaryKey.property] as string,\n\t\t\tundefined,\n\t\t\tfinalConditions\n\t\t);\n\t\tif (existingIndex >= 0) {\n\t\t\tthis._store[existingIndex] = finalEntity;\n\t\t} else {\n\t\t\tthis._store.push(finalEntity);\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(MemoryEntityStorageConnector.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\tfor (const entity of entities) {\n\t\t\tEntitySchemaHelper.validateEntity(entity, this.getSchema());\n\t\t}\n\n\t\tconst indexMap = new Map<string, number>();\n\t\tfor (let i = 0; i < this._store.length; i++) {\n\t\t\tconst stored = this._store[i];\n\t\t\tconst storedPartition = ObjectHelper.propertyGet(\n\t\t\t\tstored as object,\n\t\t\t\tMemoryEntityStorageConnector._PARTITION_KEY\n\t\t\t);\n\t\t\tif (!Is.stringValue(partitionKey) || storedPartition === partitionKey) {\n\t\t\t\tindexMap.set(stored[this._primaryKey.property] as string, i);\n\t\t\t}\n\t\t}\n\n\t\tfor (const entity of entities) {\n\t\t\tconst finalEntity = ObjectHelper.clone(entity);\n\t\t\tif (Is.stringValue(partitionKey)) {\n\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\tfinalEntity,\n\t\t\t\t\tMemoryEntityStorageConnector._PARTITION_KEY,\n\t\t\t\t\tpartitionKey\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst id = finalEntity[this._primaryKey.property] as string;\n\t\t\tconst existingIndex = indexMap.get(id);\n\t\t\tif (existingIndex !== undefined) {\n\t\t\t\tthis._store[existingIndex] = finalEntity;\n\t\t\t} else {\n\t\t\t\tconst newIndex = this._store.push(finalEntity) - 1;\n\t\t\t\tindexMap.set(id, newIndex);\n\t\t\t}\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(MemoryEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst finalConditions = conditions ?? [];\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: MemoryEntityStorageConnector._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(id, undefined, finalConditions);\n\n\t\tif (index >= 0) {\n\t\t\tthis._store.splice(index, 1);\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\tlet allEntities = this._store.slice();\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: MemoryEntityStorageConnector._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(\n\t\t\t\tMemoryEntityStorageConnector.normalizeNullToUndefined(conditions)\n\t\t\t);\n\t\t}\n\n\t\tconst entities = [];\n\t\tconst finalLimit = limit ?? MemoryEntityStorageConnector._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 = ObjectHelper.clone(ObjectHelper.pick(allEntities[i], properties));\n\t\t\t\t\tObjectHelper.propertyDelete(entity, MemoryEntityStorageConnector._PARTITION_KEY);\n\t\t\t\t\tentities.push(entity);\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 * 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\tif (Is.stringValue(partitionKey)) {\n\t\t\tfor (let i = this._store.length - 1; i >= 0; i--) {\n\t\t\t\tif (\n\t\t\t\t\tObjectHelper.propertyGet(\n\t\t\t\t\t\tthis._store[i] as object,\n\t\t\t\t\t\tMemoryEntityStorageConnector._PARTITION_KEY\n\t\t\t\t\t) === partitionKey\n\t\t\t\t) {\n\t\t\t\t\tthis._store.splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._store.splice(0, this._store.length);\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(MemoryEntityStorageConnector.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\tconst finalConditions: { property: keyof T; value: unknown }[] = [];\n\t\tif (Is.stringValue(partitionKey)) {\n\t\t\tfinalConditions.push({\n\t\t\t\tproperty: MemoryEntityStorageConnector._PARTITION_KEY as keyof T,\n\t\t\t\tvalue: partitionKey\n\t\t\t});\n\t\t}\n\n\t\tfor (const id of ids) {\n\t\t\tconst index = this.findItem(id, undefined, finalConditions);\n\t\t\tif (index >= 0) {\n\t\t\t\tthis._store.splice(index, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Teardown the storage by clearing the underlying store.\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: MemoryEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"storeTearingDown\"\n\t\t});\n\n\t\tthis._store.splice(0, this._store.length);\n\n\t\tawait nodeLogging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: MemoryEntityStorageConnector.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"storeTornDown\"\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @returns The total count of entities in the storage.\n\t */\n\tpublic async count(): Promise<number> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tif (!Is.stringValue(partitionKey)) {\n\t\t\treturn this._store.length;\n\t\t}\n\n\t\treturn this._store.filter(\n\t\t\titem =>\n\t\t\t\tObjectHelper.propertyGet(item as object, MemoryEntityStorageConnector._PARTITION_KEY) ===\n\t\t\t\tpartitionKey\n\t\t).length;\n\t}\n\n\t/**\n\t * Get the memory store.\n\t * @returns The store.\n\t */\n\tpublic getStore(): T[] {\n\t\treturn this._store;\n\t}\n\n\t/**\n\t * Find the item in the store.\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\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 < this._store.length; i++) {\n\t\t\t\tif (EntityConditions.check(this._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 this._store.findIndex(e => e[this._primaryKey.property] === id);\n\t\t}\n\n\t\treturn -1;\n\t}\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IMemoryEntityStorageConnectorConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IMemoryEntityStorageConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IMemoryEntityStorageConnectorConstructorOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Options for the Memory Entity Storage Connector constructor.\n */\nexport interface IMemoryEntityStorageConnectorConstructorOptions {\n\t/**\n\t * The schema for the entity.\n\t */\n\tentitySchema: string;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t */\n\tpartitionContextIds?: string[];\n}\n"]}
@@ -1,2 +1,2 @@
1
- export * from "./memoryEntityStorageConnector";
2
- export * from "./models/IMemoryEntityStorageConnectorConstructorOptions";
1
+ export * from "./memoryEntityStorageConnector.js";
2
+ export * from "./models/IMemoryEntityStorageConnectorConstructorOptions.js";
@@ -1,6 +1,7 @@
1
+ import { type IHealth } from "@twin.org/core";
1
2
  import { type EntityCondition, type IEntitySchema, type SortDirection } from "@twin.org/entity";
2
3
  import type { IEntityStorageConnector } from "@twin.org/entity-storage-models";
3
- import type { IMemoryEntityStorageConnectorConstructorOptions } from "./models/IMemoryEntityStorageConnectorConstructorOptions";
4
+ import type { IMemoryEntityStorageConnectorConstructorOptions } from "./models/IMemoryEntityStorageConnectorConstructorOptions.js";
4
5
  /**
5
6
  * Class for performing entity storage operations in-memory.
6
7
  */
@@ -8,12 +9,22 @@ export declare class MemoryEntityStorageConnector<T = unknown> implements IEntit
8
9
  /**
9
10
  * Runtime name for the class.
10
11
  */
11
- readonly CLASS_NAME: string;
12
+ static readonly CLASS_NAME: string;
12
13
  /**
13
14
  * Create a new instance of MemoryEntityStorageConnector.
14
15
  * @param options The options for the connector.
15
16
  */
16
17
  constructor(options: IMemoryEntityStorageConnectorConstructorOptions);
18
+ /**
19
+ * Returns the class name of the component.
20
+ * @returns The class name of the component.
21
+ */
22
+ className(): string;
23
+ /**
24
+ * Returns the health status of the component.
25
+ * @returns The health status of the component.
26
+ */
27
+ health(): Promise<IHealth[]>;
17
28
  /**
18
29
  * Get the schema for the entities.
19
30
  * @returns The schema for the entities.
@@ -40,6 +51,12 @@ export declare class MemoryEntityStorageConnector<T = unknown> implements IEntit
40
51
  property: keyof T;
41
52
  value: unknown;
42
53
  }[]): Promise<void>;
54
+ /**
55
+ * Set multiple entities in a batch.
56
+ * @param entities The entities to set.
57
+ * @returns Nothing.
58
+ */
59
+ setBatch(entities: T[]): Promise<void>;
43
60
  /**
44
61
  * Remove the entity.
45
62
  * @param id The id of the entity to remove.
@@ -55,15 +72,15 @@ export declare class MemoryEntityStorageConnector<T = unknown> implements IEntit
55
72
  * @param conditions The conditions to match for the entities.
56
73
  * @param sortProperties The optional sort order.
57
74
  * @param properties The optional properties to return, defaults to all.
58
- * @param cursor The cursor to request the next page of entities.
59
- * @param pageSize The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
75
+ * @param cursor The cursor to request the next chunk of entities.
76
+ * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
60
77
  * @returns All the entities for the storage matching the conditions,
61
78
  * and a cursor which can be used to request more entities.
62
79
  */
63
80
  query(conditions?: EntityCondition<T>, sortProperties?: {
64
81
  property: keyof T;
65
82
  sortDirection: SortDirection;
66
- }[], properties?: (keyof T)[], cursor?: string, pageSize?: number): Promise<{
83
+ }[], properties?: (keyof T)[], cursor?: string, limit?: number): Promise<{
67
84
  /**
68
85
  * The entities, which can be partial if a limited keys list was provided.
69
86
  */
@@ -73,6 +90,28 @@ export declare class MemoryEntityStorageConnector<T = unknown> implements IEntit
73
90
  */
74
91
  cursor?: string;
75
92
  }>;
93
+ /**
94
+ * Remove all entities from the storage.
95
+ * @returns Nothing.
96
+ */
97
+ empty(): Promise<void>;
98
+ /**
99
+ * Remove multiple entities by id.
100
+ * @param ids The ids of the entities to remove.
101
+ * @returns Nothing.
102
+ */
103
+ removeBatch(ids: string[]): Promise<void>;
104
+ /**
105
+ * Teardown the storage by clearing the underlying store.
106
+ * @param nodeLoggingComponentType The node logging component type.
107
+ * @returns True if the teardown process was successful.
108
+ */
109
+ teardown(nodeLoggingComponentType?: string): Promise<boolean>;
110
+ /**
111
+ * Count all the entities which match the conditions.
112
+ * @returns The total count of entities in the storage.
113
+ */
114
+ count(): Promise<number>;
76
115
  /**
77
116
  * Get the memory store.
78
117
  * @returns The store.
@@ -6,4 +6,8 @@ export interface IMemoryEntityStorageConnectorConstructorOptions {
6
6
  * The schema for the entity.
7
7
  */
8
8
  entitySchema: string;
9
+ /**
10
+ * The keys to use from the context ids to create partitions.
11
+ */
12
+ partitionContextIds?: string[];
9
13
  }