s3db.js 11.3.2 → 12.0.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 (82) hide show
  1. package/README.md +102 -8
  2. package/dist/s3db.cjs.js +36664 -15480
  3. package/dist/s3db.cjs.js.map +1 -1
  4. package/dist/s3db.d.ts +57 -0
  5. package/dist/s3db.es.js +36661 -15531
  6. package/dist/s3db.es.js.map +1 -1
  7. package/mcp/entrypoint.js +58 -0
  8. package/mcp/tools/documentation.js +434 -0
  9. package/mcp/tools/index.js +4 -0
  10. package/package.json +27 -6
  11. package/src/behaviors/user-managed.js +13 -6
  12. package/src/client.class.js +41 -46
  13. package/src/concerns/base62.js +85 -0
  14. package/src/concerns/dictionary-encoding.js +294 -0
  15. package/src/concerns/geo-encoding.js +256 -0
  16. package/src/concerns/high-performance-inserter.js +34 -30
  17. package/src/concerns/ip.js +325 -0
  18. package/src/concerns/metadata-encoding.js +345 -66
  19. package/src/concerns/money.js +193 -0
  20. package/src/concerns/partition-queue.js +7 -4
  21. package/src/concerns/plugin-storage.js +39 -19
  22. package/src/database.class.js +76 -74
  23. package/src/errors.js +0 -4
  24. package/src/plugins/api/auth/api-key-auth.js +88 -0
  25. package/src/plugins/api/auth/basic-auth.js +154 -0
  26. package/src/plugins/api/auth/index.js +112 -0
  27. package/src/plugins/api/auth/jwt-auth.js +169 -0
  28. package/src/plugins/api/index.js +539 -0
  29. package/src/plugins/api/middlewares/index.js +15 -0
  30. package/src/plugins/api/middlewares/validator.js +185 -0
  31. package/src/plugins/api/routes/auth-routes.js +241 -0
  32. package/src/plugins/api/routes/resource-routes.js +304 -0
  33. package/src/plugins/api/server.js +350 -0
  34. package/src/plugins/api/utils/error-handler.js +147 -0
  35. package/src/plugins/api/utils/openapi-generator.js +1240 -0
  36. package/src/plugins/api/utils/response-formatter.js +218 -0
  37. package/src/plugins/backup/streaming-exporter.js +132 -0
  38. package/src/plugins/backup.plugin.js +103 -50
  39. package/src/plugins/cache/s3-cache.class.js +95 -47
  40. package/src/plugins/cache.plugin.js +107 -9
  41. package/src/plugins/concerns/plugin-dependencies.js +313 -0
  42. package/src/plugins/concerns/prometheus-formatter.js +255 -0
  43. package/src/plugins/consumers/rabbitmq-consumer.js +4 -0
  44. package/src/plugins/consumers/sqs-consumer.js +4 -0
  45. package/src/plugins/costs.plugin.js +255 -39
  46. package/src/plugins/eventual-consistency/helpers.js +15 -1
  47. package/src/plugins/geo.plugin.js +873 -0
  48. package/src/plugins/importer/index.js +1020 -0
  49. package/src/plugins/index.js +11 -0
  50. package/src/plugins/metrics.plugin.js +163 -4
  51. package/src/plugins/queue-consumer.plugin.js +6 -27
  52. package/src/plugins/relation.errors.js +139 -0
  53. package/src/plugins/relation.plugin.js +1242 -0
  54. package/src/plugins/replicators/bigquery-replicator.class.js +180 -8
  55. package/src/plugins/replicators/dynamodb-replicator.class.js +383 -0
  56. package/src/plugins/replicators/index.js +28 -3
  57. package/src/plugins/replicators/mongodb-replicator.class.js +391 -0
  58. package/src/plugins/replicators/mysql-replicator.class.js +558 -0
  59. package/src/plugins/replicators/planetscale-replicator.class.js +409 -0
  60. package/src/plugins/replicators/postgres-replicator.class.js +182 -7
  61. package/src/plugins/replicators/s3db-replicator.class.js +1 -12
  62. package/src/plugins/replicators/schema-sync.helper.js +601 -0
  63. package/src/plugins/replicators/sqs-replicator.class.js +11 -9
  64. package/src/plugins/replicators/turso-replicator.class.js +416 -0
  65. package/src/plugins/replicators/webhook-replicator.class.js +612 -0
  66. package/src/plugins/state-machine.plugin.js +122 -68
  67. package/src/plugins/tfstate/README.md +745 -0
  68. package/src/plugins/tfstate/base-driver.js +80 -0
  69. package/src/plugins/tfstate/errors.js +112 -0
  70. package/src/plugins/tfstate/filesystem-driver.js +129 -0
  71. package/src/plugins/tfstate/index.js +2660 -0
  72. package/src/plugins/tfstate/s3-driver.js +192 -0
  73. package/src/plugins/ttl.plugin.js +536 -0
  74. package/src/resource.class.js +14 -10
  75. package/src/s3db.d.ts +57 -0
  76. package/src/schema.class.js +366 -32
  77. package/SECURITY.md +0 -76
  78. package/src/partition-drivers/base-partition-driver.js +0 -106
  79. package/src/partition-drivers/index.js +0 -66
  80. package/src/partition-drivers/memory-partition-driver.js +0 -289
  81. package/src/partition-drivers/sqs-partition-driver.js +0 -337
  82. package/src/partition-drivers/sync-partition-driver.js +0 -38
package/SECURITY.md DELETED
@@ -1,76 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- | Version | Supported |
6
- | ------- | ------------------ |
7
- | 11.x.x | :white_check_mark: |
8
- | < 11.0 | :x: |
9
-
10
- ## Known Security Advisories
11
-
12
- ### Development Dependencies
13
-
14
- The following vulnerabilities exist in **development-only** dependencies and **do not affect** the published npm package or runtime security:
15
-
16
- #### pkg (GHSA-22r3-9w55-cj54) - MODERATE
17
- - **Status**: Acknowledged, monitored
18
- - **Impact**: Local privilege escalation
19
- - **Scope**: Only affects developers running `pnpm run build:binaries`
20
- - **Mitigation**: pkg is deprecated and archived. No patched version available (`<0.0.0`).
21
- - **Risk Assessment**: LOW - Only used for creating standalone binaries during release process
22
- - **Future Plans**: Migrate to Node.js Single Executable Applications (SEA) when stable
23
-
24
- #### tar-fs - HIGH
25
- - **Status**: RESOLVED in v11.1.1+
26
- - **Fix**: Updated to patched version 2.1.4+
27
-
28
- ## Reporting a Vulnerability
29
-
30
- If you discover a security vulnerability in the **runtime code** (not dev dependencies), please report it by:
31
-
32
- 1. **DO NOT** open a public issue
33
- 2. Email: [security contact - update this]
34
- 3. Include:
35
- - Description of the vulnerability
36
- - Steps to reproduce
37
- - Potential impact
38
- - Suggested fix (if any)
39
-
40
- ### Response Timeline
41
-
42
- - **Initial Response**: Within 48 hours
43
- - **Status Update**: Within 7 days
44
- - **Fix Timeline**: Depends on severity
45
- - Critical: 7 days
46
- - High: 14 days
47
- - Medium: 30 days
48
- - Low: 60 days
49
-
50
- ## Security Best Practices
51
-
52
- ### For Users
53
-
54
- 1. **Always encrypt sensitive data**: Use `secret` field type for passwords, tokens, etc.
55
- 2. **Validate credentials**: Never commit AWS credentials to version control
56
- 3. **Use IAM policies**: Implement least-privilege access for S3 buckets
57
- 4. **Enable paranoid mode**: For production, use `paranoid: true` for soft deletes
58
- 5. **Audit hooks**: Review serialized functions before deploying to production
59
-
60
- ### For Contributors
61
-
62
- 1. **No secrets in tests**: Use environment variables or LocalStack
63
- 2. **Validate input**: All user input should be validated before S3 operations
64
- 3. **Handle errors safely**: Never expose AWS error details to end users
65
- 4. **Review dependencies**: Run `pnpm audit` before submitting PRs
66
- 5. **Test encryption**: Verify `secret` fields are actually encrypted in S3
67
-
68
- ## Audit Configuration
69
-
70
- This project uses `audit-level=high` in `.npmrc` to focus on critical vulnerabilities affecting production. Moderate/low severity issues in dev-only dependencies are monitored but may not block releases if:
71
-
72
- - They only affect development tools
73
- - No patch is available
74
- - The risk is assessed as acceptable
75
-
76
- Current audit threshold: **HIGH** (ignores moderate/low in dev dependencies)
@@ -1,106 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { PartitionDriverError } from '../errors.js';
3
-
4
- /**
5
- * Base class for all partition drivers
6
- * Defines the interface that all drivers must implement
7
- */
8
- export class BasePartitionDriver extends EventEmitter {
9
- constructor(options = {}) {
10
- super();
11
- this.options = options;
12
- this.stats = {
13
- queued: 0,
14
- processed: 0,
15
- failed: 0,
16
- processing: 0
17
- };
18
- }
19
-
20
- /**
21
- * Initialize the driver
22
- */
23
- async initialize() {
24
- // Override in subclasses if needed
25
- }
26
-
27
- /**
28
- * Queue partition operations for processing
29
- * @param {Object} operation - The partition operation to queue
30
- * @param {string} operation.type - 'create', 'update', or 'delete'
31
- * @param {Object} operation.resource - The resource instance
32
- * @param {Object} operation.data - The data for the operation
33
- */
34
- async queue(operation) {
35
- throw new PartitionDriverError('queue() must be implemented by subclass', {
36
- driver: this.name || 'BasePartitionDriver',
37
- operation: 'queue',
38
- suggestion: 'Extend BasePartitionDriver and implement the queue() method'
39
- });
40
- }
41
-
42
- /**
43
- * Process a single partition operation
44
- */
45
- async processOperation(operation) {
46
- const { type, resource, data } = operation;
47
-
48
- try {
49
- this.stats.processing++;
50
-
51
- switch (type) {
52
- case 'create':
53
- await resource.createPartitionReferences(data.object);
54
- break;
55
-
56
- case 'update':
57
- await resource.handlePartitionReferenceUpdates(data.original, data.updated);
58
- break;
59
-
60
- case 'delete':
61
- await resource.deletePartitionReferences(data.object);
62
- break;
63
-
64
- default:
65
- throw new PartitionDriverError(`Unknown partition operation type: ${type}`, {
66
- driver: this.name || 'BasePartitionDriver',
67
- operation: type,
68
- availableOperations: ['create', 'update', 'delete'],
69
- suggestion: 'Use one of the supported partition operations: create, update, or delete'
70
- });
71
- }
72
-
73
- this.stats.processed++;
74
- this.emit('processed', operation);
75
-
76
- } catch (error) {
77
- this.stats.failed++;
78
- this.emit('error', { operation, error });
79
- throw error;
80
- } finally {
81
- this.stats.processing--;
82
- }
83
- }
84
-
85
- /**
86
- * Flush any pending operations
87
- */
88
- async flush() {
89
- // Override in subclasses if needed
90
- }
91
-
92
- /**
93
- * Get driver statistics
94
- */
95
- getStats() {
96
- return { ...this.stats };
97
- }
98
-
99
- /**
100
- * Shutdown the driver
101
- */
102
- async shutdown() {
103
- await this.flush();
104
- this.removeAllListeners();
105
- }
106
- }
@@ -1,66 +0,0 @@
1
- import { SyncPartitionDriver } from './sync-partition-driver.js';
2
- import { MemoryPartitionDriver } from './memory-partition-driver.js';
3
- import { SQSPartitionDriver } from './sqs-partition-driver.js';
4
- import { PartitionDriverError } from '../errors.js';
5
-
6
- /**
7
- * Partition driver factory
8
- */
9
- export class PartitionDriverFactory {
10
- static drivers = {
11
- sync: SyncPartitionDriver,
12
- memory: MemoryPartitionDriver,
13
- sqs: SQSPartitionDriver
14
- };
15
-
16
- /**
17
- * Create a partition driver instance
18
- * @param {string|Object} config - Driver name or configuration object
19
- * @returns {BasePartitionDriver} Driver instance
20
- */
21
- static create(config) {
22
- // Handle string shorthand
23
- if (typeof config === 'string') {
24
- config = { driver: config };
25
- }
26
-
27
- // Default to memory driver
28
- const driverName = config.driver || 'memory';
29
-
30
- // Get driver class
31
- const DriverClass = this.drivers[driverName];
32
- if (!DriverClass) {
33
- throw new PartitionDriverError(`Unknown partition driver: ${driverName}`, {
34
- driver: driverName,
35
- operation: 'create',
36
- availableDrivers: Object.keys(this.drivers),
37
- suggestion: `Use one of the available drivers: ${Object.keys(this.drivers).join(', ')}, or register a custom driver`
38
- });
39
- }
40
-
41
- // Create and initialize driver
42
- const driver = new DriverClass(config);
43
-
44
- return driver;
45
- }
46
-
47
- /**
48
- * Register a custom driver
49
- */
50
- static register(name, DriverClass) {
51
- this.drivers[name] = DriverClass;
52
- }
53
-
54
- /**
55
- * Get available driver names
56
- */
57
- static getAvailableDrivers() {
58
- return Object.keys(this.drivers);
59
- }
60
- }
61
-
62
- // Export individual drivers
63
- export { BasePartitionDriver } from './base-partition-driver.js';
64
- export { SyncPartitionDriver } from './sync-partition-driver.js';
65
- export { MemoryPartitionDriver } from './memory-partition-driver.js';
66
- export { SQSPartitionDriver } from './sqs-partition-driver.js';
@@ -1,289 +0,0 @@
1
- import { BasePartitionDriver } from './base-partition-driver.js';
2
- import { PromisePool } from '@supercharge/promise-pool';
3
- import { PartitionDriverError } from '../errors.js';
4
-
5
- /**
6
- * In-memory partition driver with background processing
7
- * Queues operations in memory and processes them asynchronously
8
- * Fast and efficient for single-instance applications
9
- */
10
- export class MemoryPartitionDriver extends BasePartitionDriver {
11
- constructor(options = {}) {
12
- super(options);
13
- this.name = 'memory';
14
-
15
- // Configuration
16
- this.batchSize = options.batchSize || 100;
17
- this.concurrency = options.concurrency || 10;
18
- this.flushInterval = options.flushInterval || 1000;
19
- this.maxQueueSize = options.maxQueueSize || 10000;
20
- this.maxRetries = options.maxRetries || 3;
21
-
22
- // State
23
- this.queue = [];
24
- this.isProcessing = false;
25
- this.flushTimer = null;
26
- this.retryQueue = [];
27
- }
28
-
29
- async initialize() {
30
- // Start background processor
31
- this.startProcessor();
32
- }
33
-
34
- /**
35
- * Add operation to in-memory queue
36
- */
37
- async queue(operation) {
38
- // Check queue size limit
39
- if (this.queue.length >= this.maxQueueSize) {
40
- const error = new PartitionDriverError('Memory queue full - backpressure detected', {
41
- driver: 'memory',
42
- operation: 'queue',
43
- queueSize: this.queue.length,
44
- maxQueueSize: this.maxQueueSize,
45
- suggestion: 'Increase maxQueueSize, enable rejectOnFull, or reduce operation rate'
46
- });
47
- this.emit('queueFull', { operation, queueSize: this.queue.length });
48
-
49
- if (this.options.rejectOnFull) {
50
- throw error;
51
- }
52
-
53
- // Wait for some space
54
- await this.waitForSpace();
55
- }
56
-
57
- // Add to queue with metadata
58
- const queueItem = {
59
- ...operation,
60
- id: `${Date.now()}-${Math.random()}`,
61
- queuedAt: new Date(),
62
- attempts: 0
63
- };
64
-
65
- this.queue.push(queueItem);
66
- this.stats.queued++;
67
-
68
- // Auto-flush when batch size reached
69
- if (this.queue.length >= this.batchSize) {
70
- this.triggerFlush();
71
- }
72
-
73
- return {
74
- success: true,
75
- driver: 'memory',
76
- queuePosition: this.queue.length,
77
- queueId: queueItem.id
78
- };
79
- }
80
-
81
- /**
82
- * Start the background processor
83
- */
84
- startProcessor() {
85
- // Set up periodic flush
86
- if (this.flushInterval > 0) {
87
- this.flushTimer = setInterval(() => {
88
- if (this.queue.length > 0 && !this.isProcessing) {
89
- this.processQueue();
90
- }
91
- }, this.flushInterval);
92
- }
93
- }
94
-
95
- /**
96
- * Trigger immediate flush
97
- */
98
- triggerFlush() {
99
- if (!this.isProcessing) {
100
- setImmediate(() => this.processQueue());
101
- }
102
- }
103
-
104
- /**
105
- * Process queued operations in batches
106
- */
107
- async processQueue() {
108
- if (this.isProcessing || this.queue.length === 0) return;
109
-
110
- this.isProcessing = true;
111
-
112
- try {
113
- // Take a batch from the queue
114
- const batch = this.queue.splice(0, this.batchSize);
115
-
116
- // Process in parallel with concurrency control
117
- const { results, errors } = await PromisePool
118
- .for(batch)
119
- .withConcurrency(this.concurrency)
120
- .process(async (item) => {
121
- try {
122
- await this.processOperation(item);
123
- return { success: true, item };
124
- } catch (error) {
125
- return this.handleError(item, error);
126
- }
127
- });
128
-
129
- // Handle successful results
130
- const successful = results.filter(r => r.success);
131
- this.emit('batchProcessed', {
132
- processed: successful.length,
133
- failed: errors.length,
134
- retried: results.filter(r => r.retried).length
135
- });
136
-
137
- } finally {
138
- this.isProcessing = false;
139
-
140
- // Continue processing if more items
141
- if (this.queue.length > 0) {
142
- setImmediate(() => this.processQueue());
143
- }
144
-
145
- // Process retry queue if needed
146
- if (this.retryQueue.length > 0) {
147
- this.processRetryQueue();
148
- }
149
- }
150
- }
151
-
152
- /**
153
- * Handle processing errors with retry logic
154
- */
155
- handleError(item, error) {
156
- item.attempts++;
157
- item.lastError = error;
158
-
159
- if (item.attempts < this.maxRetries) {
160
- // Add to retry queue with exponential backoff
161
- const delay = Math.min(1000 * Math.pow(2, item.attempts - 1), 30000);
162
-
163
- setTimeout(() => {
164
- this.retryQueue.push(item);
165
- if (!this.isProcessing) {
166
- this.processRetryQueue();
167
- }
168
- }, delay);
169
-
170
- this.emit('retry', { item, error, attempt: item.attempts, delay });
171
- return { success: false, retried: true, item };
172
- } else {
173
- // Max retries exceeded
174
- this.emit('failed', { item, error, attempts: item.attempts });
175
- return { success: false, retried: false, item };
176
- }
177
- }
178
-
179
- /**
180
- * Process retry queue
181
- */
182
- async processRetryQueue() {
183
- if (this.retryQueue.length === 0) return;
184
-
185
- // Move retry items back to main queue
186
- const retryItems = this.retryQueue.splice(0, this.batchSize);
187
- this.queue.unshift(...retryItems);
188
-
189
- // Trigger processing
190
- this.triggerFlush();
191
- }
192
-
193
- /**
194
- * Wait for queue space
195
- */
196
- async waitForSpace() {
197
- const checkInterval = 100;
198
- const maxWait = 30000;
199
- const startTime = Date.now();
200
-
201
- while (this.queue.length >= this.maxQueueSize) {
202
- if (Date.now() - startTime > maxWait) {
203
- throw new PartitionDriverError('Timeout waiting for queue space', {
204
- driver: 'memory',
205
- operation: 'waitForSpace',
206
- queueSize: this.queue.length,
207
- maxQueueSize: this.maxQueueSize,
208
- waitedMs: Date.now() - startTime,
209
- maxWaitMs: maxWait,
210
- suggestion: 'Queue is full and not draining fast enough. Increase maxQueueSize or concurrency'
211
- });
212
- }
213
-
214
- await new Promise(resolve => setTimeout(resolve, checkInterval));
215
- }
216
- }
217
-
218
- /**
219
- * Force flush all pending operations
220
- */
221
- async flush() {
222
- // Process all remaining items
223
- while (this.queue.length > 0 || this.retryQueue.length > 0 || this.isProcessing) {
224
- await this.processQueue();
225
- await new Promise(resolve => setTimeout(resolve, 10));
226
- }
227
- }
228
-
229
- /**
230
- * Get detailed statistics
231
- */
232
- getStats() {
233
- return {
234
- ...super.getStats(),
235
- queueLength: this.queue.length,
236
- retryQueueLength: this.retryQueue.length,
237
- isProcessing: this.isProcessing,
238
- memoryUsage: this.estimateMemoryUsage()
239
- };
240
- }
241
-
242
- /**
243
- * Estimate memory usage of the queue
244
- */
245
- estimateMemoryUsage() {
246
- // Rough estimate: 1KB per queue item
247
- const bytes = (this.queue.length + this.retryQueue.length) * 1024;
248
- return {
249
- bytes,
250
- mb: (bytes / 1024 / 1024).toFixed(2)
251
- };
252
- }
253
-
254
- /**
255
- * Shutdown the driver
256
- */
257
- async shutdown() {
258
- // Stop the flush timer
259
- if (this.flushTimer) {
260
- clearInterval(this.flushTimer);
261
- this.flushTimer = null;
262
- }
263
-
264
- // Flush remaining items
265
- await this.flush();
266
-
267
- // Clear queues
268
- this.queue = [];
269
- this.retryQueue = [];
270
-
271
- await super.shutdown();
272
- }
273
-
274
- getInfo() {
275
- return {
276
- name: this.name,
277
- mode: 'asynchronous',
278
- description: 'In-memory queue with background processing',
279
- config: {
280
- batchSize: this.batchSize,
281
- concurrency: this.concurrency,
282
- flushInterval: this.flushInterval,
283
- maxQueueSize: this.maxQueueSize,
284
- maxRetries: this.maxRetries
285
- },
286
- stats: this.getStats()
287
- };
288
- }
289
- }