@twin.org/entity-storage-connector-synchronised 0.0.2-next.8 → 0.0.3-next.1

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.
@@ -0,0 +1,6 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./synchronisedEntityStorageConnector.js";
4
+ export * from "./models/ISynchronisedEntityStorageConnectorConfig.js";
5
+ export * from "./models/ISynchronisedEntityStorageConnectorConstructorOptions.js";
6
+ //# 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,yCAAyC,CAAC;AACxD,cAAc,uDAAuD,CAAC;AACtE,cAAc,mEAAmE,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./synchronisedEntityStorageConnector.js\";\nexport * from \"./models/ISynchronisedEntityStorageConnectorConfig.js\";\nexport * from \"./models/ISynchronisedEntityStorageConnectorConstructorOptions.js\";\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=ISynchronisedEntityStorageConnectorConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ISynchronisedEntityStorageConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/ISynchronisedEntityStorageConnectorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the Synchronised Entity Storage Connector.\n */\nexport interface ISynchronisedEntityStorageConnectorConfig {\n\t/**\n\t * The storage key for the synchronised entity storage connector.\n\t * Will default to kebab cased entity schema name.\n\t */\n\tstorageKey?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ISynchronisedEntityStorageConnectorConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ISynchronisedEntityStorageConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ISynchronisedEntityStorageConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ISynchronisedEntityStorageConnectorConfig } from \"./ISynchronisedEntityStorageConnectorConfig.js\";\n\n/**\n * Options for the Synchronised Entity Storage Connector constructor.\n */\nexport interface ISynchronisedEntityStorageConnectorConstructorOptions {\n\t/**\n\t * The name of the entity schema.\n\t */\n\tentitySchema: string;\n\n\t/**\n\t * The entity storage connector type to use for actual data.\n\t */\n\tentityStorageConnectorType: string;\n\n\t/**\n\t * The event bus component type.\n\t * @default event-bus\n\t */\n\teventBusComponentType?: string;\n\n\t/**\n\t * The configuration for the connector.\n\t */\n\tconfig?: ISynchronisedEntityStorageConnectorConfig;\n}\n"]}
@@ -1,18 +1,33 @@
1
- import { Guards, StringHelper, Is, GeneralError, ComponentFactory } from '@twin.org/core';
2
- import { EntitySchemaFactory, EntitySchemaHelper, ComparisonOperator, SortDirection } from '@twin.org/entity';
3
- import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
4
- import { SynchronisedStorageTopics, SyncChangeOperation, SyncNodeIdentityMode } from '@twin.org/synchronised-storage-models';
5
-
6
1
  // Copyright 2024 IOTA Stiftung.
7
2
  // SPDX-License-Identifier: Apache-2.0.
3
+ import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
4
+ import { ComponentFactory, GeneralError, Guards, Is, StringHelper } from "@twin.org/core";
5
+ import { ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper, SortDirection } from "@twin.org/entity";
6
+ import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
7
+ import { SyncChangeOperation, SynchronisedStorageTopics, SyncNodeIdMode } from "@twin.org/synchronised-storage-models";
8
8
  /**
9
9
  * Class for performing entity storage operations in synchronised storage.
10
10
  */
11
- class SynchronisedEntityStorageConnector {
11
+ export class SynchronisedEntityStorageConnector {
12
12
  /**
13
13
  * Runtime name for the class.
14
14
  */
15
- CLASS_NAME = "SynchronisedEntityStorageConnector";
15
+ static CLASS_NAME = "SynchronisedEntityStorageConnector";
16
+ /**
17
+ * Fixed name for node id properties.
18
+ * @internal
19
+ */
20
+ static _PROP_NAME_NODE_ID = "nodeId";
21
+ /**
22
+ * Fixed name for date modified properties.
23
+ * @internal
24
+ */
25
+ static _PROP_NAME_DATE_MODIFIED = "dateModified";
26
+ /**
27
+ * Fixed name for id properties.
28
+ * @internal
29
+ */
30
+ static _PROP_NAME_ID = "id";
16
31
  /**
17
32
  * The schema for the entity.
18
33
  * @internal
@@ -42,32 +57,32 @@ class SynchronisedEntityStorageConnector {
42
57
  * The node identity.
43
58
  * @internal
44
59
  */
45
- _nodeIdentity;
60
+ _nodeId;
46
61
  /**
47
62
  * Create a new instance of SynchronisedEntityStorageConnector.
48
63
  * @param options The options for the connector.
49
64
  */
50
65
  constructor(options) {
51
- Guards.object(this.CLASS_NAME, "options", options);
52
- Guards.stringValue(this.CLASS_NAME, "options.entitySchema", options.entitySchema);
53
- Guards.stringValue(this.CLASS_NAME, "options.entityStorageConnectorType", options.entityStorageConnectorType);
66
+ Guards.object(SynchronisedEntityStorageConnector.CLASS_NAME, "options", options);
67
+ Guards.stringValue(SynchronisedEntityStorageConnector.CLASS_NAME, "options.entitySchema", options.entitySchema);
68
+ Guards.stringValue(SynchronisedEntityStorageConnector.CLASS_NAME, "options.entityStorageConnectorType", options.entityStorageConnectorType);
54
69
  this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
55
70
  this._storageKey = options?.config?.storageKey ?? StringHelper.kebabCase(options.entitySchema);
56
71
  this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
57
72
  const requiredProperties = [
58
- "id",
59
- "nodeIdentity",
60
- "dateModified"
73
+ SynchronisedEntityStorageConnector._PROP_NAME_ID,
74
+ SynchronisedEntityStorageConnector._PROP_NAME_NODE_ID,
75
+ SynchronisedEntityStorageConnector._PROP_NAME_DATE_MODIFIED
61
76
  ];
62
77
  for (const requiredProperty of requiredProperties) {
63
78
  const foundProperty = this._entitySchema.properties?.find(prop => prop.property === requiredProperty);
64
79
  if (Is.empty(foundProperty)) {
65
- throw new GeneralError(this.CLASS_NAME, "missingRequiredProperty", { requiredProperty });
80
+ throw new GeneralError(SynchronisedEntityStorageConnector.CLASS_NAME, "missingRequiredProperty", { requiredProperty });
66
81
  }
67
82
  else if (Is.empty(foundProperty.isPrimary) &&
68
83
  Is.empty(foundProperty.isSecondary) &&
69
84
  Is.empty(foundProperty.sortDirection)) {
70
- throw new GeneralError(this.CLASS_NAME, "missingRequiredPropertySort", {
85
+ throw new GeneralError(SynchronisedEntityStorageConnector.CLASS_NAME, "missingRequiredPropertySort", {
71
86
  requiredProperty
72
87
  });
73
88
  }
@@ -75,6 +90,13 @@ class SynchronisedEntityStorageConnector {
75
90
  this._entityStorageConnector = EntityStorageConnectorFactory.get(options.entityStorageConnectorType);
76
91
  this._eventBusComponent = ComponentFactory.get(options.eventBusComponentType ?? "event-bus");
77
92
  }
93
+ /**
94
+ * Returns the class name of the component.
95
+ * @returns The class name of the component.
96
+ */
97
+ className() {
98
+ return SynchronisedEntityStorageConnector.CLASS_NAME;
99
+ }
78
100
  /**
79
101
  * Get the schema for the entities.
80
102
  * @returns The schema for the entities.
@@ -84,17 +106,18 @@ class SynchronisedEntityStorageConnector {
84
106
  }
85
107
  /**
86
108
  * The component needs to be started when the node is initialized.
87
- * @param nodeIdentity The identity of the node starting the component.
88
109
  * @param nodeLoggingComponentType The node logging component type.
89
110
  * @returns Nothing.
90
111
  */
91
- async start(nodeIdentity, nodeLoggingComponentType) {
92
- this._nodeIdentity = nodeIdentity;
112
+ async start(nodeLoggingComponentType) {
113
+ const contextIds = await ContextIdStore.getContextIds();
114
+ ContextIdHelper.guard(contextIds, ContextIdKeys.Node);
115
+ this._nodeId = contextIds[ContextIdKeys.Node];
93
116
  // Tell the synchronised storage about this storage key
94
117
  await this._eventBusComponent.publish(SynchronisedStorageTopics.RegisterStorageKey, {
95
118
  storageKey: this._storageKey
96
119
  });
97
- this.handleEventBusMessages();
120
+ await this.handleEventBusMessages();
98
121
  }
99
122
  /**
100
123
  * Get an entity.
@@ -104,7 +127,7 @@ class SynchronisedEntityStorageConnector {
104
127
  * @returns The object if it can be found or undefined.
105
128
  */
106
129
  async get(id, secondaryIndex, conditions) {
107
- Guards.stringValue(this.CLASS_NAME, "id", id);
130
+ Guards.stringValue(SynchronisedEntityStorageConnector.CLASS_NAME, "id", id);
108
131
  return this._entityStorageConnector.get(id, secondaryIndex, conditions);
109
132
  }
110
133
  /**
@@ -114,17 +137,17 @@ class SynchronisedEntityStorageConnector {
114
137
  * @returns The id of the entity.
115
138
  */
116
139
  async set(entity, conditions) {
117
- Guards.object(this.CLASS_NAME, "entity", entity);
118
- if (Is.stringValue(this._nodeIdentity)) {
119
- // Make sure the entity has the required properties
140
+ Guards.object(SynchronisedEntityStorageConnector.CLASS_NAME, "entity", entity);
141
+ if (Is.stringValue(this._nodeId)) {
142
+ // Make sure the entity has the required properties populated
120
143
  entity.dateModified = new Date(Date.now()).toISOString();
121
- entity.nodeIdentity = this._nodeIdentity;
144
+ entity.nodeId = this._nodeId;
122
145
  await this._entityStorageConnector.set(entity, conditions);
123
146
  // Tell the synchronised storage about the entity changes
124
147
  await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
125
148
  storageKey: this._storageKey,
126
149
  operation: SyncChangeOperation.Set,
127
- nodeIdentity: this._nodeIdentity,
150
+ nodeId: this._nodeId,
128
151
  id: entity[this._primaryKey.property]
129
152
  });
130
153
  }
@@ -136,14 +159,14 @@ class SynchronisedEntityStorageConnector {
136
159
  * @returns Nothing.
137
160
  */
138
161
  async remove(id, conditions) {
139
- Guards.stringValue(this.CLASS_NAME, "id", id);
140
- if (Is.stringValue(this._nodeIdentity)) {
162
+ Guards.stringValue(SynchronisedEntityStorageConnector.CLASS_NAME, "id", id);
163
+ if (Is.stringValue(this._nodeId)) {
141
164
  await this._entityStorageConnector.remove(id, conditions);
142
165
  // Tell the synchronised storage about the entity removal
143
166
  await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
144
167
  storageKey: this._storageKey,
145
168
  operation: SyncChangeOperation.Delete,
146
- nodeIdentity: this._nodeIdentity,
169
+ nodeId: this._nodeId,
147
170
  id
148
171
  });
149
172
  }
@@ -153,37 +176,38 @@ class SynchronisedEntityStorageConnector {
153
176
  * @param conditions The conditions to match for the entities.
154
177
  * @param sortProperties The optional sort order.
155
178
  * @param properties The optional properties to return, defaults to all.
156
- * @param cursor The cursor to request the next page of entities.
157
- * @param pageSize The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
179
+ * @param cursor The cursor to request the next chunk of entities.
180
+ * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
158
181
  * @returns All the entities for the storage matching the conditions,
159
182
  * and a cursor which can be used to request more entities.
160
183
  */
161
- async query(conditions, sortProperties, properties, cursor, pageSize) {
162
- return this._entityStorageConnector.query(conditions, sortProperties, properties, cursor, pageSize);
184
+ async query(conditions, sortProperties, properties, cursor, limit) {
185
+ // We deliberately skip the partition key as we want to query all partitions in synchronised storage
186
+ return this._entityStorageConnector.query(conditions, sortProperties, properties, cursor, limit);
163
187
  }
164
188
  /**
165
189
  * Handle the event bus messages.
166
190
  * @internal
167
191
  */
168
- handleEventBusMessages() {
192
+ async handleEventBusMessages() {
169
193
  // When the synchronised storage requests an item, we need to provide it
170
- this._eventBusComponent.subscribe(SynchronisedStorageTopics.LocalItemRequest, async (params) => {
194
+ await this._eventBusComponent.subscribe(SynchronisedStorageTopics.LocalItemRequest, async (params) => {
171
195
  await this.handleLocalItemRequest(params);
172
196
  });
173
197
  // When the synchronised storage requests a batch, we need to provide it
174
- this._eventBusComponent.subscribe(SynchronisedStorageTopics.BatchRequest, async (params) => {
198
+ await this._eventBusComponent.subscribe(SynchronisedStorageTopics.BatchRequest, async (params) => {
175
199
  await this.handleBatchRequest(params);
176
200
  });
177
201
  // Subscribe to remote item set events from the synchronised storage and update the local storage
178
- this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemSet, async (params) => {
202
+ await this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemSet, async (params) => {
179
203
  await this.handleRemoteItemSet(params);
180
204
  });
181
205
  // Subscribe to remote item remove events from the synchronised storage and update the local storage
182
- this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemRemove, async (params) => {
206
+ await this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemRemove, async (params) => {
183
207
  await this.handleRemoteItemRemove(params);
184
208
  });
185
209
  // Subscribe to resets from the synchronised storage and update the local storage
186
- this._eventBusComponent.subscribe(SynchronisedStorageTopics.Reset, async (params) => {
210
+ await this._eventBusComponent.subscribe(SynchronisedStorageTopics.Reset, async (params) => {
187
211
  await this.handleReset(params);
188
212
  });
189
213
  }
@@ -201,7 +225,7 @@ class SynchronisedEntityStorageConnector {
201
225
  }
202
226
  catch { }
203
227
  // Publish the item response with the entity
204
- this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemResponse, {
228
+ await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemResponse, {
205
229
  storageKey: this._storageKey,
206
230
  id: event.data.id,
207
231
  entity
@@ -217,8 +241,7 @@ class SynchronisedEntityStorageConnector {
217
241
  // Only set the item if it matches the storage key
218
242
  // and it is from another node, remote updates can not change data for this node
219
243
  // That must be done via the regular entity storage methods
220
- if (event.data.storageKey === this._storageKey &&
221
- event.data.entity.nodeIdentity !== this._nodeIdentity) {
244
+ if (event.data.storageKey === this._storageKey && event.data.entity.nodeId !== this._nodeId) {
222
245
  await this._entityStorageConnector.set(event.data.entity);
223
246
  }
224
247
  }
@@ -231,8 +254,7 @@ class SynchronisedEntityStorageConnector {
231
254
  // Only remove the item if it matches the storage key
232
255
  // and it is from another node, remote updates can not change data for this node
233
256
  // That must be done via the regular entity storage methods
234
- if (params.data.storageKey === this._storageKey &&
235
- params.data.nodeIdentity !== this._nodeIdentity) {
257
+ if (params.data.storageKey === this._storageKey && params.data.nodeId !== this._nodeId) {
236
258
  await this._entityStorageConnector.remove(params.data.id);
237
259
  }
238
260
  }
@@ -249,20 +271,25 @@ class SynchronisedEntityStorageConnector {
249
271
  const condition = {
250
272
  conditions: []
251
273
  };
252
- if (event.data.requestMode === SyncNodeIdentityMode.Local ||
253
- event.data.requestMode === SyncNodeIdentityMode.Remote) {
274
+ if (event.data.requestMode === SyncNodeIdMode.Local ||
275
+ event.data.requestMode === SyncNodeIdMode.Remote) {
254
276
  condition.conditions.push({
255
- property: "nodeIdentity",
256
- value: this._nodeIdentity,
257
- comparison: event.data.requestMode === SyncNodeIdentityMode.Local
277
+ property: SynchronisedEntityStorageConnector._PROP_NAME_NODE_ID,
278
+ value: this._nodeId,
279
+ comparison: event.data.requestMode === SyncNodeIdMode.Local
258
280
  ? ComparisonOperator.Equals
259
281
  : ComparisonOperator.NotEquals
260
282
  });
261
283
  }
262
- const result = await this._entityStorageConnector.query(condition, [{ property: "dateModified", sortDirection: SortDirection.Ascending }], undefined, cursor, event.data.batchSize);
284
+ const result = await this._entityStorageConnector.query(condition, [
285
+ {
286
+ property: SynchronisedEntityStorageConnector._PROP_NAME_DATE_MODIFIED,
287
+ sortDirection: SortDirection.Ascending
288
+ }
289
+ ], undefined, cursor, event.data.batchSize);
263
290
  cursor = result.cursor;
264
291
  // Publish the batch response with the entities
265
- this._eventBusComponent.publish(SynchronisedStorageTopics.BatchResponse, {
292
+ await this._eventBusComponent.publish(SynchronisedStorageTopics.BatchResponse, {
266
293
  storageKey: this._storageKey,
267
294
  entities: result.entities,
268
295
  lastEntry: !Is.stringValue(cursor)
@@ -286,12 +313,12 @@ class SynchronisedEntityStorageConnector {
286
313
  conditions: []
287
314
  };
288
315
  // Depending on the reset mode we can filter the entities to remove
289
- if (event.data.resetMode === SyncNodeIdentityMode.Local ||
290
- event.data.resetMode === SyncNodeIdentityMode.Remote) {
316
+ if (event.data.resetMode === SyncNodeIdMode.Local ||
317
+ event.data.resetMode === SyncNodeIdMode.Remote) {
291
318
  condition.conditions.push({
292
- property: "nodeIdentity",
293
- value: this._nodeIdentity,
294
- comparison: event.data.resetMode === SyncNodeIdentityMode.Local
319
+ property: "nodeId",
320
+ value: this._nodeId,
321
+ comparison: event.data.resetMode === SyncNodeIdMode.Local
295
322
  ? ComparisonOperator.Equals
296
323
  : ComparisonOperator.NotEquals
297
324
  });
@@ -307,5 +334,4 @@ class SynchronisedEntityStorageConnector {
307
334
  }
308
335
  }
309
336
  }
310
-
311
- export { SynchronisedEntityStorageConnector };
337
+ //# sourceMappingURL=synchronisedEntityStorageConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronisedEntityStorageConnector.js","sourceRoot":"","sources":["../../src/synchronisedEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, GeneralError, Guards, Is, StringHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty,\n\tSortDirection\n} from \"@twin.org/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IEvent, IEventBusComponent } from \"@twin.org/event-bus-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype ISyncBatchRequest,\n\ttype ISyncBatchResponse,\n\ttype ISynchronisedEntity,\n\ttype ISyncItemChange,\n\ttype ISyncItemRemove,\n\ttype ISyncItemRequest,\n\ttype ISyncItemResponse,\n\ttype ISyncItemSet,\n\ttype ISyncRegisterStorageKey,\n\ttype ISyncReset,\n\tSyncChangeOperation,\n\tSynchronisedStorageTopics,\n\tSyncNodeIdMode\n} from \"@twin.org/synchronised-storage-models\";\nimport type { ISynchronisedEntityStorageConnectorConstructorOptions } from \"./models/ISynchronisedEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations in synchronised storage.\n */\nexport class SynchronisedEntityStorageConnector<T extends ISynchronisedEntity = ISynchronisedEntity>\n\timplements IEntityStorageConnector<T>\n{\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<SynchronisedEntityStorageConnector>();\n\n\t/**\n\t * Fixed name for node id properties.\n\t * @internal\n\t */\n\tprivate static readonly _PROP_NAME_NODE_ID = \"nodeId\";\n\n\t/**\n\t * Fixed name for date modified properties.\n\t * @internal\n\t */\n\tprivate static readonly _PROP_NAME_DATE_MODIFIED = \"dateModified\";\n\n\t/**\n\t * Fixed name for id properties.\n\t * @internal\n\t */\n\tprivate static readonly _PROP_NAME_ID = \"id\";\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 primary key for the entity schema.\n\t * @internal\n\t */\n\tprivate readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * The storage key for the entity.\n\t * @internal\n\t */\n\tprivate readonly _storageKey: string;\n\n\t/**\n\t * The entity storage connector to use for actual data.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<T>;\n\n\t/**\n\t * The event bus component.\n\t * @internal\n\t */\n\tprivate readonly _eventBusComponent: IEventBusComponent;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of SynchronisedEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: ISynchronisedEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object<ISynchronisedEntityStorageConnectorConstructorOptions>(\n\t\t\tSynchronisedEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options),\n\t\t\toptions\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tSynchronisedEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tSynchronisedEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entityStorageConnectorType),\n\t\t\toptions.entityStorageConnectorType\n\t\t);\n\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._storageKey = options?.config?.storageKey ?? StringHelper.kebabCase(options.entitySchema);\n\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);\n\n\t\tconst requiredProperties: (keyof ISynchronisedEntity)[] = [\n\t\t\tSynchronisedEntityStorageConnector._PROP_NAME_ID,\n\t\t\tSynchronisedEntityStorageConnector._PROP_NAME_NODE_ID,\n\t\t\tSynchronisedEntityStorageConnector._PROP_NAME_DATE_MODIFIED\n\t\t];\n\n\t\tfor (const requiredProperty of requiredProperties) {\n\t\t\tconst foundProperty = this._entitySchema.properties?.find(\n\t\t\t\tprop => prop.property === requiredProperty\n\t\t\t);\n\t\t\tif (Is.empty(foundProperty)) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tSynchronisedEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\"missingRequiredProperty\",\n\t\t\t\t\t{ requiredProperty }\n\t\t\t\t);\n\t\t\t} else if (\n\t\t\t\tIs.empty(foundProperty.isPrimary) &&\n\t\t\t\tIs.empty(foundProperty.isSecondary) &&\n\t\t\t\tIs.empty(foundProperty.sortDirection)\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tSynchronisedEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\"missingRequiredPropertySort\",\n\t\t\t\t\t{\n\t\t\t\t\t\trequiredProperty\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions.entityStorageConnectorType\n\t\t);\n\n\t\tthis._eventBusComponent = ComponentFactory.get(options.eventBusComponentType ?? \"event-bus\");\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 SynchronisedEntityStorageConnector.CLASS_NAME;\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 * The component needs to be started when the node is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\n\t\t// Tell the synchronised storage about this storage key\n\t\tawait this._eventBusComponent.publish<ISyncRegisterStorageKey>(\n\t\t\tSynchronisedStorageTopics.RegisterStorageKey,\n\t\t\t{\n\t\t\t\tstorageKey: this._storageKey\n\t\t\t}\n\t\t);\n\n\t\tawait this.handleEventBusMessages();\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(SynchronisedEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\treturn this._entityStorageConnector.get(id, secondaryIndex, conditions);\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>(SynchronisedEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\tif (Is.stringValue(this._nodeId)) {\n\t\t\t// Make sure the entity has the required properties populated\n\t\t\tentity.dateModified = new Date(Date.now()).toISOString();\n\t\t\tentity.nodeId = this._nodeId;\n\n\t\t\tawait this._entityStorageConnector.set(entity, conditions);\n\n\t\t\t// Tell the synchronised storage about the entity changes\n\t\t\tawait this._eventBusComponent.publish<ISyncItemChange>(\n\t\t\t\tSynchronisedStorageTopics.LocalItemChange,\n\t\t\t\t{\n\t\t\t\t\tstorageKey: this._storageKey,\n\t\t\t\t\toperation: SyncChangeOperation.Set,\n\t\t\t\t\tnodeId: this._nodeId,\n\t\t\t\t\tid: entity[this._primaryKey.property] as string\n\t\t\t\t}\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(SynchronisedEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tif (Is.stringValue(this._nodeId)) {\n\t\t\tawait this._entityStorageConnector.remove(id, conditions);\n\n\t\t\t// Tell the synchronised storage about the entity removal\n\t\t\tawait this._eventBusComponent.publish<ISyncItemChange>(\n\t\t\t\tSynchronisedStorageTopics.LocalItemChange,\n\t\t\t\t{\n\t\t\t\t\tstorageKey: this._storageKey,\n\t\t\t\t\toperation: SyncChangeOperation.Delete,\n\t\t\t\t\tnodeId: this._nodeId,\n\t\t\t\t\tid\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\t// We deliberately skip the partition key as we want to query all partitions in synchronised storage\n\t\treturn this._entityStorageConnector.query(\n\t\t\tconditions,\n\t\t\tsortProperties,\n\t\t\tproperties,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\t}\n\n\t/**\n\t * Handle the event bus messages.\n\t * @internal\n\t */\n\tprivate async handleEventBusMessages(): Promise<void> {\n\t\t// When the synchronised storage requests an item, we need to provide it\n\t\tawait this._eventBusComponent.subscribe<ISyncItemRequest>(\n\t\t\tSynchronisedStorageTopics.LocalItemRequest,\n\t\t\tasync params => {\n\t\t\t\tawait this.handleLocalItemRequest(params);\n\t\t\t}\n\t\t);\n\n\t\t// When the synchronised storage requests a batch, we need to provide it\n\t\tawait this._eventBusComponent.subscribe<ISyncBatchRequest>(\n\t\t\tSynchronisedStorageTopics.BatchRequest,\n\t\t\tasync params => {\n\t\t\t\tawait this.handleBatchRequest(params);\n\t\t\t}\n\t\t);\n\n\t\t// Subscribe to remote item set events from the synchronised storage and update the local storage\n\t\tawait this._eventBusComponent.subscribe<ISyncItemSet>(\n\t\t\tSynchronisedStorageTopics.RemoteItemSet,\n\t\t\tasync params => {\n\t\t\t\tawait this.handleRemoteItemSet(params);\n\t\t\t}\n\t\t);\n\n\t\t// Subscribe to remote item remove events from the synchronised storage and update the local storage\n\t\tawait this._eventBusComponent.subscribe<ISyncItemRemove>(\n\t\t\tSynchronisedStorageTopics.RemoteItemRemove,\n\t\t\tasync params => {\n\t\t\t\tawait this.handleRemoteItemRemove(params);\n\t\t\t}\n\t\t);\n\n\t\t// Subscribe to resets from the synchronised storage and update the local storage\n\t\tawait this._eventBusComponent.subscribe<ISyncReset>(\n\t\t\tSynchronisedStorageTopics.Reset,\n\t\t\tasync params => {\n\t\t\t\tawait this.handleReset(params);\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Handle a local item request.\n\t * @param event The request parameters\n\t * @internal\n\t */\n\tprivate async handleLocalItemRequest(event: IEvent<ISyncItemRequest>): Promise<void> {\n\t\t// Only handle the request if it matches the storage key\n\t\tif (event.data.storageKey === this._storageKey) {\n\t\t\tlet entity: T | undefined;\n\t\t\ttry {\n\t\t\t\tentity = await this._entityStorageConnector.get(event.data.id);\n\t\t\t} catch {}\n\n\t\t\t// Publish the item response with the entity\n\t\t\tawait this._eventBusComponent.publish<ISyncItemResponse>(\n\t\t\t\tSynchronisedStorageTopics.LocalItemResponse,\n\t\t\t\t{\n\t\t\t\t\tstorageKey: this._storageKey,\n\t\t\t\t\tid: event.data.id,\n\t\t\t\t\tentity\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Handle a remote item set event.\n\t * @param event The event parameters\n\t * @internal\n\t */\n\tprivate async handleRemoteItemSet(event: IEvent<ISyncItemSet>): Promise<void> {\n\t\t// Only set the item if it matches the storage key\n\t\t// and it is from another node, remote updates can not change data for this node\n\t\t// That must be done via the regular entity storage methods\n\t\tif (event.data.storageKey === this._storageKey && event.data.entity.nodeId !== this._nodeId) {\n\t\t\tawait this._entityStorageConnector.set(event.data.entity as T);\n\t\t}\n\t}\n\n\t/**\n\t * Handle a remote item remove event.\n\t * @param params The event parameters\n\t * @internal\n\t */\n\tprivate async handleRemoteItemRemove(params: IEvent<ISyncItemRemove>): Promise<void> {\n\t\t// Only remove the item if it matches the storage key\n\t\t// and it is from another node, remote updates can not change data for this node\n\t\t// That must be done via the regular entity storage methods\n\t\tif (params.data.storageKey === this._storageKey && params.data.nodeId !== this._nodeId) {\n\t\t\tawait this._entityStorageConnector.remove(params.data.id);\n\t\t}\n\t}\n\n\t/**\n\t * Handle a batch request.\n\t * @param event The request parameters\n\t * @internal\n\t */\n\tprivate async handleBatchRequest(event: IEvent<ISyncBatchRequest>): Promise<void> {\n\t\t// Only handle the request if it matches the storage key\n\t\tif (event.data.storageKey === this._storageKey) {\n\t\t\tlet cursor;\n\t\t\tdo {\n\t\t\t\tconst condition: EntityCondition<T> = {\n\t\t\t\t\tconditions: []\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\tevent.data.requestMode === SyncNodeIdMode.Local ||\n\t\t\t\t\tevent.data.requestMode === SyncNodeIdMode.Remote\n\t\t\t\t) {\n\t\t\t\t\tcondition.conditions.push({\n\t\t\t\t\t\tproperty: SynchronisedEntityStorageConnector._PROP_NAME_NODE_ID,\n\t\t\t\t\t\tvalue: this._nodeId,\n\t\t\t\t\t\tcomparison:\n\t\t\t\t\t\t\tevent.data.requestMode === SyncNodeIdMode.Local\n\t\t\t\t\t\t\t\t? ComparisonOperator.Equals\n\t\t\t\t\t\t\t\t: ComparisonOperator.NotEquals\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst result = await this._entityStorageConnector.query(\n\t\t\t\t\tcondition,\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tproperty: SynchronisedEntityStorageConnector._PROP_NAME_DATE_MODIFIED,\n\t\t\t\t\t\t\tsortDirection: SortDirection.Ascending\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\tundefined,\n\t\t\t\t\tcursor,\n\t\t\t\t\tevent.data.batchSize\n\t\t\t\t);\n\n\t\t\t\tcursor = result.cursor;\n\n\t\t\t\t// Publish the batch response with the entities\n\t\t\t\tawait this._eventBusComponent.publish<ISyncBatchResponse>(\n\t\t\t\t\tSynchronisedStorageTopics.BatchResponse,\n\t\t\t\t\t{\n\t\t\t\t\t\tstorageKey: this._storageKey,\n\t\t\t\t\t\tentities: result.entities as T[],\n\t\t\t\t\t\tlastEntry: !Is.stringValue(cursor)\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} while (Is.stringValue(cursor));\n\t\t}\n\t}\n\n\t/**\n\t * Handle a reset event.\n\t * @param event The event parameters\n\t * @internal\n\t */\n\tprivate async handleReset(event: IEvent<ISyncReset>): Promise<void> {\n\t\t// Only reset the storage if it matches the storage key\n\t\tif (event.data.storageKey === this._storageKey) {\n\t\t\tlet cursor;\n\t\t\tlet toRemove: string[] = [];\n\n\t\t\t// Build a list of the ids to remove\n\t\t\tdo {\n\t\t\t\tconst condition: EntityCondition<T> = {\n\t\t\t\t\tconditions: []\n\t\t\t\t};\n\n\t\t\t\t// Depending on the reset mode we can filter the entities to remove\n\t\t\t\tif (\n\t\t\t\t\tevent.data.resetMode === SyncNodeIdMode.Local ||\n\t\t\t\t\tevent.data.resetMode === SyncNodeIdMode.Remote\n\t\t\t\t) {\n\t\t\t\t\tcondition.conditions.push({\n\t\t\t\t\t\tproperty: \"nodeId\",\n\t\t\t\t\t\tvalue: this._nodeId,\n\t\t\t\t\t\tcomparison:\n\t\t\t\t\t\t\tevent.data.resetMode === SyncNodeIdMode.Local\n\t\t\t\t\t\t\t\t? ComparisonOperator.Equals\n\t\t\t\t\t\t\t\t: ComparisonOperator.NotEquals\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst result = await this._entityStorageConnector.query(\n\t\t\t\t\tcondition,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tcursor\n\t\t\t\t);\n\n\t\t\t\tcursor = result.cursor;\n\t\t\t\ttoRemove = toRemove.concat(result.entities.map(entity => (entity as T).id));\n\t\t\t} while (Is.stringValue(cursor));\n\n\t\t\t// Remove the entities\n\t\t\tfor (let i = 0; i < toRemove.length; i++) {\n\t\t\t\tawait this.remove(toRemove[i]);\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -1,3 +1,3 @@
1
- export * from "./synchronisedEntityStorageConnector";
2
- export * from "./models/ISynchronisedEntityStorageConnectorConfig";
3
- export * from "./models/ISynchronisedEntityStorageConnectorConstructorOptions";
1
+ export * from "./synchronisedEntityStorageConnector.js";
2
+ export * from "./models/ISynchronisedEntityStorageConnectorConfig.js";
3
+ export * from "./models/ISynchronisedEntityStorageConnectorConstructorOptions.js";
@@ -1,4 +1,4 @@
1
- import type { ISynchronisedEntityStorageConnectorConfig } from "./ISynchronisedEntityStorageConnectorConfig";
1
+ import type { ISynchronisedEntityStorageConnectorConfig } from "./ISynchronisedEntityStorageConnectorConfig.js";
2
2
  /**
3
3
  * Options for the Synchronised Entity Storage Connector constructor.
4
4
  */
@@ -1,7 +1,7 @@
1
1
  import { type EntityCondition, type IEntitySchema, SortDirection } from "@twin.org/entity";
2
2
  import { type IEntityStorageConnector } from "@twin.org/entity-storage-models";
3
3
  import { type ISynchronisedEntity } from "@twin.org/synchronised-storage-models";
4
- import type { ISynchronisedEntityStorageConnectorConstructorOptions } from "./models/ISynchronisedEntityStorageConnectorConstructorOptions";
4
+ import type { ISynchronisedEntityStorageConnectorConstructorOptions } from "./models/ISynchronisedEntityStorageConnectorConstructorOptions.js";
5
5
  /**
6
6
  * Class for performing entity storage operations in synchronised storage.
7
7
  */
@@ -9,12 +9,17 @@ export declare class SynchronisedEntityStorageConnector<T extends ISynchronisedE
9
9
  /**
10
10
  * Runtime name for the class.
11
11
  */
12
- readonly CLASS_NAME: string;
12
+ static readonly CLASS_NAME: string;
13
13
  /**
14
14
  * Create a new instance of SynchronisedEntityStorageConnector.
15
15
  * @param options The options for the connector.
16
16
  */
17
17
  constructor(options: ISynchronisedEntityStorageConnectorConstructorOptions);
18
+ /**
19
+ * Returns the class name of the component.
20
+ * @returns The class name of the component.
21
+ */
22
+ className(): string;
18
23
  /**
19
24
  * Get the schema for the entities.
20
25
  * @returns The schema for the entities.
@@ -22,11 +27,10 @@ export declare class SynchronisedEntityStorageConnector<T extends ISynchronisedE
22
27
  getSchema(): IEntitySchema;
23
28
  /**
24
29
  * The component needs to be started when the node is initialized.
25
- * @param nodeIdentity The identity of the node starting the component.
26
30
  * @param nodeLoggingComponentType The node logging component type.
27
31
  * @returns Nothing.
28
32
  */
29
- start(nodeIdentity: string, nodeLoggingComponentType: string | undefined): Promise<void>;
33
+ start(nodeLoggingComponentType?: string): Promise<void>;
30
34
  /**
31
35
  * Get an entity.
32
36
  * @param id The id of the entity to get, or the index value if secondaryIndex is set.
@@ -63,15 +67,15 @@ export declare class SynchronisedEntityStorageConnector<T extends ISynchronisedE
63
67
  * @param conditions The conditions to match for the entities.
64
68
  * @param sortProperties The optional sort order.
65
69
  * @param properties The optional properties to return, defaults to all.
66
- * @param cursor The cursor to request the next page of entities.
67
- * @param pageSize The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
70
+ * @param cursor The cursor to request the next chunk of entities.
71
+ * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
68
72
  * @returns All the entities for the storage matching the conditions,
69
73
  * and a cursor which can be used to request more entities.
70
74
  */
71
75
  query(conditions?: EntityCondition<T>, sortProperties?: {
72
76
  property: keyof T;
73
77
  sortDirection: SortDirection;
74
- }[], properties?: (keyof T)[], cursor?: string, pageSize?: number): Promise<{
78
+ }[], properties?: (keyof T)[], cursor?: string, limit?: number): Promise<{
75
79
  /**
76
80
  * The entities, which can be partial if a limited keys list was provided.
77
81
  */
package/docs/changelog.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.1](https://github.com/twinfoundation/synchronised-storage/compare/entity-storage-connector-synchronised-v0.0.3-next.0...entity-storage-connector-synchronised-v0.0.3-next.1) (2025-11-12)
4
+
5
+
6
+ ### Features
7
+
8
+ * add context id features ([#24](https://github.com/twinfoundation/synchronised-storage/issues/24)) ([5266b18](https://github.com/twinfoundation/synchronised-storage/commit/5266b18088317c7dc274a209a79102a6fc88a8e4))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/synchronised-storage-models bumped from 0.0.3-next.0 to 0.0.3-next.1
16
+
17
+ ## [0.0.2-next.10](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.9...entity-storage-connector-synchronised-v0.0.2-next.10) (2025-10-09)
18
+
19
+
20
+ ### Features
21
+
22
+ * add validate-locales ([e66ef0d](https://github.com/twinfoundation/entity-storage/commit/e66ef0de26ca2f82b3fe89bb5c7a15a0978a9644))
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/entity-storage-models bumped from 0.0.2-next.9 to 0.0.2-next.10
30
+ * devDependencies
31
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.2-next.9 to 0.0.2-next.10
32
+
33
+ ## [0.0.2-next.9](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.8...entity-storage-connector-synchronised-v0.0.2-next.9) (2025-10-02)
34
+
35
+
36
+ ### Miscellaneous Chores
37
+
38
+ * **entity-storage-connector-synchronised:** Synchronize repo versions
39
+
40
+
41
+ ### Dependencies
42
+
43
+ * The following workspace dependencies were updated
44
+ * dependencies
45
+ * @twin.org/entity-storage-models bumped from 0.0.2-next.8 to 0.0.2-next.9
46
+ * devDependencies
47
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.2-next.8 to 0.0.2-next.9
48
+
3
49
  ## [0.0.2-next.8](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.7...entity-storage-connector-synchronised-v0.0.2-next.8) (2025-08-29)
4
50
 
5
51
 
@@ -36,15 +36,29 @@ The options for the connector.
36
36
 
37
37
  ### CLASS\_NAME
38
38
 
39
- > `readonly` **CLASS\_NAME**: `string`
39
+ > `readonly` `static` **CLASS\_NAME**: `string`
40
40
 
41
41
  Runtime name for the class.
42
42
 
43
+ ## Methods
44
+
45
+ ### className()
46
+
47
+ > **className**(): `string`
48
+
49
+ Returns the class name of the component.
50
+
51
+ #### Returns
52
+
53
+ `string`
54
+
55
+ The class name of the component.
56
+
43
57
  #### Implementation of
44
58
 
45
- `IEntityStorageConnector.CLASS_NAME`
59
+ `IEntityStorageConnector.className`
46
60
 
47
- ## Methods
61
+ ***
48
62
 
49
63
  ### getSchema()
50
64
 
@@ -66,24 +80,18 @@ The schema for the entities.
66
80
 
67
81
  ### start()
68
82
 
69
- > **start**(`nodeIdentity`, `nodeLoggingComponentType`): `Promise`\<`void`\>
83
+ > **start**(`nodeLoggingComponentType?`): `Promise`\<`void`\>
70
84
 
71
85
  The component needs to be started when the node is initialized.
72
86
 
73
87
  #### Parameters
74
88
 
75
- ##### nodeIdentity
89
+ ##### nodeLoggingComponentType?
76
90
 
77
91
  `string`
78
92
 
79
- The identity of the node starting the component.
80
-
81
- ##### nodeLoggingComponentType
82
-
83
93
  The node logging component type.
84
94
 
85
- `undefined` | `string`
86
-
87
95
  #### Returns
88
96
 
89
97
  `Promise`\<`void`\>
@@ -98,7 +106,7 @@ Nothing.
98
106
 
99
107
  ### get()
100
108
 
101
- > **get**(`id`, `secondaryIndex?`, `conditions?`): `Promise`\<`undefined` \| `T`\>
109
+ > **get**(`id`, `secondaryIndex?`, `conditions?`): `Promise`\<`T` \| `undefined`\>
102
110
 
103
111
  Get an entity.
104
112
 
@@ -124,7 +132,7 @@ The optional conditions to match for the entities.
124
132
 
125
133
  #### Returns
126
134
 
127
- `Promise`\<`undefined` \| `T`\>
135
+ `Promise`\<`T` \| `undefined`\>
128
136
 
129
137
  The object if it can be found or undefined.
130
138
 
@@ -200,7 +208,7 @@ Nothing.
200
208
 
201
209
  ### query()
202
210
 
203
- > **query**(`conditions?`, `sortProperties?`, `properties?`, `cursor?`, `pageSize?`): `Promise`\<\{ `entities`: `Partial`\<`T`\>[]; `cursor?`: `string`; \}\>
211
+ > **query**(`conditions?`, `sortProperties?`, `properties?`, `cursor?`, `limit?`): `Promise`\<\{ `entities`: `Partial`\<`T`\>[]; `cursor?`: `string`; \}\>
204
212
 
205
213
  Find all the entities which match the conditions.
206
214
 
@@ -228,9 +236,9 @@ The optional properties to return, defaults to all.
228
236
 
229
237
  `string`
230
238
 
231
- The cursor to request the next page of entities.
239
+ The cursor to request the next chunk of entities.
232
240
 
233
- ##### pageSize?
241
+ ##### limit?
234
242
 
235
243
  `number`
236
244
 
package/locales/en.json CHANGED
@@ -1 +1,8 @@
1
- {}
1
+ {
2
+ "error": {
3
+ "synchronisedEntityStorageConnector": {
4
+ "missingRequiredProperty": "The property \"{requiredProperty}\" is required when using the Synchronised Entity Storage Connector.",
5
+ "missingRequiredPropertySort": "The property \"{requiredProperty}\" requires sorting attributes for the Synchronised Entity Storage Connector."
6
+ }
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-connector-synchronised",
3
- "version": "0.0.2-next.8",
3
+ "version": "0.0.3-next.1",
4
4
  "description": "Entity Storage connector implementation using synchronised storage",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/twinfoundation/entity-storage.git",
7
+ "url": "git+https://github.com/twinfoundation/synchronised-storage.git",
8
8
  "directory": "packages/entity-storage-connector-synchronised"
9
9
  },
10
10
  "author": "martyn.janes@iota.org",
@@ -14,30 +14,48 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
+ "@twin.org/context": "next",
17
18
  "@twin.org/core": "next",
18
19
  "@twin.org/entity": "next",
19
- "@twin.org/entity-storage-models": "0.0.2-next.8",
20
+ "@twin.org/entity-storage-models": "next",
20
21
  "@twin.org/event-bus-models": "next",
21
22
  "@twin.org/logging-models": "next",
22
23
  "@twin.org/nameof": "next",
23
- "@twin.org/synchronised-storage-models": "next"
24
+ "@twin.org/synchronised-storage-models": "0.0.3-next.1"
24
25
  },
25
- "main": "./dist/cjs/index.cjs",
26
- "module": "./dist/esm/index.mjs",
26
+ "main": "./dist/es/index.js",
27
27
  "types": "./dist/types/index.d.ts",
28
28
  "exports": {
29
29
  ".": {
30
30
  "types": "./dist/types/index.d.ts",
31
- "require": "./dist/cjs/index.cjs",
32
- "import": "./dist/esm/index.mjs"
31
+ "import": "./dist/es/index.js",
32
+ "default": "./dist/es/index.js"
33
33
  },
34
34
  "./locales/*.json": "./locales/*.json"
35
35
  },
36
36
  "files": [
37
- "dist/cjs",
38
- "dist/esm",
37
+ "dist/es",
39
38
  "dist/types",
40
39
  "locales",
41
40
  "docs"
42
- ]
41
+ ],
42
+ "keywords": [
43
+ "twin",
44
+ "trade",
45
+ "iota",
46
+ "framework",
47
+ "blockchain",
48
+ "entity-storage",
49
+ "entity",
50
+ "storage",
51
+ "persistence",
52
+ "database",
53
+ "connector",
54
+ "adapter",
55
+ "integration"
56
+ ],
57
+ "bugs": {
58
+ "url": "git+https://github.com/twinfoundation/synchronised-storage/issues"
59
+ },
60
+ "homepage": "https://twindev.org"
43
61
  }
@@ -1,313 +0,0 @@
1
- 'use strict';
2
-
3
- var core = require('@twin.org/core');
4
- var entity = require('@twin.org/entity');
5
- var entityStorageModels = require('@twin.org/entity-storage-models');
6
- var synchronisedStorageModels = require('@twin.org/synchronised-storage-models');
7
-
8
- // Copyright 2024 IOTA Stiftung.
9
- // SPDX-License-Identifier: Apache-2.0.
10
- /**
11
- * Class for performing entity storage operations in synchronised storage.
12
- */
13
- class SynchronisedEntityStorageConnector {
14
- /**
15
- * Runtime name for the class.
16
- */
17
- CLASS_NAME = "SynchronisedEntityStorageConnector";
18
- /**
19
- * The schema for the entity.
20
- * @internal
21
- */
22
- _entitySchema;
23
- /**
24
- * The primary key for the entity schema.
25
- * @internal
26
- */
27
- _primaryKey;
28
- /**
29
- * The storage key for the entity.
30
- * @internal
31
- */
32
- _storageKey;
33
- /**
34
- * The entity storage connector to use for actual data.
35
- * @internal
36
- */
37
- _entityStorageConnector;
38
- /**
39
- * The event bus component.
40
- * @internal
41
- */
42
- _eventBusComponent;
43
- /**
44
- * The node identity.
45
- * @internal
46
- */
47
- _nodeIdentity;
48
- /**
49
- * Create a new instance of SynchronisedEntityStorageConnector.
50
- * @param options The options for the connector.
51
- */
52
- constructor(options) {
53
- core.Guards.object(this.CLASS_NAME, "options", options);
54
- core.Guards.stringValue(this.CLASS_NAME, "options.entitySchema", options.entitySchema);
55
- core.Guards.stringValue(this.CLASS_NAME, "options.entityStorageConnectorType", options.entityStorageConnectorType);
56
- this._entitySchema = entity.EntitySchemaFactory.get(options.entitySchema);
57
- this._storageKey = options?.config?.storageKey ?? core.StringHelper.kebabCase(options.entitySchema);
58
- this._primaryKey = entity.EntitySchemaHelper.getPrimaryKey(this._entitySchema);
59
- const requiredProperties = [
60
- "id",
61
- "nodeIdentity",
62
- "dateModified"
63
- ];
64
- for (const requiredProperty of requiredProperties) {
65
- const foundProperty = this._entitySchema.properties?.find(prop => prop.property === requiredProperty);
66
- if (core.Is.empty(foundProperty)) {
67
- throw new core.GeneralError(this.CLASS_NAME, "missingRequiredProperty", { requiredProperty });
68
- }
69
- else if (core.Is.empty(foundProperty.isPrimary) &&
70
- core.Is.empty(foundProperty.isSecondary) &&
71
- core.Is.empty(foundProperty.sortDirection)) {
72
- throw new core.GeneralError(this.CLASS_NAME, "missingRequiredPropertySort", {
73
- requiredProperty
74
- });
75
- }
76
- }
77
- this._entityStorageConnector = entityStorageModels.EntityStorageConnectorFactory.get(options.entityStorageConnectorType);
78
- this._eventBusComponent = core.ComponentFactory.get(options.eventBusComponentType ?? "event-bus");
79
- }
80
- /**
81
- * Get the schema for the entities.
82
- * @returns The schema for the entities.
83
- */
84
- getSchema() {
85
- return this._entitySchema;
86
- }
87
- /**
88
- * The component needs to be started when the node is initialized.
89
- * @param nodeIdentity The identity of the node starting the component.
90
- * @param nodeLoggingComponentType The node logging component type.
91
- * @returns Nothing.
92
- */
93
- async start(nodeIdentity, nodeLoggingComponentType) {
94
- this._nodeIdentity = nodeIdentity;
95
- // Tell the synchronised storage about this storage key
96
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RegisterStorageKey, {
97
- storageKey: this._storageKey
98
- });
99
- this.handleEventBusMessages();
100
- }
101
- /**
102
- * Get an entity.
103
- * @param id The id of the entity to get, or the index value if secondaryIndex is set.
104
- * @param secondaryIndex Get the item using a secondary index.
105
- * @param conditions The optional conditions to match for the entities.
106
- * @returns The object if it can be found or undefined.
107
- */
108
- async get(id, secondaryIndex, conditions) {
109
- core.Guards.stringValue(this.CLASS_NAME, "id", id);
110
- return this._entityStorageConnector.get(id, secondaryIndex, conditions);
111
- }
112
- /**
113
- * Set an entity.
114
- * @param entity The entity to set.
115
- * @param conditions The optional conditions to match for the entities.
116
- * @returns The id of the entity.
117
- */
118
- async set(entity, conditions) {
119
- core.Guards.object(this.CLASS_NAME, "entity", entity);
120
- if (core.Is.stringValue(this._nodeIdentity)) {
121
- // Make sure the entity has the required properties
122
- entity.dateModified = new Date(Date.now()).toISOString();
123
- entity.nodeIdentity = this._nodeIdentity;
124
- await this._entityStorageConnector.set(entity, conditions);
125
- // Tell the synchronised storage about the entity changes
126
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
127
- storageKey: this._storageKey,
128
- operation: synchronisedStorageModels.SyncChangeOperation.Set,
129
- nodeIdentity: this._nodeIdentity,
130
- id: entity[this._primaryKey.property]
131
- });
132
- }
133
- }
134
- /**
135
- * Remove the entity.
136
- * @param id The id of the entity to remove.
137
- * @param conditions The optional conditions to match for the entities.
138
- * @returns Nothing.
139
- */
140
- async remove(id, conditions) {
141
- core.Guards.stringValue(this.CLASS_NAME, "id", id);
142
- if (core.Is.stringValue(this._nodeIdentity)) {
143
- await this._entityStorageConnector.remove(id, conditions);
144
- // Tell the synchronised storage about the entity removal
145
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
146
- storageKey: this._storageKey,
147
- operation: synchronisedStorageModels.SyncChangeOperation.Delete,
148
- nodeIdentity: this._nodeIdentity,
149
- id
150
- });
151
- }
152
- }
153
- /**
154
- * Find all the entities which match the conditions.
155
- * @param conditions The conditions to match for the entities.
156
- * @param sortProperties The optional sort order.
157
- * @param properties The optional properties to return, defaults to all.
158
- * @param cursor The cursor to request the next page of entities.
159
- * @param pageSize The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
160
- * @returns All the entities for the storage matching the conditions,
161
- * and a cursor which can be used to request more entities.
162
- */
163
- async query(conditions, sortProperties, properties, cursor, pageSize) {
164
- return this._entityStorageConnector.query(conditions, sortProperties, properties, cursor, pageSize);
165
- }
166
- /**
167
- * Handle the event bus messages.
168
- * @internal
169
- */
170
- handleEventBusMessages() {
171
- // When the synchronised storage requests an item, we need to provide it
172
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, async (params) => {
173
- await this.handleLocalItemRequest(params);
174
- });
175
- // When the synchronised storage requests a batch, we need to provide it
176
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, async (params) => {
177
- await this.handleBatchRequest(params);
178
- });
179
- // Subscribe to remote item set events from the synchronised storage and update the local storage
180
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemSet, async (params) => {
181
- await this.handleRemoteItemSet(params);
182
- });
183
- // Subscribe to remote item remove events from the synchronised storage and update the local storage
184
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemRemove, async (params) => {
185
- await this.handleRemoteItemRemove(params);
186
- });
187
- // Subscribe to resets from the synchronised storage and update the local storage
188
- this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.Reset, async (params) => {
189
- await this.handleReset(params);
190
- });
191
- }
192
- /**
193
- * Handle a local item request.
194
- * @param event The request parameters
195
- * @internal
196
- */
197
- async handleLocalItemRequest(event) {
198
- // Only handle the request if it matches the storage key
199
- if (event.data.storageKey === this._storageKey) {
200
- let entity;
201
- try {
202
- entity = await this._entityStorageConnector.get(event.data.id);
203
- }
204
- catch { }
205
- // Publish the item response with the entity
206
- this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemResponse, {
207
- storageKey: this._storageKey,
208
- id: event.data.id,
209
- entity
210
- });
211
- }
212
- }
213
- /**
214
- * Handle a remote item set event.
215
- * @param event The event parameters
216
- * @internal
217
- */
218
- async handleRemoteItemSet(event) {
219
- // Only set the item if it matches the storage key
220
- // and it is from another node, remote updates can not change data for this node
221
- // That must be done via the regular entity storage methods
222
- if (event.data.storageKey === this._storageKey &&
223
- event.data.entity.nodeIdentity !== this._nodeIdentity) {
224
- await this._entityStorageConnector.set(event.data.entity);
225
- }
226
- }
227
- /**
228
- * Handle a remote item remove event.
229
- * @param params The event parameters
230
- * @internal
231
- */
232
- async handleRemoteItemRemove(params) {
233
- // Only remove the item if it matches the storage key
234
- // and it is from another node, remote updates can not change data for this node
235
- // That must be done via the regular entity storage methods
236
- if (params.data.storageKey === this._storageKey &&
237
- params.data.nodeIdentity !== this._nodeIdentity) {
238
- await this._entityStorageConnector.remove(params.data.id);
239
- }
240
- }
241
- /**
242
- * Handle a batch request.
243
- * @param event The request parameters
244
- * @internal
245
- */
246
- async handleBatchRequest(event) {
247
- // Only handle the request if it matches the storage key
248
- if (event.data.storageKey === this._storageKey) {
249
- let cursor;
250
- do {
251
- const condition = {
252
- conditions: []
253
- };
254
- if (event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Local ||
255
- event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Remote) {
256
- condition.conditions.push({
257
- property: "nodeIdentity",
258
- value: this._nodeIdentity,
259
- comparison: event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Local
260
- ? entity.ComparisonOperator.Equals
261
- : entity.ComparisonOperator.NotEquals
262
- });
263
- }
264
- const result = await this._entityStorageConnector.query(condition, [{ property: "dateModified", sortDirection: entity.SortDirection.Ascending }], undefined, cursor, event.data.batchSize);
265
- cursor = result.cursor;
266
- // Publish the batch response with the entities
267
- this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchResponse, {
268
- storageKey: this._storageKey,
269
- entities: result.entities,
270
- lastEntry: !core.Is.stringValue(cursor)
271
- });
272
- } while (core.Is.stringValue(cursor));
273
- }
274
- }
275
- /**
276
- * Handle a reset event.
277
- * @param event The event parameters
278
- * @internal
279
- */
280
- async handleReset(event) {
281
- // Only reset the storage if it matches the storage key
282
- if (event.data.storageKey === this._storageKey) {
283
- let cursor;
284
- let toRemove = [];
285
- // Build a list of the ids to remove
286
- do {
287
- const condition = {
288
- conditions: []
289
- };
290
- // Depending on the reset mode we can filter the entities to remove
291
- if (event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Local ||
292
- event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Remote) {
293
- condition.conditions.push({
294
- property: "nodeIdentity",
295
- value: this._nodeIdentity,
296
- comparison: event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Local
297
- ? entity.ComparisonOperator.Equals
298
- : entity.ComparisonOperator.NotEquals
299
- });
300
- }
301
- const result = await this._entityStorageConnector.query(condition, undefined, undefined, cursor);
302
- cursor = result.cursor;
303
- toRemove = toRemove.concat(result.entities.map(entity => entity.id));
304
- } while (core.Is.stringValue(cursor));
305
- // Remove the entities
306
- for (let i = 0; i < toRemove.length; i++) {
307
- await this.remove(toRemove[i]);
308
- }
309
- }
310
- }
311
- }
312
-
313
- exports.SynchronisedEntityStorageConnector = SynchronisedEntityStorageConnector;