s3db.js 8.1.3 → 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 +41 -25
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +41 -25
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +41 -25
- package/dist/s3db.iife.min.js +1 -1
- package/mcp/server.js +2 -1
- package/package.json +6 -2
- package/src/database.class.js +3 -2
- 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 =
|
|
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";
|
|
@@ -395,7 +396,7 @@ export class Database extends EventEmitter {
|
|
|
395
396
|
|
|
396
397
|
// Register the plugin using the same naming convention as usePlugin()
|
|
397
398
|
const pluginName = this._getPluginName(plugin);
|
|
398
|
-
this.
|
|
399
|
+
this.pluginRegistry[pluginName] = plugin;
|
|
399
400
|
});
|
|
400
401
|
|
|
401
402
|
await Promise.all(setupProms);
|
|
@@ -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) {
|