s3db.js 13.0.0 → 13.2.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.
Files changed (37) hide show
  1. package/README.md +9 -9
  2. package/dist/s3db.cjs.js +3637 -191
  3. package/dist/s3db.cjs.js.map +1 -1
  4. package/dist/s3db.es.js +3637 -191
  5. package/dist/s3db.es.js.map +1 -1
  6. package/package.json +2 -1
  7. package/src/clients/memory-client.class.js +16 -16
  8. package/src/clients/s3-client.class.js +17 -17
  9. package/src/concerns/error-classifier.js +204 -0
  10. package/src/database.class.js +9 -9
  11. package/src/plugins/api/index.js +1 -7
  12. package/src/plugins/api/routes/resource-routes.js +3 -3
  13. package/src/plugins/api/server.js +29 -9
  14. package/src/plugins/audit.plugin.js +2 -4
  15. package/src/plugins/backup.plugin.js +10 -12
  16. package/src/plugins/cache.plugin.js +4 -6
  17. package/src/plugins/concerns/plugin-dependencies.js +12 -0
  18. package/src/plugins/costs.plugin.js +0 -2
  19. package/src/plugins/eventual-consistency/index.js +1 -3
  20. package/src/plugins/fulltext.plugin.js +2 -4
  21. package/src/plugins/geo.plugin.js +3 -5
  22. package/src/plugins/importer/index.js +0 -2
  23. package/src/plugins/index.js +0 -1
  24. package/src/plugins/metrics.plugin.js +2 -4
  25. package/src/plugins/ml.plugin.js +1004 -42
  26. package/src/plugins/plugin.class.js +1 -3
  27. package/src/plugins/queue-consumer.plugin.js +1 -3
  28. package/src/plugins/relation.plugin.js +2 -4
  29. package/src/plugins/replicator.plugin.js +18 -20
  30. package/src/plugins/s3-queue.plugin.js +6 -8
  31. package/src/plugins/scheduler.plugin.js +9 -11
  32. package/src/plugins/state-machine.errors.js +9 -1
  33. package/src/plugins/state-machine.plugin.js +605 -20
  34. package/src/plugins/tfstate/index.js +0 -2
  35. package/src/plugins/ttl.plugin.js +40 -25
  36. package/src/plugins/vector.plugin.js +10 -12
  37. package/src/resource.class.js +58 -40
@@ -4,9 +4,6 @@
4
4
  * Manages HTTP server lifecycle and routing
5
5
  */
6
6
 
7
- import { Hono } from 'hono';
8
- import { serve } from '@hono/node-server';
9
- import { swaggerUI } from '@hono/swagger-ui';
10
7
  import { createResourceRoutes, createRelationalRoutes } from './routes/resource-routes.js';
11
8
  import { errorHandler } from './utils/error-handler.js';
12
9
  import * as formatter from './utils/response-formatter.js';
@@ -46,17 +43,18 @@ export class ApiServer {
46
43
  }
47
44
  };
48
45
 
49
- this.app = new Hono();
46
+ this.app = null; // Will be initialized in start() with dynamic import
50
47
  this.server = null;
51
48
  this.isRunning = false;
52
49
  this.openAPISpec = null;
50
+ this.initialized = false;
53
51
 
54
52
  // Detect if RelationPlugin is installed
55
53
  this.relationsPlugin = this.options.database?.plugins?.relation ||
56
54
  this.options.database?.plugins?.RelationPlugin ||
57
55
  null;
58
56
 
59
- this._setupRoutes();
57
+ // Routes will be setup in start() after dynamic import
60
58
  }
61
59
 
62
60
  /**
@@ -170,7 +168,7 @@ export class ApiServer {
170
168
 
171
169
  // API Documentation UI endpoint
172
170
  if (this.options.docsUI === 'swagger') {
173
- this.app.get('/docs', swaggerUI({
171
+ this.app.get('/docs', this.swaggerUI({
174
172
  url: '/openapi.json'
175
173
  }));
176
174
  } else {
@@ -254,7 +252,7 @@ export class ApiServer {
254
252
  methods: config.methods,
255
253
  customMiddleware: config.customMiddleware || [],
256
254
  enableValidation: config.validation !== false
257
- });
255
+ }, this.Hono);
258
256
 
259
257
  // Mount resource routes
260
258
  this.app.route(`/${version}/${name}`, resourceApp);
@@ -317,7 +315,8 @@ export class ApiServer {
317
315
  resource,
318
316
  relationName,
319
317
  relationConfig,
320
- version
318
+ version,
319
+ this.Hono
321
320
  );
322
321
 
323
322
  // Mount relational routes at /{version}/{resource}/:id/{relation}
@@ -343,11 +342,32 @@ export class ApiServer {
343
342
  return;
344
343
  }
345
344
 
345
+ // Dynamic import of Hono dependencies (peer dependencies)
346
+ // This ensures hono is only loaded when server actually starts
347
+ if (!this.initialized) {
348
+ const { Hono } = await import('hono');
349
+ const { serve } = await import('@hono/node-server');
350
+ const { swaggerUI } = await import('@hono/swagger-ui');
351
+
352
+ // Store for use in _setupRoutes
353
+ this.Hono = Hono;
354
+ this.serve = serve;
355
+ this.swaggerUI = swaggerUI;
356
+
357
+ // Initialize app
358
+ this.app = new Hono();
359
+
360
+ // Setup all routes
361
+ this._setupRoutes();
362
+
363
+ this.initialized = true;
364
+ }
365
+
346
366
  const { port, host } = this.options;
347
367
 
348
368
  return new Promise((resolve, reject) => {
349
369
  try {
350
- this.server = serve({
370
+ this.server = this.serve({
351
371
  fetch: this.app.fetch,
352
372
  port,
353
373
  hostname: host
@@ -1,4 +1,4 @@
1
- import Plugin from "./plugin.class.js";
1
+ import { Plugin } from "./plugin.class.js";
2
2
  import tryFn from "../concerns/try-fn.js";
3
3
 
4
4
  export class AuditPlugin extends Plugin {
@@ -420,6 +420,4 @@ export class AuditPlugin extends Plugin {
420
420
 
421
421
  return deletedCount;
422
422
  }
423
- }
424
-
425
- export default AuditPlugin;
423
+ }
@@ -1,4 +1,4 @@
1
- import Plugin from "./plugin.class.js";
1
+ import { Plugin } from "./plugin.class.js";
2
2
  import tryFn from "../concerns/try-fn.js";
3
3
  import { createBackupDriver, validateBackupConfig } from "./backup/index.js";
4
4
  import { StreamingExporter } from "./backup/streaming-exporter.js";
@@ -149,7 +149,7 @@ export class BackupPlugin extends Plugin {
149
149
  console.log(`[BackupPlugin] Initialized with driver: ${storageInfo.type}`);
150
150
  }
151
151
 
152
- this.emit('initialized', {
152
+ this.emit('db:plugin:initialized', {
153
153
  driver: this.driver.getType(),
154
154
  config: this.driver.getStorageInfo()
155
155
  });
@@ -205,7 +205,7 @@ export class BackupPlugin extends Plugin {
205
205
  await this._executeHook(this.config.onBackupStart, type, { backupId });
206
206
  }
207
207
 
208
- this.emit('backup_start', { id: backupId, type });
208
+ this.emit('plg:backup:start', { id: backupId, type });
209
209
 
210
210
  // Create backup metadata
211
211
  const metadata = await this._createBackupMetadata(backupId, type);
@@ -262,7 +262,7 @@ export class BackupPlugin extends Plugin {
262
262
  await this._executeHook(this.config.onBackupComplete, type, stats);
263
263
  }
264
264
 
265
- this.emit('backup_complete', {
265
+ this.emit('plg:backup:complete', {
266
266
  id: backupId,
267
267
  type,
268
268
  size: totalSize,
@@ -300,7 +300,7 @@ export class BackupPlugin extends Plugin {
300
300
  duration: Date.now() - startTime
301
301
  });
302
302
 
303
- this.emit('backup_error', { id: backupId, type, error: error.message });
303
+ this.emit('plg:backup:error', { id: backupId, type, error: error.message });
304
304
  throw error;
305
305
 
306
306
  } finally {
@@ -579,7 +579,7 @@ export class BackupPlugin extends Plugin {
579
579
  await this._executeHook(this.config.onRestoreStart, backupId, options);
580
580
  }
581
581
 
582
- this.emit('restore_start', { id: backupId, options });
582
+ this.emit('plg:backup:restore-start', { id: backupId, options });
583
583
 
584
584
  // Get backup metadata
585
585
  const backup = await this.getBackupStatus(backupId);
@@ -616,7 +616,7 @@ export class BackupPlugin extends Plugin {
616
616
  await this._executeHook(this.config.onRestoreComplete, backupId, { restored: restoredResources });
617
617
  }
618
618
 
619
- this.emit('restore_complete', {
619
+ this.emit('plg:backup:restore-complete', {
620
620
  id: backupId,
621
621
  restored: restoredResources
622
622
  });
@@ -637,7 +637,7 @@ export class BackupPlugin extends Plugin {
637
637
  await this._executeHook(this.config.onRestoreError, backupId, { error });
638
638
  }
639
639
 
640
- this.emit('restore_error', { id: backupId, error: error.message });
640
+ this.emit('plg:backup:restore-error', { id: backupId, error: error.message });
641
641
  throw error;
642
642
  }
643
643
  }
@@ -973,7 +973,7 @@ export class BackupPlugin extends Plugin {
973
973
  async stop() {
974
974
  // Cancel any active backups
975
975
  for (const backupId of this.activeBackups) {
976
- this.emit('backup_cancelled', { id: backupId });
976
+ this.emit('plg:backup:cancelled', { id: backupId });
977
977
  }
978
978
  this.activeBackups.clear();
979
979
 
@@ -982,6 +982,4 @@ export class BackupPlugin extends Plugin {
982
982
  await this.driver.cleanup();
983
983
  }
984
984
  }
985
- }
986
-
987
- export default BackupPlugin;
985
+ }
@@ -2,7 +2,7 @@ import { join } from "path";
2
2
  import jsonStableStringify from "json-stable-stringify";
3
3
  import crypto from 'crypto';
4
4
 
5
- import Plugin from "./plugin.class.js";
5
+ import { Plugin } from "./plugin.class.js";
6
6
  import S3Cache from "./cache/s3-cache.class.js";
7
7
  import MemoryCache from "./cache/memory-cache.class.js";
8
8
  import { FilesystemCache } from "./cache/filesystem-cache.class.js";
@@ -434,7 +434,7 @@ export class CachePlugin extends Plugin {
434
434
  const [ok, err] = await this.clearCacheWithRetry(resource.cache, specificKey);
435
435
 
436
436
  if (!ok) {
437
- this.emit('cache_clear_error', {
437
+ this.emit('plg:cache:clear-error', {
438
438
  resource: resource.name,
439
439
  method,
440
440
  id: data.id,
@@ -456,7 +456,7 @@ export class CachePlugin extends Plugin {
456
456
  const [ok, err] = await this.clearCacheWithRetry(resource.cache, partitionKeyPrefix);
457
457
 
458
458
  if (!ok) {
459
- this.emit('cache_clear_error', {
459
+ this.emit('plg:cache:clear-error', {
460
460
  resource: resource.name,
461
461
  partition: partitionName,
462
462
  error: err.message
@@ -475,7 +475,7 @@ export class CachePlugin extends Plugin {
475
475
  const [ok, err] = await this.clearCacheWithRetry(resource.cache, keyPrefix);
476
476
 
477
477
  if (!ok) {
478
- this.emit('cache_clear_error', {
478
+ this.emit('plg:cache:clear-error', {
479
479
  resource: resource.name,
480
480
  type: 'broad',
481
481
  error: err.message
@@ -781,5 +781,3 @@ export class CachePlugin extends Plugin {
781
781
  return parts.join(' ');
782
782
  }
783
783
  }
784
-
785
- export default CachePlugin;
@@ -109,6 +109,18 @@ export const PLUGIN_DEPENDENCIES = {
109
109
  npmUrl: 'https://www.npmjs.com/package/@hono/swagger-ui'
110
110
  }
111
111
  }
112
+ },
113
+ 'ml-plugin': {
114
+ name: 'ML Plugin',
115
+ docsUrl: 'https://github.com/forattini-dev/s3db.js/blob/main/docs/plugins/ml-plugin.md',
116
+ dependencies: {
117
+ '@tensorflow/tfjs-node': {
118
+ version: '^4.0.0',
119
+ description: 'TensorFlow.js for Node.js with native bindings',
120
+ installCommand: 'pnpm add @tensorflow/tfjs-node',
121
+ npmUrl: 'https://www.npmjs.com/package/@tensorflow/tfjs-node'
122
+ }
123
+ }
112
124
  }
113
125
  };
114
126
 
@@ -299,5 +299,3 @@ export class CostsPlugin extends Plugin {
299
299
  }
300
300
  }
301
301
  }
302
-
303
- export default CostsPlugin;
@@ -4,7 +4,7 @@
4
4
  * @module eventual-consistency
5
5
  */
6
6
 
7
- import Plugin from "../plugin.class.js";
7
+ import { Plugin } from "../plugin.class.js";
8
8
  import { createConfig, validateResourcesConfig, logConfigWarnings, logInitialization } from "./config.js";
9
9
  import { detectTimezone, getCohortInfo, createFieldHandler } from "./utils.js";
10
10
  import { createPartitionConfig } from "./partitions.js";
@@ -733,5 +733,3 @@ export class EventualConsistencyPlugin extends Plugin {
733
733
  return diagnostics;
734
734
  }
735
735
  }
736
-
737
- export default EventualConsistencyPlugin;
@@ -1,4 +1,4 @@
1
- import Plugin from "./plugin.class.js";
1
+ import { Plugin } from "./plugin.class.js";
2
2
  import tryFn from "../concerns/try-fn.js";
3
3
  import { FulltextError } from "./fulltext.errors.js";
4
4
 
@@ -554,6 +554,4 @@ export class FullTextPlugin extends Plugin {
554
554
  // Save changes
555
555
  await this.saveIndexes();
556
556
  }
557
- }
558
-
559
- export default FullTextPlugin;
557
+ }
@@ -1,4 +1,4 @@
1
- import Plugin from "./plugin.class.js";
1
+ import { Plugin } from "./plugin.class.js";
2
2
  import tryFn from "../concerns/try-fn.js";
3
3
 
4
4
  /**
@@ -109,7 +109,7 @@ export class GeoPlugin extends Plugin {
109
109
  console.log(`[GeoPlugin] Installed with ${Object.keys(this.resources).length} resources`);
110
110
  }
111
111
 
112
- this.emit('installed', {
112
+ this.emit('db:plugin:installed', {
113
113
  plugin: 'GeoPlugin',
114
114
  resources: Object.keys(this.resources)
115
115
  });
@@ -861,12 +861,10 @@ export class GeoPlugin extends Plugin {
861
861
  console.log('[GeoPlugin] Uninstalled');
862
862
  }
863
863
 
864
- this.emit('uninstalled', {
864
+ this.emit('db:plugin:uninstalled', {
865
865
  plugin: 'GeoPlugin'
866
866
  });
867
867
 
868
868
  await super.uninstall();
869
869
  }
870
870
  }
871
-
872
- export default GeoPlugin;
@@ -1016,5 +1016,3 @@ export const Transformers = {
1016
1016
  return String(value).trim();
1017
1017
  }
1018
1018
  };
1019
-
1020
- export default ImporterPlugin;
@@ -1,6 +1,5 @@
1
1
  export * from './plugin.class.js'
2
2
  export * from './plugin.obj.js'
3
- export { default as Plugin } from './plugin.class.js'
4
3
 
5
4
  // plugins:
6
5
  // ApiPlugin is exported separately to avoid bundling hono dependencies
@@ -1,4 +1,4 @@
1
- import Plugin from "./plugin.class.js";
1
+ import { Plugin } from "./plugin.class.js";
2
2
  import tryFn from "../concerns/try-fn.js";
3
3
 
4
4
  export class MetricsPlugin extends Plugin {
@@ -831,6 +831,4 @@ export class MetricsPlugin extends Plugin {
831
831
  console.error('[Metrics Plugin] Standalone metrics server error:', err);
832
832
  });
833
833
  }
834
- }
835
-
836
- export default MetricsPlugin;
834
+ }