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/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, FilesystemCache } from 's3db.js';
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.1.3",
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",
@@ -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 = {}; // Initialize plugins registry
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.plugins[pluginName] = plugin;
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
- if (plugin.config.includeData) {
123
- for (const id of ids) {
124
- const [ok, err, fetched] = await tryFn(() => resource.get(id));
125
- if (ok) {
126
- objectsToDelete.push(fetched);
127
- } else {
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
- let query = {};
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
- if (resourceName) query.resourceName = resourceName;
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 (startDate || endDate) {
271
- query.timestamp = {};
272
- if (startDate) query.timestamp.$gte = startDate;
273
- if (endDate) query.timestamp.$lte = endDate;
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) {