s3db.js 12.0.1 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +212 -196
  2. package/dist/s3db.cjs.js +1431 -4001
  3. package/dist/s3db.cjs.js.map +1 -1
  4. package/dist/s3db.es.js +1426 -3997
  5. package/dist/s3db.es.js.map +1 -1
  6. package/mcp/entrypoint.js +91 -57
  7. package/package.json +7 -1
  8. package/src/cli/index.js +954 -43
  9. package/src/cli/migration-manager.js +270 -0
  10. package/src/concerns/calculator.js +0 -4
  11. package/src/concerns/metadata-encoding.js +1 -21
  12. package/src/concerns/plugin-storage.js +17 -4
  13. package/src/concerns/typescript-generator.d.ts +171 -0
  14. package/src/concerns/typescript-generator.js +275 -0
  15. package/src/database.class.js +171 -28
  16. package/src/index.js +15 -9
  17. package/src/plugins/api/index.js +0 -1
  18. package/src/plugins/api/routes/resource-routes.js +86 -1
  19. package/src/plugins/api/server.js +79 -3
  20. package/src/plugins/api/utils/openapi-generator.js +195 -5
  21. package/src/plugins/backup/multi-backup-driver.class.js +0 -1
  22. package/src/plugins/backup.plugin.js +7 -14
  23. package/src/plugins/concerns/plugin-dependencies.js +73 -19
  24. package/src/plugins/eventual-consistency/analytics.js +0 -2
  25. package/src/plugins/eventual-consistency/consolidation.js +2 -13
  26. package/src/plugins/eventual-consistency/index.js +0 -1
  27. package/src/plugins/eventual-consistency/install.js +1 -1
  28. package/src/plugins/geo.plugin.js +5 -6
  29. package/src/plugins/importer/index.js +1 -1
  30. package/src/plugins/plugin.class.js +5 -0
  31. package/src/plugins/relation.plugin.js +193 -57
  32. package/src/plugins/replicator.plugin.js +12 -21
  33. package/src/plugins/s3-queue.plugin.js +4 -4
  34. package/src/plugins/scheduler.plugin.js +10 -12
  35. package/src/plugins/state-machine.plugin.js +8 -12
  36. package/src/plugins/tfstate/README.md +1 -1
  37. package/src/plugins/tfstate/errors.js +3 -3
  38. package/src/plugins/tfstate/index.js +41 -67
  39. package/src/plugins/ttl.plugin.js +479 -304
  40. package/src/resource.class.js +263 -61
  41. package/src/schema.class.js +0 -2
  42. package/src/testing/factory.class.js +286 -0
  43. package/src/testing/index.js +15 -0
  44. package/src/testing/seeder.class.js +183 -0
  45. package/dist/s3db-cli.js +0 -55543
@@ -62,7 +62,7 @@ import { StateMachineError } from "./state-machine.errors.js";
62
62
  *
63
63
  * actions: {
64
64
  * onConfirmed: async (context, event, machine) => {
65
- * await machine.this.database.resource('inventory').update(context.productId, {
65
+ * await machine.this.database.resources['inventory'].update(context.productId, {
66
66
  * quantity: { $decrement: context.quantity }
67
67
  * });
68
68
  * await machine.sendNotification(context.customerEmail, 'order_confirmed');
@@ -74,7 +74,7 @@ import { StateMachineError } from "./state-machine.errors.js";
74
74
  *
75
75
  * guards: {
76
76
  * canShip: async (context, event, machine) => {
77
- * const inventory = await machine.this.database.resource('inventory').get(context.productId);
77
+ * const inventory = await machine.this.database.resources['inventory'].get(context.productId);
78
78
  * return inventory.quantity >= context.quantity;
79
79
  * }
80
80
  * },
@@ -352,7 +352,7 @@ export class StateMachinePlugin extends Plugin {
352
352
 
353
353
  for (let attempt = 0; attempt < this.config.retryAttempts; attempt++) {
354
354
  const [ok, err] = await tryFn(() =>
355
- this.database.resource(this.config.transitionLogResource).insert({
355
+ this.database.resources[this.config.transitionLogResource].insert({
356
356
  id: transitionId,
357
357
  machineId,
358
358
  entityId,
@@ -395,13 +395,13 @@ export class StateMachinePlugin extends Plugin {
395
395
 
396
396
  // Try update first (most common case), fallback to insert if doesn't exist
397
397
  const [updateOk] = await tryFn(() =>
398
- this.database.resource(this.config.stateResource).update(stateId, stateData)
398
+ this.database.resources[this.config.stateResource].update(stateId, stateData)
399
399
  );
400
400
 
401
401
  if (!updateOk) {
402
402
  // Record doesn't exist, insert it
403
403
  const [insertOk, insertErr] = await tryFn(() =>
404
- this.database.resource(this.config.stateResource).insert({ id: stateId, ...stateData })
404
+ this.database.resources[this.config.stateResource].insert({ id: stateId, ...stateData })
405
405
  );
406
406
 
407
407
  if (!insertOk && this.config.verbose) {
@@ -476,7 +476,7 @@ export class StateMachinePlugin extends Plugin {
476
476
  if (this.config.persistTransitions) {
477
477
  const stateId = `${machineId}_${entityId}`;
478
478
  const [ok, err, stateRecord] = await tryFn(() =>
479
- this.database.resource(this.config.stateResource).get(stateId)
479
+ this.database.resources[this.config.stateResource].get(stateId)
480
480
  );
481
481
 
482
482
  if (ok && stateRecord) {
@@ -530,7 +530,7 @@ export class StateMachinePlugin extends Plugin {
530
530
  const { limit = 50, offset = 0 } = options;
531
531
 
532
532
  const [ok, err, transitions] = await tryFn(() =>
533
- this.database.resource(this.config.transitionLogResource).query({
533
+ this.database.resources[this.config.transitionLogResource].query({
534
534
  machineId,
535
535
  entityId
536
536
  }, {
@@ -581,7 +581,7 @@ export class StateMachinePlugin extends Plugin {
581
581
 
582
582
  // Try to insert, ignore if already exists (idempotent)
583
583
  const [ok, err] = await tryFn(() =>
584
- this.database.resource(this.config.stateResource).insert({
584
+ this.database.resources[this.config.stateResource].insert({
585
585
  id: stateId,
586
586
  machineId,
587
587
  entityId,
@@ -682,10 +682,6 @@ export class StateMachinePlugin extends Plugin {
682
682
 
683
683
  async stop() {
684
684
  this.machines.clear();
685
- }
686
-
687
- async cleanup() {
688
- await this.stop();
689
685
  this.removeAllListeners();
690
686
  }
691
687
  }
@@ -740,6 +740,6 @@ Verificar que partitions são rápidas:
740
740
 
741
741
  ## 📚 References
742
742
 
743
- - [Terraform State Format](https://www.terraform.io/internals/json-format)
743
+ - [Tfstate Format](https://www.terraform.io/internals/json-format)
744
744
  - [s3db Partitioning Guide](../../docs/partitioning.md)
745
745
  - [Plugin Development](../../docs/plugins.md)
@@ -20,7 +20,7 @@ export class TfStateError extends Error {
20
20
  */
21
21
  export class InvalidStateFileError extends TfStateError {
22
22
  constructor(filePath, reason, context = {}) {
23
- super(`Invalid Terraform state file "${filePath}": ${reason}`, context);
23
+ super(`Invalid Tfstate file "${filePath}": ${reason}`, context);
24
24
  this.name = 'InvalidStateFileError';
25
25
  this.filePath = filePath;
26
26
  this.reason = reason;
@@ -33,7 +33,7 @@ export class InvalidStateFileError extends TfStateError {
33
33
  export class UnsupportedStateVersionError extends TfStateError {
34
34
  constructor(version, supportedVersions, context = {}) {
35
35
  super(
36
- `Terraform state version ${version} is not supported. Supported versions: ${supportedVersions.join(', ')}`,
36
+ `Tfstate version ${version} is not supported. Supported versions: ${supportedVersions.join(', ')}`,
37
37
  context
38
38
  );
39
39
  this.name = 'UnsupportedStateVersionError';
@@ -47,7 +47,7 @@ export class UnsupportedStateVersionError extends TfStateError {
47
47
  */
48
48
  export class StateFileNotFoundError extends TfStateError {
49
49
  constructor(filePath, context = {}) {
50
- super(`Terraform state file not found: ${filePath}`, context);
50
+ super(`Tfstate file not found: ${filePath}`, context);
51
51
  this.name = 'StateFileNotFoundError';
52
52
  this.filePath = filePath;
53
53
  }
@@ -8,7 +8,7 @@
8
8
  * OpenTofu maintains backward compatibility with Terraform's state file format, so this plugin works seamlessly with both.
9
9
  *
10
10
  * === 🚀 Key Features ===
11
- * ✅ **Multi-version support**: Terraform state v3 and v4
11
+ * ✅ **Multi-version support**: Tfstate v3 and v4
12
12
  * ✅ **Multiple sources**: Local files, S3 buckets, remote backends
13
13
  * ✅ **SHA256 deduplication**: Prevent duplicate state imports automatically
14
14
  * ✅ **Historical tracking**: Full audit trail of infrastructure changes
@@ -16,7 +16,7 @@
16
16
  * ✅ **Batch import**: Process multiple state files with controlled parallelism
17
17
  * ✅ **Resource filtering**: Include/exclude resources by type or pattern
18
18
  * ✅ **Auto-sync**: File watching and cron-based monitoring (optional)
19
- * ✅ **Export capability**: Convert back to Terraform state format
19
+ * ✅ **Export capability**: Convert back to Tfstate format
20
20
  * ✅ **Automatic partition optimization**: 10-100x faster queries with zero configuration
21
21
  *
22
22
  * === ⚡ Performance Optimizations (Auto-Applied) ===
@@ -266,59 +266,36 @@ export class TfStatePlugin extends Plugin {
266
266
  constructor(config = {}) {
267
267
  super(config);
268
268
 
269
- // Detect new config format (driver-based) vs legacy
270
- const isNewFormat = config.driver !== undefined;
271
-
272
- if (isNewFormat) {
273
- // New driver-based configuration
274
- this.driverType = config.driver || 's3';
275
- this.driverConfig = config.config || {};
276
-
277
- // Resource names
278
- const resources = config.resources || {};
279
- this.resourceName = resources.resources || 'plg_tfstate_resources';
280
- this.stateFilesName = resources.stateFiles || 'plg_tfstate_state_files';
281
- this.diffsName = resources.diffs || 'plg_tfstate_state_diffs';
282
-
283
- // Monitoring configuration
284
- const monitor = config.monitor || {};
285
- this.monitorEnabled = monitor.enabled || false;
286
- this.monitorCron = monitor.cron || '*/5 * * * *'; // Default: every 5 minutes
287
-
288
- // Diff configuration
289
- const diffs = config.diffs || {};
290
- this.trackDiffs = diffs.enabled !== undefined ? diffs.enabled : true;
291
- this.diffsLookback = diffs.lookback || 10; // How many previous states to compare
292
-
293
- // Partition configuration
294
- this.asyncPartitions = config.asyncPartitions !== undefined ? config.asyncPartitions : true;
295
-
296
- // Legacy fields for backward compatibility
297
- this.autoSync = false;
298
- this.watchPaths = [];
299
- this.filters = config.filters || {};
300
- this.verbose = config.verbose || false;
301
- } else {
302
- // Legacy configuration (backward compatible)
303
- this.driverType = null; // Will use legacy methods
304
- this.driverConfig = {};
305
- this.resourceName = config.resourceName || 'plg_tfstate_resources';
306
- this.stateFilesName = config.stateFilesName || 'plg_tfstate_state_files';
307
- // Support both 'diffsName' and 'stateHistoryName' (legacy) for backward compatibility
308
- this.diffsName = config.diffsName || config.stateHistoryName || 'plg_tfstate_state_diffs';
309
- this.stateHistoryName = this.diffsName; // Alias for backward compatibility
310
- this.autoSync = config.autoSync || false;
311
- this.watchPaths = Array.isArray(config.watchPaths) ? config.watchPaths : [];
312
- this.filters = config.filters || {};
313
- this.trackDiffs = config.trackDiffs !== undefined ? config.trackDiffs : true;
314
- this.diffsLookback = 10;
315
- this.asyncPartitions = config.asyncPartitions !== undefined ? config.asyncPartitions : true;
316
- this.verbose = config.verbose || false;
317
- this.monitorEnabled = false;
318
- this.monitorCron = '*/5 * * * *';
319
- }
320
-
321
- // Supported Terraform state versions
269
+ // Driver-based configuration
270
+ this.driverType = config.driver || null;
271
+ this.driverConfig = config.config || {};
272
+
273
+ // Resource names
274
+ const resources = config.resources || {};
275
+ this.resourceName = resources.resources || config.resourceName || 'plg_tfstate_resources';
276
+ this.stateFilesName = resources.stateFiles || config.stateFilesName || 'plg_tfstate_state_files';
277
+ this.diffsName = resources.diffs || config.diffsName || 'plg_tfstate_state_diffs';
278
+
279
+ // Monitoring configuration
280
+ const monitor = config.monitor || {};
281
+ this.monitorEnabled = monitor.enabled || false;
282
+ this.monitorCron = monitor.cron || '*/5 * * * *';
283
+
284
+ // Diff configuration
285
+ const diffs = config.diffs || {};
286
+ this.trackDiffs = diffs.enabled !== undefined ? diffs.enabled : (config.trackDiffs !== undefined ? config.trackDiffs : true);
287
+ this.diffsLookback = diffs.lookback || 10;
288
+
289
+ // Partition configuration
290
+ this.asyncPartitions = config.asyncPartitions !== undefined ? config.asyncPartitions : true;
291
+
292
+ // Other config
293
+ this.autoSync = config.autoSync || false;
294
+ this.watchPaths = config.watchPaths || [];
295
+ this.filters = config.filters || {};
296
+ this.verbose = config.verbose || false;
297
+
298
+ // Supported Tfstate versions
322
299
  this.supportedVersions = [3, 4];
323
300
 
324
301
  // Internal state
@@ -377,12 +354,12 @@ export class TfStatePlugin extends Plugin {
377
354
  }
378
355
 
379
356
  // Resource 0: Terraform Lineages (Master tracking resource)
380
- // NEW: Tracks unique Terraform state lineages for efficient diff tracking
357
+ // NEW: Tracks unique Tfstate lineages for efficient diff tracking
381
358
  this.lineagesName = 'plg_tfstate_lineages';
382
359
  this.lineagesResource = await this.database.createResource({
383
360
  name: this.lineagesName,
384
361
  attributes: {
385
- id: 'string|required', // = lineage UUID from Terraform state
362
+ id: 'string|required', // = lineage UUID from Tfstate
386
363
  latestSerial: 'number', // Track latest for quick access
387
364
  latestStateId: 'string', // FK to stateFilesResource
388
365
  totalStates: 'number', // Counter
@@ -396,7 +373,7 @@ export class TfStatePlugin extends Plugin {
396
373
  createdBy: 'TfStatePlugin'
397
374
  });
398
375
 
399
- // Resource 1: Terraform State Files Metadata
376
+ // Resource 1: Tfstate Files Metadata
400
377
  // Dedicated to tracking state file metadata with SHA256 hash for deduplication
401
378
  this.stateFilesResource = await this.database.createResource({
402
379
  name: this.stateFilesName,
@@ -460,7 +437,7 @@ export class TfStatePlugin extends Plugin {
460
437
  createdBy: 'TfStatePlugin'
461
438
  });
462
439
 
463
- // Resource 3: Terraform State Diffs
440
+ // Resource 3: Tfstate Diffs
464
441
  // Track changes between state versions (if diff tracking enabled)
465
442
  if (this.trackDiffs) {
466
443
  this.diffsResource = await this.database.createResource({
@@ -511,12 +488,10 @@ export class TfStatePlugin extends Plugin {
511
488
  console.log(`[TfStatePlugin] Created resources: ${resourcesCreated.join(', ')}`);
512
489
  }
513
490
 
514
- // Setup file watchers if autoSync is enabled (legacy mode)
515
491
  if (this.autoSync && this.watchPaths.length > 0) {
516
492
  await this._setupFileWatchers();
517
493
  }
518
494
 
519
- // Setup cron monitoring if enabled (new driver mode)
520
495
  if (this.monitorEnabled && this.driver) {
521
496
  await this._setupCronMonitoring();
522
497
  }
@@ -556,7 +531,6 @@ export class TfStatePlugin extends Plugin {
556
531
  }
557
532
  }
558
533
 
559
- // Stop all file watchers (legacy mode)
560
534
  for (const watcher of this.watchers) {
561
535
  try {
562
536
  // fs.promises.watch returns an AsyncIterator with a return() method
@@ -1281,7 +1255,7 @@ export class TfStatePlugin extends Plugin {
1281
1255
  }
1282
1256
 
1283
1257
  /**
1284
- * Read and parse Terraform state file
1258
+ * Read and parse Tfstate file
1285
1259
  * @private
1286
1260
  */
1287
1261
  async _readStateFile(filePath) {
@@ -1327,7 +1301,7 @@ export class TfStatePlugin extends Plugin {
1327
1301
  }
1328
1302
 
1329
1303
  /**
1330
- * Validate Terraform state version
1304
+ * Validate Tfstate version
1331
1305
  * @private
1332
1306
  */
1333
1307
  _validateStateVersion(state) {
@@ -1343,7 +1317,7 @@ export class TfStatePlugin extends Plugin {
1343
1317
  }
1344
1318
 
1345
1319
  /**
1346
- * Extract resources from Terraform state
1320
+ * Extract resources from Tfstate
1347
1321
  * @private
1348
1322
  */
1349
1323
  async _extractResources(state, filePath, stateFileId, lineageId) {
@@ -2065,14 +2039,14 @@ export class TfStatePlugin extends Plugin {
2065
2039
  }
2066
2040
 
2067
2041
  /**
2068
- * Export resources to Terraform state format
2042
+ * Export resources to Tfstate format
2069
2043
  * @param {Object} options - Export options
2070
2044
  * @param {number} options.serial - Specific serial to export (default: latest)
2071
2045
  * @param {string[]} options.resourceTypes - Filter by resource types
2072
2046
  * @param {string} options.terraformVersion - Terraform version for output (default: '1.5.0')
2073
2047
  * @param {string} options.lineage - State lineage (default: auto-generated)
2074
2048
  * @param {Object} options.outputs - Terraform outputs to include
2075
- * @returns {Promise<Object>} Terraform state object
2049
+ * @returns {Promise<Object>} Tfstate object
2076
2050
  *
2077
2051
  * @example
2078
2052
  * // Export latest state