@twin.org/entity-storage-connector-synchronised 0.0.2-next.3 → 0.0.2-next.5

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.
@@ -40,6 +40,11 @@ class SynchronisedEntityStorageConnector {
40
40
  * @internal
41
41
  */
42
42
  _eventBusComponent;
43
+ /**
44
+ * The node identity.
45
+ * @internal
46
+ */
47
+ _nodeIdentity;
43
48
  /**
44
49
  * Create a new instance of SynchronisedEntityStorageConnector.
45
50
  * @param options The options for the connector.
@@ -51,13 +56,19 @@ class SynchronisedEntityStorageConnector {
51
56
  this._entitySchema = entity.EntitySchemaFactory.get(options.entitySchema);
52
57
  this._storageKey = options?.config?.storageKey ?? core.StringHelper.kebabCase(options.entitySchema);
53
58
  this._primaryKey = entity.EntitySchemaHelper.getPrimaryKey(this._entitySchema);
54
- const requiredProperties = ["nodeIdentity", "dateModified"];
59
+ const requiredProperties = [
60
+ "id",
61
+ "nodeIdentity",
62
+ "dateModified"
63
+ ];
55
64
  for (const requiredProperty of requiredProperties) {
56
65
  const foundProperty = this._entitySchema.properties?.find(prop => prop.property === requiredProperty);
57
66
  if (core.Is.empty(foundProperty)) {
58
67
  throw new core.GeneralError(this.CLASS_NAME, "missingRequiredProperty", { requiredProperty });
59
68
  }
60
- else if (core.Is.empty(foundProperty.isSecondary) && core.Is.empty(foundProperty.sortDirection)) {
69
+ else if (core.Is.empty(foundProperty.isPrimary) &&
70
+ core.Is.empty(foundProperty.isSecondary) &&
71
+ core.Is.empty(foundProperty.sortDirection)) {
61
72
  throw new core.GeneralError(this.CLASS_NAME, "missingRequiredPropertySort", {
62
73
  requiredProperty
63
74
  });
@@ -81,6 +92,7 @@ class SynchronisedEntityStorageConnector {
81
92
  * @returns Nothing.
82
93
  */
83
94
  async start(nodeIdentity, nodeLoggingConnectorType, componentState) {
95
+ this._nodeIdentity = nodeIdentity;
84
96
  // Tell the synchronised storage about this storage key
85
97
  await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.RegisterStorageKey, {
86
98
  storageKey: this._storageKey
@@ -106,15 +118,19 @@ class SynchronisedEntityStorageConnector {
106
118
  */
107
119
  async set(entity, conditions) {
108
120
  core.Guards.object(this.CLASS_NAME, "entity", entity);
109
- // Make sure the entity has the required properties
110
- entity.dateModified = new Date(Date.now()).toISOString();
111
- await this._entityStorageConnector.set(entity, conditions);
112
- // Tell the synchronised storage about the entity changes
113
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
114
- storageKey: this._storageKey,
115
- operation: synchronisedStorageModels.SyncChangeOperation.Set,
116
- id: entity[this._primaryKey.property]
117
- });
121
+ if (core.Is.stringValue(this._nodeIdentity)) {
122
+ // Make sure the entity has the required properties
123
+ entity.dateModified = new Date(Date.now()).toISOString();
124
+ entity.nodeIdentity = this._nodeIdentity;
125
+ await this._entityStorageConnector.set(entity, conditions);
126
+ // Tell the synchronised storage about the entity changes
127
+ await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
128
+ storageKey: this._storageKey,
129
+ operation: synchronisedStorageModels.SyncChangeOperation.Set,
130
+ nodeIdentity: this._nodeIdentity,
131
+ id: entity[this._primaryKey.property]
132
+ });
133
+ }
118
134
  }
119
135
  /**
120
136
  * Remove the entity.
@@ -124,13 +140,16 @@ class SynchronisedEntityStorageConnector {
124
140
  */
125
141
  async remove(id, conditions) {
126
142
  core.Guards.stringValue(this.CLASS_NAME, "id", id);
127
- await this._entityStorageConnector.remove(id, conditions);
128
- // Tell the synchronised storage about the entity removal
129
- await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
130
- storageKey: this._storageKey,
131
- operation: synchronisedStorageModels.SyncChangeOperation.Delete,
132
- id
133
- });
143
+ if (core.Is.stringValue(this._nodeIdentity)) {
144
+ await this._entityStorageConnector.remove(id, conditions);
145
+ // Tell the synchronised storage about the entity removal
146
+ await this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemChange, {
147
+ storageKey: this._storageKey,
148
+ operation: synchronisedStorageModels.SyncChangeOperation.Delete,
149
+ nodeIdentity: this._nodeIdentity,
150
+ id
151
+ });
152
+ }
134
153
  }
135
154
  /**
136
155
  * Find all the entities which match the conditions.
@@ -152,54 +171,144 @@ class SynchronisedEntityStorageConnector {
152
171
  handleEventBusMessages() {
153
172
  // When the synchronised storage requests an item, we need to provide it
154
173
  this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemRequest, async (params) => {
155
- // Only handle the request if it matches the storage key
156
- if (params.data.storageKey === this._storageKey) {
157
- let entity;
158
- try {
159
- entity = await this._entityStorageConnector.get(params.data.id);
160
- }
161
- catch { }
162
- // Publish the item response with the entity
163
- this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemResponse, {
164
- storageKey: this._storageKey,
165
- id: params.data.id,
166
- entity
167
- });
168
- }
174
+ await this.handleLocalItemRequest(params);
169
175
  });
170
176
  // When the synchronised storage requests a batch, we need to provide it
171
177
  this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.BatchRequest, async (params) => {
172
- // Only handle the request if it matches the storage key
173
- if (params.data.storageKey === this._storageKey) {
174
- let cursor;
175
- do {
176
- const result = await this._entityStorageConnector.query(undefined, [{ property: "dateModified", sortDirection: entity.SortDirection.Ascending }], undefined, cursor, params.data.batchSize);
177
- cursor = result.cursor;
178
- // Publish the batch response with the entities
179
- this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchResponse, {
180
- storageKey: this._storageKey,
181
- primaryKey: this._primaryKey.property,
182
- entities: result.entities,
183
- lastEntry: !core.Is.stringValue(cursor)
184
- });
185
- } while (core.Is.stringValue(cursor));
186
- }
178
+ await this.handleBatchRequest(params);
187
179
  });
188
180
  // Subscribe to remote item set events from the synchronised storage and update the local storage
189
181
  this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemSet, async (params) => {
190
- // Only remove the item if it matches the storage key
191
- if (params.data.storageKey === this._storageKey) {
192
- await this.set(params.data.entity);
193
- }
182
+ await this.handleRemoteItemSet(params);
194
183
  });
195
184
  // Subscribe to remote item remove events from the synchronised storage and update the local storage
196
185
  this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.RemoteItemRemove, async (params) => {
197
- // Only remove the item if it matches the storage key
198
- if (params.data.storageKey === this._storageKey) {
199
- await this.remove(params.data.id);
200
- }
186
+ await this.handleRemoteItemRemove(params);
187
+ });
188
+ // Subscribe to resets from the synchronised storage and update the local storage
189
+ this._eventBusComponent.subscribe(synchronisedStorageModels.SynchronisedStorageTopics.Reset, async (params) => {
190
+ await this.handleReset(params);
201
191
  });
202
192
  }
193
+ /**
194
+ * Handle a local item request.
195
+ * @param event The request parameters
196
+ * @internal
197
+ */
198
+ async handleLocalItemRequest(event) {
199
+ // Only handle the request if it matches the storage key
200
+ if (event.data.storageKey === this._storageKey) {
201
+ let entity;
202
+ try {
203
+ entity = await this._entityStorageConnector.get(event.data.id);
204
+ }
205
+ catch { }
206
+ // Publish the item response with the entity
207
+ this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.LocalItemResponse, {
208
+ storageKey: this._storageKey,
209
+ id: event.data.id,
210
+ entity
211
+ });
212
+ }
213
+ }
214
+ /**
215
+ * Handle a remote item set event.
216
+ * @param event The event parameters
217
+ * @internal
218
+ */
219
+ async handleRemoteItemSet(event) {
220
+ // Only set the item if it matches the storage key
221
+ // and it is from another node, remote updates can not change data for this node
222
+ // That must be done via the regular entity storage methods
223
+ if (event.data.storageKey === this._storageKey &&
224
+ event.data.entity.nodeIdentity !== this._nodeIdentity) {
225
+ await this._entityStorageConnector.set(event.data.entity);
226
+ }
227
+ }
228
+ /**
229
+ * Handle a remote item remove event.
230
+ * @param params The event parameters
231
+ * @internal
232
+ */
233
+ async handleRemoteItemRemove(params) {
234
+ // Only remove the item if it matches the storage key
235
+ // and it is from another node, remote updates can not change data for this node
236
+ // That must be done via the regular entity storage methods
237
+ if (params.data.storageKey === this._storageKey &&
238
+ params.data.nodeIdentity !== this._nodeIdentity) {
239
+ await this._entityStorageConnector.remove(params.data.id);
240
+ }
241
+ }
242
+ /**
243
+ * Handle a batch request.
244
+ * @param event The request parameters
245
+ * @internal
246
+ */
247
+ async handleBatchRequest(event) {
248
+ // Only handle the request if it matches the storage key
249
+ if (event.data.storageKey === this._storageKey) {
250
+ let cursor;
251
+ do {
252
+ const condition = {
253
+ conditions: []
254
+ };
255
+ if (event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Local ||
256
+ event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Remote) {
257
+ condition.conditions.push({
258
+ property: "nodeIdentity",
259
+ value: this._nodeIdentity,
260
+ comparison: event.data.requestMode === synchronisedStorageModels.SyncNodeIdentityMode.Local
261
+ ? entity.ComparisonOperator.Equals
262
+ : entity.ComparisonOperator.NotEquals
263
+ });
264
+ }
265
+ const result = await this._entityStorageConnector.query(condition, [{ property: "dateModified", sortDirection: entity.SortDirection.Ascending }], undefined, cursor, event.data.batchSize);
266
+ cursor = result.cursor;
267
+ // Publish the batch response with the entities
268
+ this._eventBusComponent.publish(synchronisedStorageModels.SynchronisedStorageTopics.BatchResponse, {
269
+ storageKey: this._storageKey,
270
+ entities: result.entities,
271
+ lastEntry: !core.Is.stringValue(cursor)
272
+ });
273
+ } while (core.Is.stringValue(cursor));
274
+ }
275
+ }
276
+ /**
277
+ * Handle a reset event.
278
+ * @param event The event parameters
279
+ * @internal
280
+ */
281
+ async handleReset(event) {
282
+ // Only reset the storage if it matches the storage key
283
+ if (event.data.storageKey === this._storageKey) {
284
+ let cursor;
285
+ let toRemove = [];
286
+ // Build a list of the ids to remove
287
+ do {
288
+ const condition = {
289
+ conditions: []
290
+ };
291
+ // Depending on the reset mode we can filter the entities to remove
292
+ if (event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Local ||
293
+ event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Remote) {
294
+ condition.conditions.push({
295
+ property: "nodeIdentity",
296
+ value: this._nodeIdentity,
297
+ comparison: event.data.resetMode === synchronisedStorageModels.SyncNodeIdentityMode.Local
298
+ ? entity.ComparisonOperator.Equals
299
+ : entity.ComparisonOperator.NotEquals
300
+ });
301
+ }
302
+ const result = await this._entityStorageConnector.query(condition, undefined, undefined, cursor);
303
+ cursor = result.cursor;
304
+ toRemove = toRemove.concat(result.entities.map(entity => entity.id));
305
+ } while (core.Is.stringValue(cursor));
306
+ // Remove the entities
307
+ for (let i = 0; i < toRemove.length; i++) {
308
+ await this.remove(toRemove[i]);
309
+ }
310
+ }
311
+ }
203
312
  }
204
313
 
205
314
  exports.SynchronisedEntityStorageConnector = SynchronisedEntityStorageConnector;
@@ -1,7 +1,7 @@
1
1
  import { Guards, StringHelper, Is, GeneralError, ComponentFactory } from '@twin.org/core';
2
- import { EntitySchemaFactory, EntitySchemaHelper, SortDirection } from '@twin.org/entity';
2
+ import { EntitySchemaFactory, EntitySchemaHelper, ComparisonOperator, SortDirection } from '@twin.org/entity';
3
3
  import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
4
- import { SynchronisedStorageTopics, SyncChangeOperation } from '@twin.org/synchronised-storage-models';
4
+ import { SynchronisedStorageTopics, SyncChangeOperation, SyncNodeIdentityMode } from '@twin.org/synchronised-storage-models';
5
5
 
6
6
  // Copyright 2024 IOTA Stiftung.
7
7
  // SPDX-License-Identifier: Apache-2.0.
@@ -38,6 +38,11 @@ class SynchronisedEntityStorageConnector {
38
38
  * @internal
39
39
  */
40
40
  _eventBusComponent;
41
+ /**
42
+ * The node identity.
43
+ * @internal
44
+ */
45
+ _nodeIdentity;
41
46
  /**
42
47
  * Create a new instance of SynchronisedEntityStorageConnector.
43
48
  * @param options The options for the connector.
@@ -49,13 +54,19 @@ class SynchronisedEntityStorageConnector {
49
54
  this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
50
55
  this._storageKey = options?.config?.storageKey ?? StringHelper.kebabCase(options.entitySchema);
51
56
  this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
52
- const requiredProperties = ["nodeIdentity", "dateModified"];
57
+ const requiredProperties = [
58
+ "id",
59
+ "nodeIdentity",
60
+ "dateModified"
61
+ ];
53
62
  for (const requiredProperty of requiredProperties) {
54
63
  const foundProperty = this._entitySchema.properties?.find(prop => prop.property === requiredProperty);
55
64
  if (Is.empty(foundProperty)) {
56
65
  throw new GeneralError(this.CLASS_NAME, "missingRequiredProperty", { requiredProperty });
57
66
  }
58
- else if (Is.empty(foundProperty.isSecondary) && Is.empty(foundProperty.sortDirection)) {
67
+ else if (Is.empty(foundProperty.isPrimary) &&
68
+ Is.empty(foundProperty.isSecondary) &&
69
+ Is.empty(foundProperty.sortDirection)) {
59
70
  throw new GeneralError(this.CLASS_NAME, "missingRequiredPropertySort", {
60
71
  requiredProperty
61
72
  });
@@ -79,6 +90,7 @@ class SynchronisedEntityStorageConnector {
79
90
  * @returns Nothing.
80
91
  */
81
92
  async start(nodeIdentity, nodeLoggingConnectorType, componentState) {
93
+ this._nodeIdentity = nodeIdentity;
82
94
  // Tell the synchronised storage about this storage key
83
95
  await this._eventBusComponent.publish(SynchronisedStorageTopics.RegisterStorageKey, {
84
96
  storageKey: this._storageKey
@@ -104,15 +116,19 @@ class SynchronisedEntityStorageConnector {
104
116
  */
105
117
  async set(entity, conditions) {
106
118
  Guards.object(this.CLASS_NAME, "entity", entity);
107
- // Make sure the entity has the required properties
108
- entity.dateModified = new Date(Date.now()).toISOString();
109
- await this._entityStorageConnector.set(entity, conditions);
110
- // Tell the synchronised storage about the entity changes
111
- await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
112
- storageKey: this._storageKey,
113
- operation: SyncChangeOperation.Set,
114
- id: entity[this._primaryKey.property]
115
- });
119
+ if (Is.stringValue(this._nodeIdentity)) {
120
+ // Make sure the entity has the required properties
121
+ entity.dateModified = new Date(Date.now()).toISOString();
122
+ entity.nodeIdentity = this._nodeIdentity;
123
+ await this._entityStorageConnector.set(entity, conditions);
124
+ // Tell the synchronised storage about the entity changes
125
+ await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
126
+ storageKey: this._storageKey,
127
+ operation: SyncChangeOperation.Set,
128
+ nodeIdentity: this._nodeIdentity,
129
+ id: entity[this._primaryKey.property]
130
+ });
131
+ }
116
132
  }
117
133
  /**
118
134
  * Remove the entity.
@@ -122,13 +138,16 @@ class SynchronisedEntityStorageConnector {
122
138
  */
123
139
  async remove(id, conditions) {
124
140
  Guards.stringValue(this.CLASS_NAME, "id", id);
125
- await this._entityStorageConnector.remove(id, conditions);
126
- // Tell the synchronised storage about the entity removal
127
- await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
128
- storageKey: this._storageKey,
129
- operation: SyncChangeOperation.Delete,
130
- id
131
- });
141
+ if (Is.stringValue(this._nodeIdentity)) {
142
+ await this._entityStorageConnector.remove(id, conditions);
143
+ // Tell the synchronised storage about the entity removal
144
+ await this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemChange, {
145
+ storageKey: this._storageKey,
146
+ operation: SyncChangeOperation.Delete,
147
+ nodeIdentity: this._nodeIdentity,
148
+ id
149
+ });
150
+ }
132
151
  }
133
152
  /**
134
153
  * Find all the entities which match the conditions.
@@ -150,54 +169,144 @@ class SynchronisedEntityStorageConnector {
150
169
  handleEventBusMessages() {
151
170
  // When the synchronised storage requests an item, we need to provide it
152
171
  this._eventBusComponent.subscribe(SynchronisedStorageTopics.LocalItemRequest, async (params) => {
153
- // Only handle the request if it matches the storage key
154
- if (params.data.storageKey === this._storageKey) {
155
- let entity;
156
- try {
157
- entity = await this._entityStorageConnector.get(params.data.id);
158
- }
159
- catch { }
160
- // Publish the item response with the entity
161
- this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemResponse, {
162
- storageKey: this._storageKey,
163
- id: params.data.id,
164
- entity
165
- });
166
- }
172
+ await this.handleLocalItemRequest(params);
167
173
  });
168
174
  // When the synchronised storage requests a batch, we need to provide it
169
175
  this._eventBusComponent.subscribe(SynchronisedStorageTopics.BatchRequest, async (params) => {
170
- // Only handle the request if it matches the storage key
171
- if (params.data.storageKey === this._storageKey) {
172
- let cursor;
173
- do {
174
- const result = await this._entityStorageConnector.query(undefined, [{ property: "dateModified", sortDirection: SortDirection.Ascending }], undefined, cursor, params.data.batchSize);
175
- cursor = result.cursor;
176
- // Publish the batch response with the entities
177
- this._eventBusComponent.publish(SynchronisedStorageTopics.BatchResponse, {
178
- storageKey: this._storageKey,
179
- primaryKey: this._primaryKey.property,
180
- entities: result.entities,
181
- lastEntry: !Is.stringValue(cursor)
182
- });
183
- } while (Is.stringValue(cursor));
184
- }
176
+ await this.handleBatchRequest(params);
185
177
  });
186
178
  // Subscribe to remote item set events from the synchronised storage and update the local storage
187
179
  this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemSet, async (params) => {
188
- // Only remove the item if it matches the storage key
189
- if (params.data.storageKey === this._storageKey) {
190
- await this.set(params.data.entity);
191
- }
180
+ await this.handleRemoteItemSet(params);
192
181
  });
193
182
  // Subscribe to remote item remove events from the synchronised storage and update the local storage
194
183
  this._eventBusComponent.subscribe(SynchronisedStorageTopics.RemoteItemRemove, async (params) => {
195
- // Only remove the item if it matches the storage key
196
- if (params.data.storageKey === this._storageKey) {
197
- await this.remove(params.data.id);
198
- }
184
+ await this.handleRemoteItemRemove(params);
185
+ });
186
+ // Subscribe to resets from the synchronised storage and update the local storage
187
+ this._eventBusComponent.subscribe(SynchronisedStorageTopics.Reset, async (params) => {
188
+ await this.handleReset(params);
199
189
  });
200
190
  }
191
+ /**
192
+ * Handle a local item request.
193
+ * @param event The request parameters
194
+ * @internal
195
+ */
196
+ async handleLocalItemRequest(event) {
197
+ // Only handle the request if it matches the storage key
198
+ if (event.data.storageKey === this._storageKey) {
199
+ let entity;
200
+ try {
201
+ entity = await this._entityStorageConnector.get(event.data.id);
202
+ }
203
+ catch { }
204
+ // Publish the item response with the entity
205
+ this._eventBusComponent.publish(SynchronisedStorageTopics.LocalItemResponse, {
206
+ storageKey: this._storageKey,
207
+ id: event.data.id,
208
+ entity
209
+ });
210
+ }
211
+ }
212
+ /**
213
+ * Handle a remote item set event.
214
+ * @param event The event parameters
215
+ * @internal
216
+ */
217
+ async handleRemoteItemSet(event) {
218
+ // Only set the item if it matches the storage key
219
+ // and it is from another node, remote updates can not change data for this node
220
+ // That must be done via the regular entity storage methods
221
+ if (event.data.storageKey === this._storageKey &&
222
+ event.data.entity.nodeIdentity !== this._nodeIdentity) {
223
+ await this._entityStorageConnector.set(event.data.entity);
224
+ }
225
+ }
226
+ /**
227
+ * Handle a remote item remove event.
228
+ * @param params The event parameters
229
+ * @internal
230
+ */
231
+ async handleRemoteItemRemove(params) {
232
+ // Only remove the item if it matches the storage key
233
+ // and it is from another node, remote updates can not change data for this node
234
+ // That must be done via the regular entity storage methods
235
+ if (params.data.storageKey === this._storageKey &&
236
+ params.data.nodeIdentity !== this._nodeIdentity) {
237
+ await this._entityStorageConnector.remove(params.data.id);
238
+ }
239
+ }
240
+ /**
241
+ * Handle a batch request.
242
+ * @param event The request parameters
243
+ * @internal
244
+ */
245
+ async handleBatchRequest(event) {
246
+ // Only handle the request if it matches the storage key
247
+ if (event.data.storageKey === this._storageKey) {
248
+ let cursor;
249
+ do {
250
+ const condition = {
251
+ conditions: []
252
+ };
253
+ if (event.data.requestMode === SyncNodeIdentityMode.Local ||
254
+ event.data.requestMode === SyncNodeIdentityMode.Remote) {
255
+ condition.conditions.push({
256
+ property: "nodeIdentity",
257
+ value: this._nodeIdentity,
258
+ comparison: event.data.requestMode === SyncNodeIdentityMode.Local
259
+ ? ComparisonOperator.Equals
260
+ : ComparisonOperator.NotEquals
261
+ });
262
+ }
263
+ const result = await this._entityStorageConnector.query(condition, [{ property: "dateModified", sortDirection: SortDirection.Ascending }], undefined, cursor, event.data.batchSize);
264
+ cursor = result.cursor;
265
+ // Publish the batch response with the entities
266
+ this._eventBusComponent.publish(SynchronisedStorageTopics.BatchResponse, {
267
+ storageKey: this._storageKey,
268
+ entities: result.entities,
269
+ lastEntry: !Is.stringValue(cursor)
270
+ });
271
+ } while (Is.stringValue(cursor));
272
+ }
273
+ }
274
+ /**
275
+ * Handle a reset event.
276
+ * @param event The event parameters
277
+ * @internal
278
+ */
279
+ async handleReset(event) {
280
+ // Only reset the storage if it matches the storage key
281
+ if (event.data.storageKey === this._storageKey) {
282
+ let cursor;
283
+ let toRemove = [];
284
+ // Build a list of the ids to remove
285
+ do {
286
+ const condition = {
287
+ conditions: []
288
+ };
289
+ // Depending on the reset mode we can filter the entities to remove
290
+ if (event.data.resetMode === SyncNodeIdentityMode.Local ||
291
+ event.data.resetMode === SyncNodeIdentityMode.Remote) {
292
+ condition.conditions.push({
293
+ property: "nodeIdentity",
294
+ value: this._nodeIdentity,
295
+ comparison: event.data.resetMode === SyncNodeIdentityMode.Local
296
+ ? ComparisonOperator.Equals
297
+ : ComparisonOperator.NotEquals
298
+ });
299
+ }
300
+ const result = await this._entityStorageConnector.query(condition, undefined, undefined, cursor);
301
+ cursor = result.cursor;
302
+ toRemove = toRemove.concat(result.entities.map(entity => entity.id));
303
+ } while (Is.stringValue(cursor));
304
+ // Remove the entities
305
+ for (let i = 0; i < toRemove.length; i++) {
306
+ await this.remove(toRemove[i]);
307
+ }
308
+ }
309
+ }
201
310
  }
202
311
 
203
312
  export { SynchronisedEntityStorageConnector };
package/docs/changelog.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.2-next.5](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.4...entity-storage-connector-synchronised-v0.0.2-next.5) (2025-08-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * support new synchronised storage features ([0d9ebab](https://github.com/twinfoundation/entity-storage/commit/0d9ebab237040892cab8e7db751aa2e40c0c76e0))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/entity-storage-models bumped from 0.0.2-next.4 to 0.0.2-next.5
16
+ * devDependencies
17
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.2-next.4 to 0.0.2-next.5
18
+
19
+ ## [0.0.2-next.4](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.3...entity-storage-connector-synchronised-v0.0.2-next.4) (2025-08-08)
20
+
21
+
22
+ ### Features
23
+
24
+ * support new synchronised storage features ([a43ee88](https://github.com/twinfoundation/entity-storage/commit/a43ee88431ee3eb8e59e4aeb176009d63ea09d05))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @twin.org/entity-storage-models bumped from 0.0.2-next.3 to 0.0.2-next.4
32
+ * devDependencies
33
+ * @twin.org/entity-storage-connector-memory bumped from 0.0.2-next.3 to 0.0.2-next.4
34
+
3
35
  ## [0.0.2-next.3](https://github.com/twinfoundation/entity-storage/compare/entity-storage-connector-synchronised-v0.0.2-next.2...entity-storage-connector-synchronised-v0.0.2-next.3) (2025-07-25)
4
36
 
5
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-connector-synchronised",
3
- "version": "0.0.2-next.3",
3
+ "version": "0.0.2-next.5",
4
4
  "description": "Entity Storage connector implementation using synchronised storage",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "@twin.org/core": "next",
18
18
  "@twin.org/entity": "next",
19
- "@twin.org/entity-storage-models": "0.0.2-next.3",
19
+ "@twin.org/entity-storage-models": "0.0.2-next.5",
20
20
  "@twin.org/event-bus-models": "next",
21
21
  "@twin.org/logging-models": "next",
22
22
  "@twin.org/nameof": "next",