s3db.js 11.3.2 → 12.0.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 (83) hide show
  1. package/README.md +102 -8
  2. package/dist/s3db.cjs.js +36945 -15510
  3. package/dist/s3db.cjs.js.map +1 -1
  4. package/dist/s3db.d.ts +66 -1
  5. package/dist/s3db.es.js +36914 -15534
  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 +35 -15
  11. package/src/behaviors/user-managed.js +13 -6
  12. package/src/client.class.js +79 -49
  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 +97 -47
  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 +544 -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 +354 -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/replicator.plugin.js +2 -1
  55. package/src/plugins/replicators/bigquery-replicator.class.js +180 -8
  56. package/src/plugins/replicators/dynamodb-replicator.class.js +383 -0
  57. package/src/plugins/replicators/index.js +28 -3
  58. package/src/plugins/replicators/mongodb-replicator.class.js +391 -0
  59. package/src/plugins/replicators/mysql-replicator.class.js +558 -0
  60. package/src/plugins/replicators/planetscale-replicator.class.js +409 -0
  61. package/src/plugins/replicators/postgres-replicator.class.js +182 -7
  62. package/src/plugins/replicators/s3db-replicator.class.js +1 -12
  63. package/src/plugins/replicators/schema-sync.helper.js +601 -0
  64. package/src/plugins/replicators/sqs-replicator.class.js +11 -9
  65. package/src/plugins/replicators/turso-replicator.class.js +416 -0
  66. package/src/plugins/replicators/webhook-replicator.class.js +612 -0
  67. package/src/plugins/state-machine.plugin.js +122 -68
  68. package/src/plugins/tfstate/README.md +745 -0
  69. package/src/plugins/tfstate/base-driver.js +80 -0
  70. package/src/plugins/tfstate/errors.js +112 -0
  71. package/src/plugins/tfstate/filesystem-driver.js +129 -0
  72. package/src/plugins/tfstate/index.js +2660 -0
  73. package/src/plugins/tfstate/s3-driver.js +192 -0
  74. package/src/plugins/ttl.plugin.js +536 -0
  75. package/src/resource.class.js +315 -36
  76. package/src/s3db.d.ts +66 -1
  77. package/src/schema.class.js +366 -32
  78. package/SECURITY.md +0 -76
  79. package/src/partition-drivers/base-partition-driver.js +0 -106
  80. package/src/partition-drivers/index.js +0 -66
  81. package/src/partition-drivers/memory-partition-driver.js +0 -289
  82. package/src/partition-drivers/sqs-partition-driver.js +0 -337
  83. package/src/partition-drivers/sync-partition-driver.js +0 -38
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Base Driver Class for TfState Plugin
3
+ *
4
+ * All tfstate drivers must extend this class and implement the required methods.
5
+ */
6
+ export class TfStateDriver {
7
+ constructor(config = {}) {
8
+ this.config = config;
9
+ this.selector = config.selector || '**/*.tfstate';
10
+ }
11
+
12
+ /**
13
+ * Initialize the driver
14
+ * Called during plugin installation
15
+ */
16
+ async initialize() {
17
+ throw new Error('Driver must implement initialize()');
18
+ }
19
+
20
+ /**
21
+ * List all state files matching the selector
22
+ * @returns {Promise<Array>} Array of state file metadata { path, lastModified, size }
23
+ */
24
+ async listStateFiles() {
25
+ throw new Error('Driver must implement listStateFiles()');
26
+ }
27
+
28
+ /**
29
+ * Read a state file content
30
+ * @param {string} path - Path to the state file
31
+ * @returns {Promise<Object>} Parsed state file content
32
+ */
33
+ async readStateFile(path) {
34
+ throw new Error('Driver must implement readStateFile()');
35
+ }
36
+
37
+ /**
38
+ * Get state file metadata
39
+ * @param {string} path - Path to the state file
40
+ * @returns {Promise<Object>} Metadata { path, lastModified, size, etag }
41
+ */
42
+ async getStateFileMetadata(path) {
43
+ throw new Error('Driver must implement getStateFileMetadata()');
44
+ }
45
+
46
+ /**
47
+ * Check if a state file has been modified since last check
48
+ * @param {string} path - Path to the state file
49
+ * @param {Date} since - Check modifications since this date
50
+ * @returns {Promise<boolean>} True if modified
51
+ */
52
+ async hasBeenModified(path, since) {
53
+ const metadata = await this.getStateFileMetadata(path);
54
+ return new Date(metadata.lastModified) > new Date(since);
55
+ }
56
+
57
+ /**
58
+ * Match a path against the selector pattern
59
+ * @param {string} path - Path to check
60
+ * @returns {boolean} True if matches
61
+ */
62
+ matchesSelector(path) {
63
+ const pattern = this.selector
64
+ .replace(/\*\*/g, '__DOUBLE_STAR__')
65
+ .replace(/\*/g, '[^/]*')
66
+ .replace(/__DOUBLE_STAR__/g, '.*')
67
+ .replace(/\?/g, '.')
68
+ .replace(/\[([^\]]+)\]/g, '[$1]');
69
+
70
+ const regex = new RegExp(`^${pattern}$`);
71
+ return regex.test(path);
72
+ }
73
+
74
+ /**
75
+ * Close/cleanup driver resources
76
+ */
77
+ async close() {
78
+ // Optional cleanup, override if needed
79
+ }
80
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * TfStatePlugin Error Classes
3
+ * Custom errors for Terraform/OpenTofu state operations
4
+ */
5
+
6
+ /**
7
+ * Base error for all Terraform/OpenTofu state operations
8
+ */
9
+ export class TfStateError extends Error {
10
+ constructor(message, context = {}) {
11
+ super(message);
12
+ this.name = 'TfStateError';
13
+ this.context = context;
14
+ Error.captureStackTrace(this, this.constructor);
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Thrown when state file is invalid or corrupted
20
+ */
21
+ export class InvalidStateFileError extends TfStateError {
22
+ constructor(filePath, reason, context = {}) {
23
+ super(`Invalid Terraform state file "${filePath}": ${reason}`, context);
24
+ this.name = 'InvalidStateFileError';
25
+ this.filePath = filePath;
26
+ this.reason = reason;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Thrown when state file version is not supported
32
+ */
33
+ export class UnsupportedStateVersionError extends TfStateError {
34
+ constructor(version, supportedVersions, context = {}) {
35
+ super(
36
+ `Terraform state version ${version} is not supported. Supported versions: ${supportedVersions.join(', ')}`,
37
+ context
38
+ );
39
+ this.name = 'UnsupportedStateVersionError';
40
+ this.version = version;
41
+ this.supportedVersions = supportedVersions;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Thrown when state file cannot be read
47
+ */
48
+ export class StateFileNotFoundError extends TfStateError {
49
+ constructor(filePath, context = {}) {
50
+ super(`Terraform state file not found: ${filePath}`, context);
51
+ this.name = 'StateFileNotFoundError';
52
+ this.filePath = filePath;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Thrown when resource extraction fails
58
+ */
59
+ export class ResourceExtractionError extends TfStateError {
60
+ constructor(resourceAddress, originalError, context = {}) {
61
+ super(
62
+ `Failed to extract resource "${resourceAddress}": ${originalError.message}`,
63
+ context
64
+ );
65
+ this.name = 'ResourceExtractionError';
66
+ this.resourceAddress = resourceAddress;
67
+ this.originalError = originalError;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Thrown when state diff calculation fails
73
+ */
74
+ export class StateDiffError extends TfStateError {
75
+ constructor(oldSerial, newSerial, originalError, context = {}) {
76
+ super(
77
+ `Failed to calculate diff between state serials ${oldSerial} and ${newSerial}: ${originalError.message}`,
78
+ context
79
+ );
80
+ this.name = 'StateDiffError';
81
+ this.oldSerial = oldSerial;
82
+ this.newSerial = newSerial;
83
+ this.originalError = originalError;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Thrown when file watching setup fails
89
+ */
90
+ export class FileWatchError extends TfStateError {
91
+ constructor(path, originalError, context = {}) {
92
+ super(`Failed to watch path "${path}": ${originalError.message}`, context);
93
+ this.name = 'FileWatchError';
94
+ this.path = path;
95
+ this.originalError = originalError;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Thrown when resource filtering fails
101
+ */
102
+ export class ResourceFilterError extends TfStateError {
103
+ constructor(filterExpression, originalError, context = {}) {
104
+ super(
105
+ `Failed to apply resource filter "${filterExpression}": ${originalError.message}`,
106
+ context
107
+ );
108
+ this.name = 'ResourceFilterError';
109
+ this.filterExpression = filterExpression;
110
+ this.originalError = originalError;
111
+ }
112
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Filesystem Driver for TfState Plugin
3
+ *
4
+ * Reads Terraform/OpenTofu state files from local filesystem
5
+ * Useful for development and testing
6
+ */
7
+ import { TfStateDriver } from './base-driver.js';
8
+ import { readFile, stat } from 'fs/promises';
9
+ import { join, relative } from 'path';
10
+ import { glob } from 'glob';
11
+
12
+ export class FilesystemTfStateDriver extends TfStateDriver {
13
+ constructor(config = {}) {
14
+ super(config);
15
+ this.basePath = config.basePath || config.path || process.cwd();
16
+ }
17
+
18
+ /**
19
+ * Initialize filesystem driver
20
+ */
21
+ async initialize() {
22
+ // Verify base path exists
23
+ try {
24
+ const stats = await stat(this.basePath);
25
+ if (!stats.isDirectory()) {
26
+ throw new Error(`Base path is not a directory: ${this.basePath}`);
27
+ }
28
+ } catch (error) {
29
+ throw new Error(`Invalid base path: ${this.basePath} - ${error.message}`);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * List all state files matching the selector
35
+ */
36
+ async listStateFiles() {
37
+ const pattern = join(this.basePath, this.selector);
38
+
39
+ try {
40
+ const files = await glob(pattern, {
41
+ nodir: true,
42
+ absolute: false,
43
+ cwd: this.basePath
44
+ });
45
+
46
+ const stateFiles = await Promise.all(
47
+ files.map(async (file) => {
48
+ const fullPath = join(this.basePath, file);
49
+ const stats = await stat(fullPath);
50
+
51
+ return {
52
+ path: file,
53
+ fullPath,
54
+ lastModified: stats.mtime,
55
+ size: stats.size,
56
+ etag: `${stats.mtime.getTime()}-${stats.size}` // Pseudo-etag
57
+ };
58
+ })
59
+ );
60
+
61
+ return stateFiles;
62
+ } catch (error) {
63
+ throw new Error(`Failed to list state files: ${error.message}`);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Read a state file from filesystem
69
+ */
70
+ async readStateFile(path) {
71
+ const fullPath = path.startsWith(this.basePath)
72
+ ? path
73
+ : join(this.basePath, path);
74
+
75
+ try {
76
+ const content = await readFile(fullPath, 'utf-8');
77
+ return JSON.parse(content);
78
+ } catch (error) {
79
+ if (error.code === 'ENOENT') {
80
+ throw new Error(`State file not found: ${path}`);
81
+ }
82
+ throw new Error(`Failed to read state file ${path}: ${error.message}`);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Get state file metadata from filesystem
88
+ */
89
+ async getStateFileMetadata(path) {
90
+ const fullPath = path.startsWith(this.basePath)
91
+ ? path
92
+ : join(this.basePath, path);
93
+
94
+ try {
95
+ const stats = await stat(fullPath);
96
+
97
+ return {
98
+ path,
99
+ fullPath,
100
+ lastModified: stats.mtime,
101
+ size: stats.size,
102
+ etag: `${stats.mtime.getTime()}-${stats.size}`
103
+ };
104
+ } catch (error) {
105
+ if (error.code === 'ENOENT') {
106
+ throw new Error(`State file not found: ${path}`);
107
+ }
108
+ throw new Error(`Failed to get metadata for ${path}: ${error.message}`);
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Check if state file has been modified
114
+ */
115
+ async hasBeenModified(path, since) {
116
+ const metadata = await this.getStateFileMetadata(path);
117
+ const lastModified = new Date(metadata.lastModified);
118
+ const sinceDate = new Date(since);
119
+
120
+ return lastModified > sinceDate;
121
+ }
122
+
123
+ /**
124
+ * Close filesystem driver (no-op)
125
+ */
126
+ async close() {
127
+ // Nothing to close for filesystem
128
+ }
129
+ }