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
@@ -1,337 +0,0 @@
1
- import { BasePartitionDriver } from './base-partition-driver.js';
2
- import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
3
- import { PartitionDriverError } from '../errors.js';
4
-
5
- /**
6
- * SQS-based partition driver for distributed processing
7
- * Sends partition operations to SQS for processing by workers
8
- * Ideal for high-volume, distributed systems
9
- */
10
- export class SQSPartitionDriver extends BasePartitionDriver {
11
- constructor(options = {}) {
12
- super(options);
13
- this.name = 'sqs';
14
-
15
- // SQS Configuration
16
- this.queueUrl = options.queueUrl;
17
- if (!this.queueUrl) {
18
- throw new PartitionDriverError('SQS queue URL is required', {
19
- driver: 'sqs',
20
- operation: 'constructor',
21
- suggestion: 'Provide queueUrl in options: new SQSPartitionDriver({ queueUrl: "https://sqs.region.amazonaws.com/account/queue" })'
22
- });
23
- }
24
-
25
- this.region = options.region || 'us-east-1';
26
- this.credentials = options.credentials;
27
- this.dlqUrl = options.dlqUrl; // Dead Letter Queue
28
- this.messageGroupId = options.messageGroupId || 's3db-partitions';
29
- this.visibilityTimeout = options.visibilityTimeout || 300; // 5 minutes
30
- this.batchSize = options.batchSize || 10; // SQS max batch size
31
-
32
- // Worker configuration
33
- this.isWorker = options.isWorker || false;
34
- this.workerConcurrency = options.workerConcurrency || 5;
35
- this.pollInterval = options.pollInterval || 1000;
36
-
37
- // Initialize SQS client
38
- this.sqsClient = new SQSClient({
39
- region: this.region,
40
- credentials: this.credentials
41
- });
42
-
43
- this.workerRunning = false;
44
- this.messageBuffer = [];
45
- }
46
-
47
- async initialize() {
48
- // Start worker if configured
49
- if (this.isWorker) {
50
- await this.startWorker();
51
- }
52
- }
53
-
54
- /**
55
- * Send partition operation to SQS
56
- */
57
- async queue(operation) {
58
- try {
59
- // Prepare message
60
- const message = {
61
- id: `${Date.now()}-${Math.random()}`,
62
- timestamp: new Date().toISOString(),
63
- operation: {
64
- type: operation.type,
65
- resourceName: operation.resource.name,
66
- data: this.serializeData(operation.data)
67
- }
68
- };
69
-
70
- // Buffer messages for batch sending
71
- this.messageBuffer.push(message);
72
- this.stats.queued++;
73
-
74
- // Send batch when buffer is full
75
- if (this.messageBuffer.length >= this.batchSize) {
76
- await this.flushMessages();
77
- } else {
78
- // Schedule flush if not already scheduled
79
- if (!this.flushTimeout) {
80
- this.flushTimeout = setTimeout(() => this.flushMessages(), 100);
81
- }
82
- }
83
-
84
- return {
85
- success: true,
86
- driver: 'sqs',
87
- messageId: message.id,
88
- queueUrl: this.queueUrl
89
- };
90
-
91
- } catch (error) {
92
- this.emit('error', { operation, error });
93
- throw error;
94
- }
95
- }
96
-
97
- /**
98
- * Flush buffered messages to SQS
99
- */
100
- async flushMessages() {
101
- if (this.messageBuffer.length === 0) return;
102
-
103
- clearTimeout(this.flushTimeout);
104
- this.flushTimeout = null;
105
-
106
- const messages = this.messageBuffer.splice(0, this.batchSize);
107
-
108
- try {
109
- // For FIFO queues, add deduplication ID
110
- const isFifo = this.queueUrl.includes('.fifo');
111
-
112
- for (const message of messages) {
113
- const params = {
114
- QueueUrl: this.queueUrl,
115
- MessageBody: JSON.stringify(message),
116
- MessageAttributes: {
117
- Type: {
118
- DataType: 'String',
119
- StringValue: message.operation.type
120
- },
121
- Resource: {
122
- DataType: 'String',
123
- StringValue: message.operation.resourceName
124
- }
125
- }
126
- };
127
-
128
- if (isFifo) {
129
- params.MessageGroupId = this.messageGroupId;
130
- params.MessageDeduplicationId = message.id;
131
- }
132
-
133
- await this.sqsClient.send(new SendMessageCommand(params));
134
- }
135
-
136
- this.emit('messagesSent', { count: messages.length });
137
-
138
- } catch (error) {
139
- // Return messages to buffer for retry
140
- this.messageBuffer.unshift(...messages);
141
- this.emit('sendError', { error, messages: messages.length });
142
- throw error;
143
- }
144
- }
145
-
146
- /**
147
- * Start SQS worker to process messages
148
- */
149
- async startWorker() {
150
- if (this.workerRunning) return;
151
-
152
- this.workerRunning = true;
153
- this.emit('workerStarted', { concurrency: this.workerConcurrency });
154
-
155
- // Start multiple concurrent workers
156
- for (let i = 0; i < this.workerConcurrency; i++) {
157
- this.pollMessages(i);
158
- }
159
- }
160
-
161
- /**
162
- * Poll SQS for messages
163
- */
164
- async pollMessages(workerId) {
165
- while (this.workerRunning) {
166
- try {
167
- // Receive messages from SQS
168
- const params = {
169
- QueueUrl: this.queueUrl,
170
- MaxNumberOfMessages: 10,
171
- WaitTimeSeconds: 20, // Long polling
172
- VisibilityTimeout: this.visibilityTimeout,
173
- MessageAttributeNames: ['All']
174
- };
175
-
176
- const response = await this.sqsClient.send(new ReceiveMessageCommand(params));
177
-
178
- if (response.Messages && response.Messages.length > 0) {
179
- // Process messages
180
- for (const message of response.Messages) {
181
- await this.processMessage(message, workerId);
182
- }
183
- }
184
-
185
- } catch (error) {
186
- this.emit('pollError', { workerId, error });
187
- // Wait before retrying
188
- await new Promise(resolve => setTimeout(resolve, this.pollInterval));
189
- }
190
- }
191
- }
192
-
193
- /**
194
- * Process a single SQS message
195
- */
196
- async processMessage(message, workerId) {
197
- try {
198
- // Parse message body
199
- const data = JSON.parse(message.Body);
200
- const operation = {
201
- type: data.operation.type,
202
- data: this.deserializeData(data.operation.data)
203
- };
204
-
205
- // Process the partition operation
206
- // Note: We need the actual resource instance to process
207
- // This would typically be handled by a separate worker service
208
- this.emit('processingMessage', { workerId, messageId: message.MessageId });
209
-
210
- // In a real implementation, you'd look up the resource and process:
211
- // await this.processOperation(operation);
212
-
213
- // Delete message from queue after successful processing
214
- await this.sqsClient.send(new DeleteMessageCommand({
215
- QueueUrl: this.queueUrl,
216
- ReceiptHandle: message.ReceiptHandle
217
- }));
218
-
219
- this.stats.processed++;
220
- this.emit('messageProcessed', { workerId, messageId: message.MessageId });
221
-
222
- } catch (error) {
223
- this.stats.failed++;
224
- this.emit('processError', { workerId, error, messageId: message.MessageId });
225
-
226
- // Message will become visible again after VisibilityTimeout
227
- // and eventually move to DLQ if configured
228
- }
229
- }
230
-
231
- /**
232
- * Serialize data for SQS transport
233
- */
234
- serializeData(data) {
235
- // Remove circular references and functions
236
- return JSON.parse(JSON.stringify(data, (key, value) => {
237
- if (typeof value === 'function') return undefined;
238
- if (value instanceof Buffer) return value.toString('base64');
239
- return value;
240
- }));
241
- }
242
-
243
- /**
244
- * Deserialize data from SQS
245
- */
246
- deserializeData(data) {
247
- return data;
248
- }
249
-
250
- /**
251
- * Stop the worker
252
- */
253
- async stopWorker() {
254
- this.workerRunning = false;
255
- this.emit('workerStopped');
256
- }
257
-
258
- /**
259
- * Force flush all pending messages
260
- */
261
- async flush() {
262
- await this.flushMessages();
263
- }
264
-
265
- /**
266
- * Get queue metrics from SQS
267
- */
268
- async getQueueMetrics() {
269
- try {
270
- const { Attributes } = await this.sqsClient.send(new GetQueueAttributesCommand({
271
- QueueUrl: this.queueUrl,
272
- AttributeNames: [
273
- 'ApproximateNumberOfMessages',
274
- 'ApproximateNumberOfMessagesNotVisible',
275
- 'ApproximateNumberOfMessagesDelayed'
276
- ]
277
- }));
278
-
279
- return {
280
- messagesAvailable: parseInt(Attributes.ApproximateNumberOfMessages || 0),
281
- messagesInFlight: parseInt(Attributes.ApproximateNumberOfMessagesNotVisible || 0),
282
- messagesDelayed: parseInt(Attributes.ApproximateNumberOfMessagesDelayed || 0)
283
- };
284
- } catch (error) {
285
- return null;
286
- }
287
- }
288
-
289
- /**
290
- * Get detailed statistics
291
- */
292
- async getStats() {
293
- const baseStats = super.getStats();
294
- const queueMetrics = await this.getQueueMetrics();
295
-
296
- return {
297
- ...baseStats,
298
- bufferLength: this.messageBuffer.length,
299
- isWorker: this.isWorker,
300
- workerRunning: this.workerRunning,
301
- queue: queueMetrics
302
- };
303
- }
304
-
305
- /**
306
- * Shutdown the driver
307
- */
308
- async shutdown() {
309
- // Stop worker if running
310
- await this.stopWorker();
311
-
312
- // Flush remaining messages
313
- await this.flush();
314
-
315
- // Clear buffer
316
- this.messageBuffer = [];
317
-
318
- await super.shutdown();
319
- }
320
-
321
- getInfo() {
322
- return {
323
- name: this.name,
324
- mode: 'distributed',
325
- description: 'SQS-based queue for distributed partition processing',
326
- config: {
327
- queueUrl: this.queueUrl,
328
- region: this.region,
329
- dlqUrl: this.dlqUrl,
330
- isWorker: this.isWorker,
331
- workerConcurrency: this.workerConcurrency,
332
- visibilityTimeout: this.visibilityTimeout
333
- },
334
- stats: this.getStats()
335
- };
336
- }
337
- }
@@ -1,38 +0,0 @@
1
- import { BasePartitionDriver } from './base-partition-driver.js';
2
-
3
- /**
4
- * Synchronous partition driver
5
- * Creates partitions immediately during insert/update/delete
6
- * Use this when data consistency is critical
7
- */
8
- export class SyncPartitionDriver extends BasePartitionDriver {
9
- constructor(options = {}) {
10
- super(options);
11
- this.name = 'sync';
12
- }
13
-
14
- /**
15
- * Process partition operations synchronously
16
- */
17
- async queue(operation) {
18
- this.stats.queued++;
19
-
20
- try {
21
- // Process immediately and wait for completion
22
- await this.processOperation(operation);
23
- return { success: true, driver: 'sync' };
24
- } catch (error) {
25
- // Re-throw to make the main operation fail
26
- throw error;
27
- }
28
- }
29
-
30
- getInfo() {
31
- return {
32
- name: this.name,
33
- mode: 'synchronous',
34
- description: 'Processes partitions immediately, blocking the main operation',
35
- stats: this.getStats()
36
- };
37
- }
38
- }