s3db.js 11.2.6 → 11.3.2

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.
@@ -0,0 +1,579 @@
1
+ /**
2
+ * CRUD Operations Tools
3
+ * Handles basic Create, Read, Update, Delete operations for resources
4
+ */
5
+
6
+ export const crudTools = [
7
+ {
8
+ name: 'resourceInsert',
9
+ description: 'Insert a new document into a resource',
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ resourceName: {
14
+ type: 'string',
15
+ description: 'Name of the resource'
16
+ },
17
+ data: {
18
+ type: 'object',
19
+ description: 'Data to insert'
20
+ }
21
+ },
22
+ required: ['resourceName', 'data']
23
+ }
24
+ },
25
+ {
26
+ name: 'resourceInsertMany',
27
+ description: 'Insert multiple documents into a resource',
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ resourceName: {
32
+ type: 'string',
33
+ description: 'Name of the resource'
34
+ },
35
+ data: {
36
+ type: 'array',
37
+ description: 'Array of documents to insert'
38
+ }
39
+ },
40
+ required: ['resourceName', 'data']
41
+ }
42
+ },
43
+ {
44
+ name: 'resourceGet',
45
+ description: 'Get a document by ID from a resource',
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ resourceName: {
50
+ type: 'string',
51
+ description: 'Name of the resource'
52
+ },
53
+ id: {
54
+ type: 'string',
55
+ description: 'Document ID'
56
+ },
57
+ partition: {
58
+ type: 'string',
59
+ description: 'Partition name for optimized retrieval'
60
+ },
61
+ partitionValues: {
62
+ type: 'object',
63
+ description: 'Partition values for targeted access'
64
+ }
65
+ },
66
+ required: ['resourceName', 'id']
67
+ }
68
+ },
69
+ {
70
+ name: 'resourceGetMany',
71
+ description: 'Get multiple documents by IDs from a resource',
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: {
75
+ resourceName: {
76
+ type: 'string',
77
+ description: 'Name of the resource'
78
+ },
79
+ ids: {
80
+ type: 'array',
81
+ items: { type: 'string' },
82
+ description: 'Array of document IDs'
83
+ }
84
+ },
85
+ required: ['resourceName', 'ids']
86
+ }
87
+ },
88
+ {
89
+ name: 'resourceUpdate',
90
+ description: 'Update a document in a resource',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ resourceName: {
95
+ type: 'string',
96
+ description: 'Name of the resource'
97
+ },
98
+ id: {
99
+ type: 'string',
100
+ description: 'Document ID'
101
+ },
102
+ data: {
103
+ type: 'object',
104
+ description: 'Data to update'
105
+ }
106
+ },
107
+ required: ['resourceName', 'id', 'data']
108
+ }
109
+ },
110
+ {
111
+ name: 'resourceUpsert',
112
+ description: 'Insert or update a document in a resource',
113
+ inputSchema: {
114
+ type: 'object',
115
+ properties: {
116
+ resourceName: {
117
+ type: 'string',
118
+ description: 'Name of the resource'
119
+ },
120
+ data: {
121
+ type: 'object',
122
+ description: 'Data to upsert (must include id if updating)'
123
+ }
124
+ },
125
+ required: ['resourceName', 'data']
126
+ }
127
+ },
128
+ {
129
+ name: 'resourceDelete',
130
+ description: 'Delete a document from a resource',
131
+ inputSchema: {
132
+ type: 'object',
133
+ properties: {
134
+ resourceName: {
135
+ type: 'string',
136
+ description: 'Name of the resource'
137
+ },
138
+ id: {
139
+ type: 'string',
140
+ description: 'Document ID'
141
+ }
142
+ },
143
+ required: ['resourceName', 'id']
144
+ }
145
+ },
146
+ {
147
+ name: 'resourceDeleteMany',
148
+ description: 'Delete multiple documents from a resource',
149
+ inputSchema: {
150
+ type: 'object',
151
+ properties: {
152
+ resourceName: {
153
+ type: 'string',
154
+ description: 'Name of the resource'
155
+ },
156
+ ids: {
157
+ type: 'array',
158
+ items: { type: 'string' },
159
+ description: 'Array of document IDs to delete'
160
+ }
161
+ },
162
+ required: ['resourceName', 'ids']
163
+ }
164
+ },
165
+ {
166
+ name: 'resourceExists',
167
+ description: 'Check if a document exists in a resource',
168
+ inputSchema: {
169
+ type: 'object',
170
+ properties: {
171
+ resourceName: {
172
+ type: 'string',
173
+ description: 'Name of the resource'
174
+ },
175
+ id: {
176
+ type: 'string',
177
+ description: 'Document ID'
178
+ },
179
+ partition: {
180
+ type: 'string',
181
+ description: 'Partition name for optimized check'
182
+ },
183
+ partitionValues: {
184
+ type: 'object',
185
+ description: 'Partition values for targeted check'
186
+ }
187
+ },
188
+ required: ['resourceName', 'id']
189
+ }
190
+ },
191
+ {
192
+ name: 'resourceList',
193
+ description: 'List documents in a resource with pagination and filtering',
194
+ inputSchema: {
195
+ type: 'object',
196
+ properties: {
197
+ resourceName: {
198
+ type: 'string',
199
+ description: 'Name of the resource'
200
+ },
201
+ limit: {
202
+ type: 'number',
203
+ description: 'Maximum number of documents to return',
204
+ default: 100
205
+ },
206
+ offset: {
207
+ type: 'number',
208
+ description: 'Number of documents to skip',
209
+ default: 0
210
+ },
211
+ partition: {
212
+ type: 'string',
213
+ description: 'Partition name to filter by'
214
+ },
215
+ partitionValues: {
216
+ type: 'object',
217
+ description: 'Partition values for filtering'
218
+ }
219
+ },
220
+ required: ['resourceName']
221
+ }
222
+ },
223
+ {
224
+ name: 'resourceListIds',
225
+ description: 'List document IDs in a resource',
226
+ inputSchema: {
227
+ type: 'object',
228
+ properties: {
229
+ resourceName: {
230
+ type: 'string',
231
+ description: 'Name of the resource'
232
+ },
233
+ limit: {
234
+ type: 'number',
235
+ description: 'Maximum number of IDs to return',
236
+ default: 1000
237
+ },
238
+ offset: {
239
+ type: 'number',
240
+ description: 'Number of IDs to skip',
241
+ default: 0
242
+ }
243
+ },
244
+ required: ['resourceName']
245
+ }
246
+ },
247
+ {
248
+ name: 'resourceCount',
249
+ description: 'Count documents in a resource',
250
+ inputSchema: {
251
+ type: 'object',
252
+ properties: {
253
+ resourceName: {
254
+ type: 'string',
255
+ description: 'Name of the resource'
256
+ },
257
+ partition: {
258
+ type: 'string',
259
+ description: 'Partition name to filter by'
260
+ },
261
+ partitionValues: {
262
+ type: 'object',
263
+ description: 'Partition values for filtering'
264
+ }
265
+ },
266
+ required: ['resourceName']
267
+ }
268
+ },
269
+ {
270
+ name: 'resourceGetAll',
271
+ description: 'Get all documents from a resource (use with caution on large datasets)',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ resourceName: {
276
+ type: 'string',
277
+ description: 'Name of the resource'
278
+ }
279
+ },
280
+ required: ['resourceName']
281
+ }
282
+ },
283
+ {
284
+ name: 'resourceDeleteAll',
285
+ description: 'Delete all documents from a resource',
286
+ inputSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ resourceName: {
290
+ type: 'string',
291
+ description: 'Name of the resource'
292
+ },
293
+ confirm: {
294
+ type: 'boolean',
295
+ description: 'Confirmation flag - must be true to proceed'
296
+ }
297
+ },
298
+ required: ['resourceName', 'confirm']
299
+ }
300
+ }
301
+ ];
302
+
303
+ export function createCrudHandlers(server) {
304
+ return {
305
+ async resourceInsert(args, database) {
306
+ server.ensureConnected(database);
307
+ const { resourceName, data } = args;
308
+
309
+ const resource = server.getResource(database, resourceName);
310
+ const result = await resource.insert(data);
311
+
312
+ // Extract partition information for cache invalidation
313
+ const partitionInfo = server._extractPartitionInfo(resource, result);
314
+
315
+ // Generate cache invalidation patterns
316
+ const cacheInvalidationPatterns = server._generateCacheInvalidationPatterns(resource, result, 'insert');
317
+
318
+ return {
319
+ success: true,
320
+ data: result,
321
+ ...(partitionInfo && { partitionInfo }),
322
+ cacheInvalidationPatterns
323
+ };
324
+ },
325
+
326
+ async resourceInsertMany(args, database) {
327
+ server.ensureConnected(database);
328
+ const { resourceName, data } = args;
329
+
330
+ const resource = server.getResource(database, resourceName);
331
+ const result = await resource.insertMany(data);
332
+
333
+ return {
334
+ success: true,
335
+ data: result,
336
+ count: result.length
337
+ };
338
+ },
339
+
340
+ async resourceGet(args, database) {
341
+ server.ensureConnected(database);
342
+ const { resourceName, id, partition, partitionValues } = args;
343
+
344
+ const resource = server.getResource(database, resourceName);
345
+
346
+ // Use partition information for optimized retrieval if provided
347
+ let options = {};
348
+ if (partition && partitionValues) {
349
+ options.partition = partition;
350
+ options.partitionValues = partitionValues;
351
+ }
352
+
353
+ const result = await resource.get(id, options);
354
+
355
+ // Extract partition information from result
356
+ const partitionInfo = server._extractPartitionInfo(resource, result);
357
+
358
+ return {
359
+ success: true,
360
+ data: result,
361
+ ...(partitionInfo && { partitionInfo })
362
+ };
363
+ },
364
+
365
+ async resourceGetMany(args, database) {
366
+ server.ensureConnected(database);
367
+ const { resourceName, ids } = args;
368
+
369
+ const resource = server.getResource(database, resourceName);
370
+ const result = await resource.getMany(ids);
371
+
372
+ return {
373
+ success: true,
374
+ data: result,
375
+ count: result.length
376
+ };
377
+ },
378
+
379
+ async resourceUpdate(args, database) {
380
+ server.ensureConnected(database);
381
+ const { resourceName, id, data } = args;
382
+
383
+ const resource = server.getResource(database, resourceName);
384
+ const result = await resource.update(id, data);
385
+
386
+ // Extract partition information for cache invalidation
387
+ const partitionInfo = server._extractPartitionInfo(resource, result);
388
+
389
+ return {
390
+ success: true,
391
+ data: result,
392
+ ...(partitionInfo && { partitionInfo })
393
+ };
394
+ },
395
+
396
+ async resourceUpsert(args, database) {
397
+ server.ensureConnected(database);
398
+ const { resourceName, data } = args;
399
+
400
+ const resource = server.getResource(database, resourceName);
401
+ const result = await resource.upsert(data);
402
+
403
+ return {
404
+ success: true,
405
+ data: result
406
+ };
407
+ },
408
+
409
+ async resourceDelete(args, database) {
410
+ server.ensureConnected(database);
411
+ const { resourceName, id } = args;
412
+
413
+ const resource = server.getResource(database, resourceName);
414
+ await resource.delete(id);
415
+
416
+ return {
417
+ success: true,
418
+ message: `Document ${id} deleted from ${resourceName}`
419
+ };
420
+ },
421
+
422
+ async resourceDeleteMany(args, database) {
423
+ server.ensureConnected(database);
424
+ const { resourceName, ids } = args;
425
+
426
+ const resource = server.getResource(database, resourceName);
427
+ await resource.deleteMany(ids);
428
+
429
+ return {
430
+ success: true,
431
+ message: `${ids.length} documents deleted from ${resourceName}`,
432
+ deletedIds: ids
433
+ };
434
+ },
435
+
436
+ async resourceExists(args, database) {
437
+ server.ensureConnected(database);
438
+ const { resourceName, id, partition, partitionValues } = args;
439
+
440
+ const resource = server.getResource(database, resourceName);
441
+
442
+ // Use partition information for optimized existence check if provided
443
+ let options = {};
444
+ if (partition && partitionValues) {
445
+ options.partition = partition;
446
+ options.partitionValues = partitionValues;
447
+ }
448
+
449
+ const exists = await resource.exists(id, options);
450
+
451
+ return {
452
+ success: true,
453
+ exists,
454
+ id,
455
+ resource: resourceName,
456
+ ...(partition && { partition }),
457
+ ...(partitionValues && { partitionValues })
458
+ };
459
+ },
460
+
461
+ async resourceList(args, database) {
462
+ server.ensureConnected(database);
463
+ const { resourceName, limit = 100, offset = 0, partition, partitionValues } = args;
464
+
465
+ const resource = server.getResource(database, resourceName);
466
+ const options = { limit, offset };
467
+
468
+ if (partition && partitionValues) {
469
+ options.partition = partition;
470
+ options.partitionValues = partitionValues;
471
+ }
472
+
473
+ const result = await resource.list(options);
474
+
475
+ // Generate cache key hint for intelligent caching
476
+ const cacheKeyHint = server._generateCacheKeyHint(resourceName, 'list', {
477
+ limit,
478
+ offset,
479
+ partition,
480
+ partitionValues
481
+ });
482
+
483
+ return {
484
+ success: true,
485
+ data: result,
486
+ count: result.length,
487
+ pagination: {
488
+ limit,
489
+ offset,
490
+ hasMore: result.length === limit
491
+ },
492
+ cacheKeyHint,
493
+ ...(partition && { partition }),
494
+ ...(partitionValues && { partitionValues })
495
+ };
496
+ },
497
+
498
+ async resourceListIds(args, database) {
499
+ server.ensureConnected(database);
500
+ const { resourceName, limit = 1000, offset = 0 } = args;
501
+
502
+ const resource = server.getResource(database, resourceName);
503
+ const result = await resource.listIds({ limit, offset });
504
+
505
+ return {
506
+ success: true,
507
+ ids: result,
508
+ count: result.length,
509
+ pagination: {
510
+ limit,
511
+ offset,
512
+ hasMore: result.length === limit
513
+ }
514
+ };
515
+ },
516
+
517
+ async resourceCount(args, database) {
518
+ server.ensureConnected(database);
519
+ const { resourceName, partition, partitionValues } = args;
520
+
521
+ const resource = server.getResource(database, resourceName);
522
+ const options = {};
523
+
524
+ if (partition && partitionValues) {
525
+ options.partition = partition;
526
+ options.partitionValues = partitionValues;
527
+ }
528
+
529
+ const count = await resource.count(options);
530
+
531
+ // Generate cache key hint for intelligent caching
532
+ const cacheKeyHint = server._generateCacheKeyHint(resourceName, 'count', {
533
+ partition,
534
+ partitionValues
535
+ });
536
+
537
+ return {
538
+ success: true,
539
+ count,
540
+ resource: resourceName,
541
+ cacheKeyHint,
542
+ ...(partition && { partition }),
543
+ ...(partitionValues && { partitionValues })
544
+ };
545
+ },
546
+
547
+ async resourceGetAll(args, database) {
548
+ server.ensureConnected(database);
549
+ const { resourceName } = args;
550
+
551
+ const resource = server.getResource(database, resourceName);
552
+ const result = await resource.getAll();
553
+
554
+ return {
555
+ success: true,
556
+ data: result,
557
+ count: result.length,
558
+ warning: result.length > 1000 ? 'Large dataset returned. Consider using resourceList with pagination.' : undefined
559
+ };
560
+ },
561
+
562
+ async resourceDeleteAll(args, database) {
563
+ server.ensureConnected(database);
564
+ const { resourceName, confirm } = args;
565
+
566
+ if (!confirm) {
567
+ throw new Error('Confirmation required. Set confirm: true to proceed with deleting all data.');
568
+ }
569
+
570
+ const resource = server.getResource(database, resourceName);
571
+ await resource.deleteAll();
572
+
573
+ return {
574
+ success: true,
575
+ message: `All documents deleted from ${resourceName}`
576
+ };
577
+ }
578
+ };
579
+ }