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,112 @@
1
+ /**
2
+ * Authentication Factory - Create authentication middleware based on configuration
3
+ *
4
+ * Provides unified interface for multiple authentication methods
5
+ */
6
+
7
+ import { jwtAuth } from './jwt-auth.js';
8
+ import { apiKeyAuth } from './api-key-auth.js';
9
+ import { basicAuth } from './basic-auth.js';
10
+ import { unauthorized } from '../utils/response-formatter.js';
11
+
12
+ /**
13
+ * Create authentication middleware that supports multiple auth methods
14
+ * @param {Object} options - Authentication options
15
+ * @param {Array<string>} options.methods - Allowed auth methods (['jwt', 'apiKey', 'basic'])
16
+ * @param {Object} options.jwt - JWT configuration
17
+ * @param {Object} options.apiKey - API Key configuration
18
+ * @param {Object} options.basic - Basic Auth configuration
19
+ * @param {Object} options.usersResource - Users resource
20
+ * @param {boolean} options.optional - If true, allows requests without auth
21
+ * @returns {Function} Hono middleware
22
+ */
23
+ export function createAuthMiddleware(options = {}) {
24
+ const {
25
+ methods = [],
26
+ jwt: jwtConfig = {},
27
+ apiKey: apiKeyConfig = {},
28
+ basic: basicConfig = {},
29
+ usersResource,
30
+ optional = false
31
+ } = options;
32
+
33
+ // If no methods specified, allow all requests
34
+ if (methods.length === 0) {
35
+ return async (c, next) => await next();
36
+ }
37
+
38
+ // Create individual auth middlewares
39
+ const middlewares = [];
40
+
41
+ if (methods.includes('jwt') && jwtConfig.secret) {
42
+ middlewares.push({
43
+ name: 'jwt',
44
+ middleware: jwtAuth({
45
+ secret: jwtConfig.secret,
46
+ usersResource,
47
+ optional: true // Check all methods before rejecting
48
+ })
49
+ });
50
+ }
51
+
52
+ if (methods.includes('apiKey') && usersResource) {
53
+ middlewares.push({
54
+ name: 'apiKey',
55
+ middleware: apiKeyAuth({
56
+ headerName: apiKeyConfig.headerName || 'X-API-Key',
57
+ usersResource,
58
+ optional: true // Check all methods before rejecting
59
+ })
60
+ });
61
+ }
62
+
63
+ if (methods.includes('basic') && usersResource) {
64
+ middlewares.push({
65
+ name: 'basic',
66
+ middleware: basicAuth({
67
+ realm: basicConfig.realm || 'API Access',
68
+ usersResource,
69
+ passphrase: basicConfig.passphrase || 'secret',
70
+ optional: true // Check all methods before rejecting
71
+ })
72
+ });
73
+ }
74
+
75
+ // Return combined middleware
76
+ return async (c, next) => {
77
+ // Try each auth method
78
+ for (const { name, middleware } of middlewares) {
79
+ // Create a temporary next that captures success
80
+ let authSuccess = false;
81
+ const tempNext = async () => {
82
+ authSuccess = true;
83
+ };
84
+
85
+ // Try auth method
86
+ await middleware(c, tempNext);
87
+
88
+ // If auth succeeded, continue
89
+ if (authSuccess && c.get('user')) {
90
+ return await next();
91
+ }
92
+ }
93
+
94
+ // No auth method succeeded
95
+ if (optional) {
96
+ return await next();
97
+ }
98
+
99
+ // Require authentication
100
+ const response = unauthorized(
101
+ `Authentication required. Supported methods: ${methods.join(', ')}`
102
+ );
103
+ return c.json(response, response._status);
104
+ };
105
+ }
106
+
107
+ export default {
108
+ createAuthMiddleware,
109
+ jwtAuth,
110
+ apiKeyAuth,
111
+ basicAuth
112
+ };
@@ -0,0 +1,169 @@
1
+ /**
2
+ * JWT Authentication - JSON Web Token authentication middleware
3
+ *
4
+ * Provides stateless authentication using JWT tokens
5
+ */
6
+
7
+ import { createHash } from 'crypto';
8
+ import { unauthorized } from '../utils/response-formatter.js';
9
+
10
+ /**
11
+ * Create JWT token (simple implementation without external dependencies)
12
+ * Note: In production, use 'jsonwebtoken' package for better security
13
+ * @param {Object} payload - Token payload
14
+ * @param {string} secret - JWT secret
15
+ * @param {string} expiresIn - Token expiration (e.g., '7d')
16
+ * @returns {string} JWT token
17
+ */
18
+ export function createToken(payload, secret, expiresIn = '7d') {
19
+ // Parse expiresIn
20
+ const match = expiresIn.match(/^(\d+)([smhd])$/);
21
+ if (!match) {
22
+ throw new Error('Invalid expiresIn format. Use: 60s, 30m, 24h, 7d');
23
+ }
24
+
25
+ const [, value, unit] = match;
26
+ const multipliers = { s: 1, m: 60, h: 3600, d: 86400 };
27
+ const expiresInSeconds = parseInt(value) * multipliers[unit];
28
+
29
+ const header = { alg: 'HS256', typ: 'JWT' };
30
+ const now = Math.floor(Date.now() / 1000);
31
+
32
+ const data = {
33
+ ...payload,
34
+ iat: now,
35
+ exp: now + expiresInSeconds
36
+ };
37
+
38
+ // Encode
39
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
40
+ const encodedPayload = Buffer.from(JSON.stringify(data)).toString('base64url');
41
+
42
+ // Sign
43
+ const signature = createHash('sha256')
44
+ .update(`${encodedHeader}.${encodedPayload}.${secret}`)
45
+ .digest('base64url');
46
+
47
+ return `${encodedHeader}.${encodedPayload}.${signature}`;
48
+ }
49
+
50
+ /**
51
+ * Verify JWT token
52
+ * @param {string} token - JWT token
53
+ * @param {string} secret - JWT secret
54
+ * @returns {Object|null} Decoded payload or null if invalid
55
+ */
56
+ export function verifyToken(token, secret) {
57
+ try {
58
+ const [encodedHeader, encodedPayload, signature] = token.split('.');
59
+
60
+ if (!encodedHeader || !encodedPayload || !signature) {
61
+ return null;
62
+ }
63
+
64
+ // Verify signature
65
+ const expectedSignature = createHash('sha256')
66
+ .update(`${encodedHeader}.${encodedPayload}.${secret}`)
67
+ .digest('base64url');
68
+
69
+ if (signature !== expectedSignature) {
70
+ return null;
71
+ }
72
+
73
+ // Decode payload
74
+ const payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString());
75
+
76
+ // Check expiration
77
+ const now = Math.floor(Date.now() / 1000);
78
+ if (payload.exp && payload.exp < now) {
79
+ return null; // Expired
80
+ }
81
+
82
+ return payload;
83
+ } catch (err) {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Create JWT authentication middleware
90
+ * @param {Object} options - JWT options
91
+ * @param {string} options.secret - JWT secret key
92
+ * @param {Object} options.usersResource - Users resource for user lookup
93
+ * @param {boolean} options.optional - If true, allows requests without auth
94
+ * @returns {Function} Hono middleware
95
+ */
96
+ export function jwtAuth(options = {}) {
97
+ const { secret, usersResource, optional = false } = options;
98
+
99
+ if (!secret) {
100
+ throw new Error('JWT secret is required');
101
+ }
102
+
103
+ return async (c, next) => {
104
+ const authHeader = c.req.header('authorization');
105
+
106
+ if (!authHeader) {
107
+ if (optional) {
108
+ return await next();
109
+ }
110
+
111
+ const response = unauthorized('No authorization header provided');
112
+ return c.json(response, response._status);
113
+ }
114
+
115
+ // Extract token from "Bearer <token>"
116
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
117
+ if (!match) {
118
+ const response = unauthorized('Invalid authorization header format. Use: Bearer <token>');
119
+ return c.json(response, response._status);
120
+ }
121
+
122
+ const token = match[1];
123
+
124
+ // Verify token
125
+ const payload = verifyToken(token, secret);
126
+
127
+ if (!payload) {
128
+ const response = unauthorized('Invalid or expired token');
129
+ return c.json(response, response._status);
130
+ }
131
+
132
+ // Optionally load user from database
133
+ if (usersResource && payload.userId) {
134
+ try {
135
+ const user = await usersResource.get(payload.userId);
136
+
137
+ if (!user) {
138
+ const response = unauthorized('User not found');
139
+ return c.json(response, response._status);
140
+ }
141
+
142
+ if (!user.active) {
143
+ const response = unauthorized('User account is inactive');
144
+ return c.json(response, response._status);
145
+ }
146
+
147
+ // Store user in context
148
+ c.set('user', user);
149
+ c.set('authMethod', 'jwt');
150
+ } catch (err) {
151
+ console.error('[JWT Auth] Error loading user:', err);
152
+ const response = unauthorized('Authentication error');
153
+ return c.json(response, response._status);
154
+ }
155
+ } else {
156
+ // Store payload as user
157
+ c.set('user', payload);
158
+ c.set('authMethod', 'jwt');
159
+ }
160
+
161
+ await next();
162
+ };
163
+ }
164
+
165
+ export default {
166
+ createToken,
167
+ verifyToken,
168
+ jwtAuth
169
+ };