s3db.js 8.1.1 → 8.2.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.
- package/bin/cli.js +430 -0
- package/dist/s3db.cjs.js +49 -24
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +49 -24
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +49 -24
- package/dist/s3db.iife.min.js +1 -1
- package/mcp/server.js +2 -1
- package/package.json +6 -2
- package/src/database.class.js +17 -4
- package/src/plugins/audit.plugin.js +43 -22
package/mcp/server.js
CHANGED
|
@@ -4,7 +4,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
5
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
6
6
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
-
import { S3db, CachePlugin, CostsPlugin
|
|
7
|
+
import { S3db, CachePlugin, CostsPlugin } from '../dist/s3db.es.js';
|
|
8
|
+
import { FilesystemCache } from '../src/plugins/cache/filesystem-cache.class.js';
|
|
8
9
|
import { config } from 'dotenv';
|
|
9
10
|
import { fileURLToPath } from 'url';
|
|
10
11
|
import { dirname, join } from 'path';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s3db.js",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
|
|
5
5
|
"main": "dist/s3db.cjs.js",
|
|
6
6
|
"module": "dist/s3db.es.js",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"author": "@stone/martech",
|
|
12
12
|
"license": "UNLICENSED",
|
|
13
13
|
"bin": {
|
|
14
|
+
"s3db.js": "./bin/cli.js",
|
|
15
|
+
"s3db": "./bin/cli.js",
|
|
14
16
|
"s3db-mcp": "./mcp/server.js"
|
|
15
17
|
},
|
|
16
18
|
"repository": {
|
|
@@ -51,8 +53,11 @@
|
|
|
51
53
|
],
|
|
52
54
|
"dependencies": {
|
|
53
55
|
"@aws-sdk/client-s3": "^3.850.0",
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.17.3",
|
|
54
57
|
"@smithy/node-http-handler": "^4.1.0",
|
|
55
58
|
"@supercharge/promise-pool": "^3.2.0",
|
|
59
|
+
"commander": "^12.1.0",
|
|
60
|
+
"dotenv": "^17.2.1",
|
|
56
61
|
"fastest-validator": "^1.19.1",
|
|
57
62
|
"flat": "^6.0.1",
|
|
58
63
|
"json-stable-stringify": "^1.3.0",
|
|
@@ -90,7 +95,6 @@
|
|
|
90
95
|
"@rollup/plugin-replace": "^6.0.2",
|
|
91
96
|
"@rollup/plugin-terser": "^0.4.4",
|
|
92
97
|
"@types/node": "24.1.0",
|
|
93
|
-
"dotenv": "^17.2.1",
|
|
94
98
|
"jest": "^30.0.5",
|
|
95
99
|
"rollup": "^4.46.1",
|
|
96
100
|
"rollup-plugin-copy": "^3.5.0",
|
package/src/database.class.js
CHANGED
|
@@ -28,7 +28,8 @@ export class Database extends EventEmitter {
|
|
|
28
28
|
this.options = options;
|
|
29
29
|
this.verbose = options.verbose || false;
|
|
30
30
|
this.parallelism = parseInt(options.parallelism + "") || 10;
|
|
31
|
-
this.plugins = options.plugins || []; //
|
|
31
|
+
this.plugins = options.plugins || []; // Keep the original array for backward compatibility
|
|
32
|
+
this.pluginRegistry = {}; // Initialize plugins registry as separate object
|
|
32
33
|
this.pluginList = options.plugins || []; // Keep the list for backward compatibility
|
|
33
34
|
this.cache = options.cache;
|
|
34
35
|
this.passphrase = options.passphrase || "secret";
|
|
@@ -390,9 +391,13 @@ export class Database extends EventEmitter {
|
|
|
390
391
|
|
|
391
392
|
const setupProms = plugins.map(async (plugin) => {
|
|
392
393
|
if (plugin.beforeSetup) await plugin.beforeSetup()
|
|
393
|
-
|
|
394
|
+
await plugin.setup(db)
|
|
394
395
|
if (plugin.afterSetup) await plugin.afterSetup()
|
|
395
|
-
|
|
396
|
+
|
|
397
|
+
// Register the plugin using the same naming convention as usePlugin()
|
|
398
|
+
const pluginName = this._getPluginName(plugin);
|
|
399
|
+
this.pluginRegistry[pluginName] = plugin;
|
|
400
|
+
});
|
|
396
401
|
|
|
397
402
|
await Promise.all(setupProms);
|
|
398
403
|
|
|
@@ -411,8 +416,16 @@ export class Database extends EventEmitter {
|
|
|
411
416
|
* @param {Plugin} plugin - Plugin instance to register
|
|
412
417
|
* @param {string} [name] - Optional name for the plugin (defaults to plugin.constructor.name)
|
|
413
418
|
*/
|
|
419
|
+
/**
|
|
420
|
+
* Get the normalized plugin name
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
_getPluginName(plugin, customName = null) {
|
|
424
|
+
return customName || plugin.constructor.name.replace('Plugin', '').toLowerCase();
|
|
425
|
+
}
|
|
426
|
+
|
|
414
427
|
async usePlugin(plugin, name = null) {
|
|
415
|
-
const pluginName =
|
|
428
|
+
const pluginName = this._getPluginName(plugin, name);
|
|
416
429
|
|
|
417
430
|
// Register the plugin
|
|
418
431
|
this.plugins[pluginName] = plugin;
|
|
@@ -119,17 +119,13 @@ export class AuditPlugin extends Plugin {
|
|
|
119
119
|
resource.deleteMany = async function(ids) {
|
|
120
120
|
// Fetch all objects before deletion for audit logging
|
|
121
121
|
const objectsToDelete = [];
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
objectsToDelete.push({ id }); // Just store the ID if we can't fetch
|
|
129
|
-
}
|
|
122
|
+
for (const id of ids) {
|
|
123
|
+
const [ok, err, fetched] = await tryFn(() => resource.get(id));
|
|
124
|
+
if (ok) {
|
|
125
|
+
objectsToDelete.push(fetched);
|
|
126
|
+
} else {
|
|
127
|
+
objectsToDelete.push({ id }); // Just store the ID if we can't fetch
|
|
130
128
|
}
|
|
131
|
-
} else {
|
|
132
|
-
objectsToDelete.push(...ids.map(id => ({ id })));
|
|
133
129
|
}
|
|
134
130
|
|
|
135
131
|
// Perform the actual deletion
|
|
@@ -260,21 +256,46 @@ export class AuditPlugin extends Plugin {
|
|
|
260
256
|
|
|
261
257
|
const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100, offset = 0 } = options;
|
|
262
258
|
|
|
263
|
-
|
|
259
|
+
// If we have specific filters, we need to fetch more items to ensure proper pagination after filtering
|
|
260
|
+
const hasFilters = resourceName || operation || recordId || partition || startDate || endDate;
|
|
264
261
|
|
|
265
|
-
|
|
266
|
-
if (operation) query.operation = operation;
|
|
267
|
-
if (recordId) query.recordId = recordId;
|
|
268
|
-
if (partition) query.partition = partition;
|
|
262
|
+
let items = [];
|
|
269
263
|
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
264
|
+
if (hasFilters) {
|
|
265
|
+
// Fetch enough items to handle filtering
|
|
266
|
+
const fetchSize = Math.min(10000, Math.max(1000, (limit + offset) * 20));
|
|
267
|
+
const result = await this.auditResource.list({ limit: fetchSize });
|
|
268
|
+
items = result || [];
|
|
269
|
+
|
|
270
|
+
// Apply filters
|
|
271
|
+
if (resourceName) {
|
|
272
|
+
items = items.filter(log => log.resourceName === resourceName);
|
|
273
|
+
}
|
|
274
|
+
if (operation) {
|
|
275
|
+
items = items.filter(log => log.operation === operation);
|
|
276
|
+
}
|
|
277
|
+
if (recordId) {
|
|
278
|
+
items = items.filter(log => log.recordId === recordId);
|
|
279
|
+
}
|
|
280
|
+
if (partition) {
|
|
281
|
+
items = items.filter(log => log.partition === partition);
|
|
282
|
+
}
|
|
283
|
+
if (startDate || endDate) {
|
|
284
|
+
items = items.filter(log => {
|
|
285
|
+
const timestamp = new Date(log.timestamp);
|
|
286
|
+
if (startDate && timestamp < new Date(startDate)) return false;
|
|
287
|
+
if (endDate && timestamp > new Date(endDate)) return false;
|
|
288
|
+
return true;
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Apply offset and limit after filtering
|
|
293
|
+
return items.slice(offset, offset + limit);
|
|
294
|
+
} else {
|
|
295
|
+
// No filters, use direct pagination
|
|
296
|
+
const result = await this.auditResource.page({ size: limit, offset });
|
|
297
|
+
return result.items || [];
|
|
274
298
|
}
|
|
275
|
-
|
|
276
|
-
const result = await this.auditResource.page({ query, limit, offset });
|
|
277
|
-
return result.items || [];
|
|
278
299
|
}
|
|
279
300
|
|
|
280
301
|
async getRecordHistory(resourceName, recordId) {
|