s3db.js 13.1.0 → 13.2.2

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.
@@ -200,7 +200,7 @@ export class TTLPlugin extends Plugin {
200
200
  console.log(`[TTLPlugin] Installed with ${Object.keys(this.resources).length} resources`);
201
201
  }
202
202
 
203
- this.emit('installed', {
203
+ this.emit('db:plugin:installed', {
204
204
  plugin: 'TTLPlugin',
205
205
  resources: Object.keys(this.resources)
206
206
  });
@@ -512,7 +512,7 @@ export class TTLPlugin extends Plugin {
512
512
  this.stats.lastScanAt = new Date().toISOString();
513
513
  this.stats.lastScanDuration = Date.now() - startTime;
514
514
 
515
- this.emit('scanCompleted', {
515
+ this.emit('plg:ttl:scan-completed', {
516
516
  granularity,
517
517
  duration: this.stats.lastScanDuration,
518
518
  cohorts
@@ -520,7 +520,7 @@ export class TTLPlugin extends Plugin {
520
520
  } catch (error) {
521
521
  console.error(`[TTLPlugin] Error in ${granularity} cleanup:`, error);
522
522
  this.stats.totalErrors++;
523
- this.emit('cleanupError', { granularity, error });
523
+ this.emit('plg:ttl:cleanup-error', { granularity, error });
524
524
  }
525
525
  }
526
526
 
@@ -585,7 +585,7 @@ export class TTLPlugin extends Plugin {
585
585
  await this.expirationIndex.delete(entry.id);
586
586
 
587
587
  this.stats.totalExpired++;
588
- this.emit('recordExpired', { resource: entry.resourceName, record });
588
+ this.emit('plg:ttl:record-expired', { resource: entry.resourceName, record });
589
589
  } catch (error) {
590
590
  console.error(`[TTLPlugin] Error processing expired entry:`, error);
591
591
  this.stats.totalErrors++;
@@ -47,7 +47,7 @@ export class VectorPlugin extends Plugin {
47
47
  }
48
48
 
49
49
  async onInstall() {
50
- this.emit('installed', { plugin: 'VectorPlugin' });
50
+ this.emit('db:plugin:installed', { plugin: 'VectorPlugin' });
51
51
 
52
52
  // Validate vector storage for all resources
53
53
  this.validateVectorStorage();
@@ -57,11 +57,11 @@ export class VectorPlugin extends Plugin {
57
57
  }
58
58
 
59
59
  async onStart() {
60
- this.emit('started', { plugin: 'VectorPlugin' });
60
+ this.emit('db:plugin:started', { plugin: 'VectorPlugin' });
61
61
  }
62
62
 
63
63
  async onStop() {
64
- this.emit('stopped', { plugin: 'VectorPlugin' });
64
+ this.emit('db:plugin:stopped', { plugin: 'VectorPlugin' });
65
65
  }
66
66
 
67
67
  async onUninstall(options) {
@@ -78,7 +78,7 @@ export class VectorPlugin extends Plugin {
78
78
  delete resource.distance;
79
79
  }
80
80
 
81
- this.emit('uninstalled', { plugin: 'VectorPlugin' });
81
+ this.emit('db:plugin:uninstalled', { plugin: 'VectorPlugin' });
82
82
  }
83
83
 
84
84
  /**
@@ -114,12 +114,12 @@ export class VectorPlugin extends Plugin {
114
114
  recommendation: 'body-overflow'
115
115
  };
116
116
 
117
- this.emit('vector:storage-warning', warning);
117
+ this.emit('plg:vector:storage-warning', warning);
118
118
 
119
119
  // Auto-fix if configured
120
120
  if (this.config.autoFixBehavior) {
121
121
  resource.behavior = 'body-overflow';
122
- this.emit('vector:behavior-fixed', {
122
+ this.emit('plg:vector:behavior-fixed', {
123
123
  resource: resource.name,
124
124
  newBehavior: 'body-overflow'
125
125
  });
@@ -163,7 +163,7 @@ export class VectorPlugin extends Plugin {
163
163
 
164
164
  // Check if partition already exists
165
165
  if (resource.config.partitions && resource.config.partitions[partitionName]) {
166
- this.emit('vector:partition-exists', {
166
+ this.emit('plg:vector:partition-exists', {
167
167
  resource: resource.name,
168
168
  vectorField: vectorField.name,
169
169
  partition: partitionName,
@@ -193,7 +193,7 @@ export class VectorPlugin extends Plugin {
193
193
  }
194
194
 
195
195
  // Emit event
196
- this.emit('vector:partition-created', {
196
+ this.emit('plg:vector:partition-created', {
197
197
  resource: resource.name,
198
198
  vectorField: vectorField.name,
199
199
  partition: partitionName,
@@ -296,7 +296,7 @@ export class VectorPlugin extends Plugin {
296
296
  return updates;
297
297
  });
298
298
 
299
- this.emit('vector:hooks-installed', {
299
+ this.emit('plg:vector:hooks-installed', {
300
300
  resource: resource.name,
301
301
  vectorField,
302
302
  trackingField,
@@ -429,7 +429,7 @@ export class VectorPlugin extends Plugin {
429
429
 
430
430
  // Emit event if field detected
431
431
  if (vectorField && this.config.emitEvents) {
432
- this.emit('vector:field-detected', {
432
+ this.emit('plg:vector:field-detected', {
433
433
  resource: resource.name,
434
434
  vectorField,
435
435
  timestamp: Date.now()
@@ -1021,6 +1021,24 @@ export class Resource extends AsyncEventEmitter {
1021
1021
  return Buffer.byteLength(String(body), 'utf8');
1022
1022
  }
1023
1023
 
1024
+ /**
1025
+ * Emit standardized events with optional ID-specific variant
1026
+ *
1027
+ * @private
1028
+ * @param {string} event - Event name
1029
+ * @param {Object} payload - Event payload
1030
+ * @param {string} [id] - Optional ID for ID-specific events
1031
+ */
1032
+ _emitStandardized(event, payload, id = null) {
1033
+ // Emit standardized event
1034
+ this.emit(event, payload);
1035
+
1036
+ // Emit ID-specific event if ID provided
1037
+ if (id) {
1038
+ this.emit(`${event}:${id}`, payload);
1039
+ }
1040
+ }
1041
+
1024
1042
  /**
1025
1043
  * Insert a new resource object
1026
1044
  * @param {Object} attributes - Resource attributes
@@ -1191,24 +1209,24 @@ export class Resource extends AsyncEventEmitter {
1191
1209
  }
1192
1210
 
1193
1211
  // Execute other afterInsert hooks synchronously (excluding partition hook)
1194
- const nonPartitionHooks = this.hooks.afterInsert.filter(hook =>
1212
+ const nonPartitionHooks = this.hooks.afterInsert.filter(hook =>
1195
1213
  !hook.toString().includes('createPartitionReferences')
1196
1214
  );
1197
1215
  let finalResult = insertedObject;
1198
1216
  for (const hook of nonPartitionHooks) {
1199
1217
  finalResult = await hook(finalResult);
1200
1218
  }
1201
-
1202
- // Emit insert event
1203
- this.emit('insert', finalResult);
1219
+
1220
+ // Emit insert event with standardized naming
1221
+ this._emitStandardized('inserted', finalResult, finalResult?.id || insertedObject?.id);
1204
1222
  return finalResult;
1205
1223
  } else {
1206
1224
  // Sync mode: execute all hooks including partition creation
1207
1225
  const finalResult = await this.executeHooks('afterInsert', insertedObject);
1208
-
1209
- // Emit insert event
1210
- this.emit('insert', finalResult);
1211
-
1226
+
1227
+ // Emit insert event with standardized naming
1228
+ this._emitStandardized('inserted', finalResult, finalResult?.id || insertedObject?.id);
1229
+
1212
1230
  // Return the final object
1213
1231
  return finalResult;
1214
1232
  }
@@ -1304,7 +1322,7 @@ export class Resource extends AsyncEventEmitter {
1304
1322
  // Execute afterGet hooks
1305
1323
  data = await this.executeHooks('afterGet', data);
1306
1324
 
1307
- this.emit("get", data);
1325
+ this._emitStandardized("get", "fetched", data, data.id);
1308
1326
  const value = data;
1309
1327
  return value;
1310
1328
  }
@@ -1579,28 +1597,28 @@ export class Resource extends AsyncEventEmitter {
1579
1597
  }
1580
1598
 
1581
1599
  // Execute other afterUpdate hooks synchronously (excluding partition hook)
1582
- const nonPartitionHooks = this.hooks.afterUpdate.filter(hook =>
1600
+ const nonPartitionHooks = this.hooks.afterUpdate.filter(hook =>
1583
1601
  !hook.toString().includes('handlePartitionReferenceUpdates')
1584
1602
  );
1585
1603
  let finalResult = updatedData;
1586
1604
  for (const hook of nonPartitionHooks) {
1587
1605
  finalResult = await hook(finalResult);
1588
1606
  }
1589
-
1590
- this.emit('update', {
1607
+
1608
+ this._emitStandardized('updated', {
1591
1609
  ...updatedData,
1592
1610
  $before: { ...originalData },
1593
1611
  $after: { ...finalResult }
1594
- });
1612
+ }, updatedData.id);
1595
1613
  return finalResult;
1596
1614
  } else {
1597
1615
  // Sync mode: execute all hooks including partition updates
1598
1616
  const finalResult = await this.executeHooks('afterUpdate', updatedData);
1599
- this.emit('update', {
1617
+ this._emitStandardized('updated', {
1600
1618
  ...updatedData,
1601
1619
  $before: { ...originalData },
1602
1620
  $after: { ...finalResult }
1603
- });
1621
+ }, updatedData.id);
1604
1622
  return finalResult;
1605
1623
  }
1606
1624
  }
@@ -2125,11 +2143,11 @@ export class Resource extends AsyncEventEmitter {
2125
2143
  finalResult = await hook(finalResult);
2126
2144
  }
2127
2145
 
2128
- this.emit('update', {
2146
+ this._emitStandardized('updated', {
2129
2147
  ...updatedData,
2130
2148
  $before: { ...originalData },
2131
2149
  $after: { ...finalResult }
2132
- });
2150
+ }, updatedData.id);
2133
2151
 
2134
2152
  return {
2135
2153
  success: true,
@@ -2141,11 +2159,11 @@ export class Resource extends AsyncEventEmitter {
2141
2159
  await this.handlePartitionReferenceUpdates(oldData, newData);
2142
2160
  const finalResult = await this.executeHooks('afterUpdate', updatedData);
2143
2161
 
2144
- this.emit('update', {
2162
+ this._emitStandardized('updated', {
2145
2163
  ...updatedData,
2146
2164
  $before: { ...originalData },
2147
2165
  $after: { ...finalResult }
2148
- });
2166
+ }, updatedData.id);
2149
2167
 
2150
2168
  return {
2151
2169
  success: true,
@@ -2182,13 +2200,13 @@ export class Resource extends AsyncEventEmitter {
2182
2200
  await this.executeHooks('beforeDelete', objectData);
2183
2201
  const key = this.getResourceKey(id);
2184
2202
  const [ok2, err2, response] = await tryFn(() => this.client.deleteObject(key));
2185
-
2203
+
2186
2204
  // Always emit delete event for audit purposes, even if delete fails
2187
- this.emit("delete", {
2205
+ this._emitStandardized("delete", "deleted", {
2188
2206
  ...objectData,
2189
2207
  $before: { ...objectData },
2190
2208
  $after: null
2191
- });
2209
+ }, id);
2192
2210
 
2193
2211
  // If we had an error getting the object, throw it now (after emitting the event)
2194
2212
  if (deleteError) {
@@ -2339,7 +2357,7 @@ export class Resource extends AsyncEventEmitter {
2339
2357
  // Execute afterCount hooks
2340
2358
  await this.executeHooks('afterCount', { count, partition, partitionValues });
2341
2359
 
2342
- this.emit("count", count);
2360
+ this._emitStandardized("count", "counted", count);
2343
2361
  return count;
2344
2362
  }
2345
2363
 
@@ -2367,7 +2385,7 @@ export class Resource extends AsyncEventEmitter {
2367
2385
  return result;
2368
2386
  });
2369
2387
 
2370
- this.emit("insertMany", objects.length);
2388
+ this._emitStandardized("insertMany", "inserted-many", objects.length);
2371
2389
  return results;
2372
2390
  }
2373
2391
 
@@ -2417,7 +2435,7 @@ export class Resource extends AsyncEventEmitter {
2417
2435
  // Execute afterDeleteMany hooks
2418
2436
  await this.executeHooks('afterDeleteMany', { ids, results });
2419
2437
 
2420
- this.emit("deleteMany", ids.length);
2438
+ this._emitStandardized("deleteMany", "deleted-many", ids.length);
2421
2439
  return results;
2422
2440
  }
2423
2441
 
@@ -2431,7 +2449,7 @@ export class Resource extends AsyncEventEmitter {
2431
2449
  const prefix = `resource=${this.name}/data`;
2432
2450
  const deletedCount = await this.client.deleteAll({ prefix });
2433
2451
 
2434
- this.emit("deleteAll", {
2452
+ this._emitStandardized("deleteAll", "deleted-all", {
2435
2453
  version: this.version,
2436
2454
  prefix,
2437
2455
  deletedCount
@@ -2454,7 +2472,7 @@ export class Resource extends AsyncEventEmitter {
2454
2472
  const prefix = `resource=${this.name}`;
2455
2473
  const deletedCount = await this.client.deleteAll({ prefix });
2456
2474
 
2457
- this.emit("deleteAllData", {
2475
+ this._emitStandardized("deleteAllData", "deleted-all-data", {
2458
2476
  resource: this.name,
2459
2477
  prefix,
2460
2478
  deletedCount
@@ -2532,7 +2550,7 @@ export class Resource extends AsyncEventEmitter {
2532
2550
  const idPart = parts.find(part => part.startsWith('id='));
2533
2551
  return idPart ? idPart.replace('id=', '') : null;
2534
2552
  }).filter(Boolean);
2535
- this.emit("listIds", ids.length);
2553
+ this._emitStandardized("listIds", "listed-ids", ids.length);
2536
2554
  return ids;
2537
2555
  }
2538
2556
 
@@ -2580,13 +2598,13 @@ export class Resource extends AsyncEventEmitter {
2580
2598
  const [ok, err, ids] = await tryFn(() => this.listIds({ limit, offset }));
2581
2599
  if (!ok) throw err;
2582
2600
  const results = await this.processListResults(ids, 'main');
2583
- this.emit("list", { count: results.length, errors: 0 });
2601
+ this._emitStandardized("list", "listed", { count: results.length, errors: 0 });
2584
2602
  return results;
2585
2603
  }
2586
2604
 
2587
2605
  async listPartition({ partition, partitionValues, limit, offset = 0 }) {
2588
2606
  if (!this.config.partitions?.[partition]) {
2589
- this.emit("list", { partition, partitionValues, count: 0, errors: 0 });
2607
+ this._emitStandardized("list", "listed", { partition, partitionValues, count: 0, errors: 0 });
2590
2608
  return [];
2591
2609
  }
2592
2610
  const partitionDef = this.config.partitions[partition];
@@ -2596,7 +2614,7 @@ export class Resource extends AsyncEventEmitter {
2596
2614
  const ids = this.extractIdsFromKeys(keys).slice(offset);
2597
2615
  const filteredIds = limit ? ids.slice(0, limit) : ids;
2598
2616
  const results = await this.processPartitionResults(filteredIds, partition, partitionDef, keys);
2599
- this.emit("list", { partition, partitionValues, count: results.length, errors: 0 });
2617
+ this._emitStandardized("list", "listed", { partition, partitionValues, count: results.length, errors: 0 });
2600
2618
  return results;
2601
2619
  }
2602
2620
 
@@ -2652,7 +2670,7 @@ export class Resource extends AsyncEventEmitter {
2652
2670
  }
2653
2671
  return this.handleResourceError(err, id, context);
2654
2672
  });
2655
- this.emit("list", { count: results.length, errors: 0 });
2673
+ this._emitStandardized("list", "listed", { count: results.length, errors: 0 });
2656
2674
  return results;
2657
2675
  }
2658
2676
 
@@ -2725,11 +2743,11 @@ export class Resource extends AsyncEventEmitter {
2725
2743
  */
2726
2744
  handleListError(error, { partition, partitionValues }) {
2727
2745
  if (error.message.includes("Partition '") && error.message.includes("' not found")) {
2728
- this.emit("list", { partition, partitionValues, count: 0, errors: 1 });
2746
+ this._emitStandardized("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
2729
2747
  return [];
2730
2748
  }
2731
2749
 
2732
- this.emit("list", { partition, partitionValues, count: 0, errors: 1 });
2750
+ this._emitStandardized("list", "listed", { partition, partitionValues, count: 0, errors: 1 });
2733
2751
  return [];
2734
2752
  }
2735
2753
 
@@ -2771,7 +2789,7 @@ export class Resource extends AsyncEventEmitter {
2771
2789
  // Execute afterGetMany hooks
2772
2790
  const finalResults = await this.executeHooks('afterGetMany', results);
2773
2791
 
2774
- this.emit("getMany", ids.length);
2792
+ this._emitStandardized("getMany", "fetched-many", ids.length);
2775
2793
  return finalResults;
2776
2794
  }
2777
2795
 
@@ -2862,7 +2880,7 @@ export class Resource extends AsyncEventEmitter {
2862
2880
  hasTotalItems: totalItems !== null
2863
2881
  }
2864
2882
  };
2865
- this.emit("page", result);
2883
+ this._emitStandardized("page", "paginated", result);
2866
2884
  return result;
2867
2885
  });
2868
2886
  if (ok) return result;
@@ -2936,7 +2954,7 @@ export class Resource extends AsyncEventEmitter {
2936
2954
  contentType
2937
2955
  }));
2938
2956
  if (!ok2) throw err2;
2939
- this.emit("setContent", { id, contentType, contentLength: buffer.length });
2957
+ this._emitStandardized("setContent", "content-set", { id, contentType, contentLength: buffer.length }, id);
2940
2958
  return updatedData;
2941
2959
  }
2942
2960
 
@@ -2966,7 +2984,7 @@ export class Resource extends AsyncEventEmitter {
2966
2984
  }
2967
2985
  const buffer = Buffer.from(await response.Body.transformToByteArray());
2968
2986
  const contentType = response.ContentType || null;
2969
- this.emit("content", id, buffer.length, contentType);
2987
+ this._emitStandardized("content", "content-fetched", { id, contentLength: buffer.length, contentType }, id);
2970
2988
  return {
2971
2989
  buffer,
2972
2990
  contentType
@@ -3000,7 +3018,7 @@ export class Resource extends AsyncEventEmitter {
3000
3018
  metadata: existingMetadata,
3001
3019
  }));
3002
3020
  if (!ok2) throw err2;
3003
- this.emit("deleteContent", id);
3021
+ this._emitStandardized("deleteContent", "content-deleted", id, id);
3004
3022
  return response;
3005
3023
  }
3006
3024
 
@@ -3410,7 +3428,7 @@ export class Resource extends AsyncEventEmitter {
3410
3428
  data._partition = partitionName;
3411
3429
  data._partitionValues = partitionValues;
3412
3430
 
3413
- this.emit("getFromPartition", data);
3431
+ this._emitStandardized("getFromPartition", "partition-fetched", data, data.id);
3414
3432
  return data;
3415
3433
  }
3416
3434
 
@@ -3696,6 +3714,137 @@ export class Resource extends AsyncEventEmitter {
3696
3714
  return out;
3697
3715
  }
3698
3716
 
3717
+ // ============================================================================
3718
+ // STATE MACHINE METHODS
3719
+ // ============================================================================
3720
+
3721
+ /**
3722
+ * Send an event to trigger a state transition
3723
+ * @param {string} id - Entity ID
3724
+ * @param {string} event - Event name
3725
+ * @param {Object} [eventData] - Event data
3726
+ * @returns {Promise<Object>} Transition result
3727
+ * @throws {Error} If no state machine is configured for this resource
3728
+ * @example
3729
+ * await orders.state('order-123', 'CONFIRM', { confirmedBy: 'user-456' });
3730
+ */
3731
+ async state(id, event, eventData) {
3732
+ if (!this._stateMachine) {
3733
+ throw new Error(
3734
+ `No state machine configured for resource '${this.name}'. ` +
3735
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3736
+ );
3737
+ }
3738
+ return this._stateMachine.send(id, event, eventData);
3739
+ }
3740
+
3741
+ /**
3742
+ * Get current state of an entity
3743
+ * @param {string} id - Entity ID
3744
+ * @returns {Promise<string>} Current state
3745
+ * @throws {Error} If no state machine is configured for this resource
3746
+ * @example
3747
+ * const currentState = await orders.getState('order-123');
3748
+ */
3749
+ async getState(id) {
3750
+ if (!this._stateMachine) {
3751
+ throw new Error(
3752
+ `No state machine configured for resource '${this.name}'. ` +
3753
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3754
+ );
3755
+ }
3756
+ return this._stateMachine.getState(id);
3757
+ }
3758
+
3759
+ /**
3760
+ * Check if a transition is valid
3761
+ * @param {string} id - Entity ID
3762
+ * @param {string} event - Event name
3763
+ * @returns {Promise<boolean>} True if transition is valid
3764
+ * @throws {Error} If no state machine is configured for this resource
3765
+ * @example
3766
+ * const canConfirm = await orders.canTransition('order-123', 'CONFIRM');
3767
+ */
3768
+ async canTransition(id, event) {
3769
+ if (!this._stateMachine) {
3770
+ throw new Error(
3771
+ `No state machine configured for resource '${this.name}'. ` +
3772
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3773
+ );
3774
+ }
3775
+ return this._stateMachine.canTransition(id, event);
3776
+ }
3777
+
3778
+ /**
3779
+ * Get all valid events for the current state
3780
+ * @param {string} id - Entity ID
3781
+ * @returns {Promise<Array<string>>} Array of valid event names
3782
+ * @throws {Error} If no state machine is configured for this resource
3783
+ * @example
3784
+ * const events = await orders.getValidEvents('order-123');
3785
+ * // Returns: ['SHIP', 'CANCEL']
3786
+ */
3787
+ async getValidEvents(id) {
3788
+ if (!this._stateMachine) {
3789
+ throw new Error(
3790
+ `No state machine configured for resource '${this.name}'. ` +
3791
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3792
+ );
3793
+ }
3794
+ return this._stateMachine.getValidEvents(id);
3795
+ }
3796
+
3797
+ /**
3798
+ * Initialize entity with initial state
3799
+ * @param {string} id - Entity ID
3800
+ * @param {Object} [context] - Initial context data
3801
+ * @returns {Promise<void>}
3802
+ * @throws {Error} If no state machine is configured for this resource
3803
+ * @example
3804
+ * await orders.initializeState('order-456', { customerId: 'user-123' });
3805
+ */
3806
+ async initializeState(id, context) {
3807
+ if (!this._stateMachine) {
3808
+ throw new Error(
3809
+ `No state machine configured for resource '${this.name}'. ` +
3810
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3811
+ );
3812
+ }
3813
+ return this._stateMachine.initializeEntity(id, context);
3814
+ }
3815
+
3816
+ /**
3817
+ * Get transition history for an entity
3818
+ * @param {string} id - Entity ID
3819
+ * @param {Object} [options] - Query options
3820
+ * @param {number} [options.limit=100] - Maximum number of transitions
3821
+ * @param {Date} [options.fromDate] - Filter from date
3822
+ * @param {Date} [options.toDate] - Filter to date
3823
+ * @returns {Promise<Array<Object>>} Transition history
3824
+ * @throws {Error} If no state machine is configured for this resource
3825
+ * @example
3826
+ * const history = await orders.getStateHistory('order-123', { limit: 50 });
3827
+ */
3828
+ async getStateHistory(id, options) {
3829
+ if (!this._stateMachine) {
3830
+ throw new Error(
3831
+ `No state machine configured for resource '${this.name}'. ` +
3832
+ `Ensure StateMachinePlugin is installed and configured for this resource.`
3833
+ );
3834
+ }
3835
+ return this._stateMachine.getTransitionHistory(id, options);
3836
+ }
3837
+
3838
+ /**
3839
+ * Internal method to attach state machine instance
3840
+ * This is called by StateMachinePlugin during initialization
3841
+ * @private
3842
+ * @param {Object} stateMachine - State machine instance
3843
+ */
3844
+ _attachStateMachine(stateMachine) {
3845
+ this._stateMachine = stateMachine;
3846
+ }
3847
+
3699
3848
  }
3700
3849
 
3701
3850
  /**