s3db.js 13.6.0 → 14.0.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.
Files changed (193) hide show
  1. package/README.md +139 -43
  2. package/dist/s3db.cjs +72425 -38970
  3. package/dist/s3db.cjs.map +1 -1
  4. package/dist/s3db.es.js +72177 -38764
  5. package/dist/s3db.es.js.map +1 -1
  6. package/mcp/lib/base-handler.js +157 -0
  7. package/mcp/lib/handlers/connection-handler.js +280 -0
  8. package/mcp/lib/handlers/query-handler.js +533 -0
  9. package/mcp/lib/handlers/resource-handler.js +428 -0
  10. package/mcp/lib/tool-registry.js +336 -0
  11. package/mcp/lib/tools/connection-tools.js +161 -0
  12. package/mcp/lib/tools/query-tools.js +267 -0
  13. package/mcp/lib/tools/resource-tools.js +404 -0
  14. package/package.json +94 -49
  15. package/src/clients/memory-client.class.js +346 -191
  16. package/src/clients/memory-storage.class.js +300 -84
  17. package/src/clients/s3-client.class.js +7 -6
  18. package/src/concerns/geo-encoding.js +19 -2
  19. package/src/concerns/ip.js +59 -9
  20. package/src/concerns/money.js +8 -1
  21. package/src/concerns/password-hashing.js +49 -8
  22. package/src/concerns/plugin-storage.js +186 -18
  23. package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
  24. package/src/database.class.js +139 -29
  25. package/src/errors.js +332 -42
  26. package/src/plugins/api/auth/oidc-auth.js +66 -17
  27. package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
  28. package/src/plugins/api/auth/strategies/factory.class.js +63 -0
  29. package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
  30. package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
  31. package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
  32. package/src/plugins/api/concerns/failban-manager.js +106 -57
  33. package/src/plugins/api/concerns/opengraph-helper.js +116 -0
  34. package/src/plugins/api/concerns/route-context.js +601 -0
  35. package/src/plugins/api/concerns/state-machine.js +288 -0
  36. package/src/plugins/api/index.js +180 -41
  37. package/src/plugins/api/routes/auth-routes.js +198 -30
  38. package/src/plugins/api/routes/resource-routes.js +19 -4
  39. package/src/plugins/api/server/health-manager.class.js +163 -0
  40. package/src/plugins/api/server/middleware-chain.class.js +310 -0
  41. package/src/plugins/api/server/router.class.js +472 -0
  42. package/src/plugins/api/server.js +280 -1303
  43. package/src/plugins/api/utils/custom-routes.js +17 -5
  44. package/src/plugins/api/utils/guards.js +76 -17
  45. package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
  46. package/src/plugins/api/utils/openapi-generator.js +7 -6
  47. package/src/plugins/api/utils/template-engine.js +77 -3
  48. package/src/plugins/audit.plugin.js +30 -8
  49. package/src/plugins/backup.plugin.js +110 -14
  50. package/src/plugins/cache/cache.class.js +22 -5
  51. package/src/plugins/cache/filesystem-cache.class.js +116 -19
  52. package/src/plugins/cache/memory-cache.class.js +211 -57
  53. package/src/plugins/cache/multi-tier-cache.class.js +371 -0
  54. package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
  55. package/src/plugins/cache/redis-cache.class.js +552 -0
  56. package/src/plugins/cache/s3-cache.class.js +17 -8
  57. package/src/plugins/cache.plugin.js +176 -61
  58. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
  59. package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
  60. package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
  61. package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
  62. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
  63. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
  64. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
  65. package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
  66. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
  67. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
  68. package/src/plugins/cloud-inventory/index.js +29 -8
  69. package/src/plugins/cloud-inventory/registry.js +64 -42
  70. package/src/plugins/cloud-inventory.plugin.js +240 -138
  71. package/src/plugins/concerns/plugin-dependencies.js +54 -0
  72. package/src/plugins/concerns/resource-names.js +100 -0
  73. package/src/plugins/consumers/index.js +10 -2
  74. package/src/plugins/consumers/sqs-consumer.js +12 -2
  75. package/src/plugins/cookie-farm-suite.plugin.js +278 -0
  76. package/src/plugins/cookie-farm.errors.js +73 -0
  77. package/src/plugins/cookie-farm.plugin.js +869 -0
  78. package/src/plugins/costs.plugin.js +7 -1
  79. package/src/plugins/eventual-consistency/analytics.js +94 -19
  80. package/src/plugins/eventual-consistency/config.js +15 -7
  81. package/src/plugins/eventual-consistency/consolidation.js +29 -11
  82. package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
  83. package/src/plugins/eventual-consistency/helpers.js +39 -14
  84. package/src/plugins/eventual-consistency/install.js +21 -2
  85. package/src/plugins/eventual-consistency/utils.js +32 -10
  86. package/src/plugins/fulltext.plugin.js +38 -11
  87. package/src/plugins/geo.plugin.js +61 -9
  88. package/src/plugins/identity/concerns/config.js +61 -0
  89. package/src/plugins/identity/concerns/mfa-manager.js +15 -2
  90. package/src/plugins/identity/concerns/rate-limit.js +124 -0
  91. package/src/plugins/identity/concerns/resource-schemas.js +9 -1
  92. package/src/plugins/identity/concerns/token-generator.js +29 -4
  93. package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
  94. package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
  95. package/src/plugins/identity/drivers/index.js +18 -0
  96. package/src/plugins/identity/drivers/password-driver.js +122 -0
  97. package/src/plugins/identity/email-service.js +17 -2
  98. package/src/plugins/identity/index.js +413 -69
  99. package/src/plugins/identity/oauth2-server.js +413 -30
  100. package/src/plugins/identity/oidc-discovery.js +16 -8
  101. package/src/plugins/identity/rsa-keys.js +115 -35
  102. package/src/plugins/identity/server.js +166 -45
  103. package/src/plugins/identity/session-manager.js +53 -7
  104. package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
  105. package/src/plugins/identity/ui/routes.js +363 -255
  106. package/src/plugins/importer/index.js +153 -20
  107. package/src/plugins/index.js +9 -2
  108. package/src/plugins/kubernetes-inventory/index.js +6 -0
  109. package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
  110. package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
  111. package/src/plugins/kubernetes-inventory.plugin.js +980 -0
  112. package/src/plugins/metrics.plugin.js +64 -16
  113. package/src/plugins/ml/base-model.class.js +25 -15
  114. package/src/plugins/ml/regression-model.class.js +1 -1
  115. package/src/plugins/ml.errors.js +57 -25
  116. package/src/plugins/ml.plugin.js +28 -4
  117. package/src/plugins/namespace.js +210 -0
  118. package/src/plugins/plugin.class.js +180 -8
  119. package/src/plugins/puppeteer/console-monitor.js +729 -0
  120. package/src/plugins/puppeteer/cookie-manager.js +492 -0
  121. package/src/plugins/puppeteer/network-monitor.js +816 -0
  122. package/src/plugins/puppeteer/performance-manager.js +746 -0
  123. package/src/plugins/puppeteer/proxy-manager.js +478 -0
  124. package/src/plugins/puppeteer/stealth-manager.js +556 -0
  125. package/src/plugins/puppeteer.errors.js +81 -0
  126. package/src/plugins/puppeteer.plugin.js +1327 -0
  127. package/src/plugins/queue-consumer.plugin.js +69 -14
  128. package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
  129. package/src/plugins/recon/concerns/command-runner.js +148 -0
  130. package/src/plugins/recon/concerns/diff-detector.js +372 -0
  131. package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
  132. package/src/plugins/recon/concerns/process-manager.js +338 -0
  133. package/src/plugins/recon/concerns/report-generator.js +478 -0
  134. package/src/plugins/recon/concerns/security-analyzer.js +571 -0
  135. package/src/plugins/recon/concerns/target-normalizer.js +68 -0
  136. package/src/plugins/recon/config/defaults.js +321 -0
  137. package/src/plugins/recon/config/resources.js +370 -0
  138. package/src/plugins/recon/index.js +778 -0
  139. package/src/plugins/recon/managers/dependency-manager.js +174 -0
  140. package/src/plugins/recon/managers/scheduler-manager.js +179 -0
  141. package/src/plugins/recon/managers/storage-manager.js +745 -0
  142. package/src/plugins/recon/managers/target-manager.js +274 -0
  143. package/src/plugins/recon/stages/asn-stage.js +314 -0
  144. package/src/plugins/recon/stages/certificate-stage.js +84 -0
  145. package/src/plugins/recon/stages/dns-stage.js +107 -0
  146. package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
  147. package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
  148. package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
  149. package/src/plugins/recon/stages/http-stage.js +89 -0
  150. package/src/plugins/recon/stages/latency-stage.js +148 -0
  151. package/src/plugins/recon/stages/massdns-stage.js +302 -0
  152. package/src/plugins/recon/stages/osint-stage.js +1373 -0
  153. package/src/plugins/recon/stages/ports-stage.js +169 -0
  154. package/src/plugins/recon/stages/screenshot-stage.js +94 -0
  155. package/src/plugins/recon/stages/secrets-stage.js +514 -0
  156. package/src/plugins/recon/stages/subdomains-stage.js +295 -0
  157. package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
  158. package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
  159. package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
  160. package/src/plugins/recon/stages/whois-stage.js +349 -0
  161. package/src/plugins/recon.plugin.js +75 -0
  162. package/src/plugins/recon.plugin.js.backup +2635 -0
  163. package/src/plugins/relation.errors.js +87 -14
  164. package/src/plugins/replicator.plugin.js +514 -137
  165. package/src/plugins/replicators/base-replicator.class.js +89 -1
  166. package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
  167. package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
  168. package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
  169. package/src/plugins/replicators/mysql-replicator.class.js +52 -17
  170. package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
  171. package/src/plugins/replicators/postgres-replicator.class.js +62 -27
  172. package/src/plugins/replicators/s3db-replicator.class.js +25 -18
  173. package/src/plugins/replicators/schema-sync.helper.js +3 -3
  174. package/src/plugins/replicators/sqs-replicator.class.js +8 -2
  175. package/src/plugins/replicators/turso-replicator.class.js +23 -3
  176. package/src/plugins/replicators/webhook-replicator.class.js +42 -4
  177. package/src/plugins/s3-queue.plugin.js +464 -65
  178. package/src/plugins/scheduler.plugin.js +20 -6
  179. package/src/plugins/state-machine.plugin.js +40 -9
  180. package/src/plugins/tfstate/README.md +126 -126
  181. package/src/plugins/tfstate/base-driver.js +28 -4
  182. package/src/plugins/tfstate/errors.js +65 -10
  183. package/src/plugins/tfstate/filesystem-driver.js +52 -8
  184. package/src/plugins/tfstate/index.js +163 -90
  185. package/src/plugins/tfstate/s3-driver.js +64 -6
  186. package/src/plugins/ttl.plugin.js +72 -17
  187. package/src/plugins/vector/distances.js +18 -12
  188. package/src/plugins/vector/kmeans.js +26 -4
  189. package/src/resource.class.js +115 -19
  190. package/src/testing/factory.class.js +20 -3
  191. package/src/testing/seeder.class.js +7 -1
  192. package/src/clients/memory-client.md +0 -917
  193. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
@@ -0,0 +1,428 @@
1
+ import { BaseHandler } from '../base-handler.js';
2
+
3
+ /**
4
+ * Handler for resource CRUD operations
5
+ */
6
+ export class ResourceHandler extends BaseHandler {
7
+ constructor(database) {
8
+ super(database);
9
+ }
10
+
11
+ /**
12
+ * Create a new resource
13
+ */
14
+ async createResource(args) {
15
+ this.ensureConnected();
16
+ this.validateParams(args, ['name', 'attributes']);
17
+
18
+ const {
19
+ name,
20
+ attributes,
21
+ behavior = 'user-managed',
22
+ timestamps = false,
23
+ partitions,
24
+ paranoid = true,
25
+ hooks,
26
+ events,
27
+ idGenerator,
28
+ idSize = 22,
29
+ versioningEnabled
30
+ } = args;
31
+
32
+ const resource = await this.database.createResource({
33
+ name,
34
+ attributes,
35
+ behavior,
36
+ timestamps,
37
+ partitions,
38
+ paranoid,
39
+ hooks,
40
+ events,
41
+ idGenerator,
42
+ idSize,
43
+ versioningEnabled
44
+ });
45
+
46
+ return this.formatResponse({
47
+ name: resource.name,
48
+ behavior: resource.behavior,
49
+ attributes: resource.attributes,
50
+ partitions: resource.config.partitions,
51
+ timestamps: resource.config.timestamps,
52
+ paranoid: resource.config.paranoid
53
+ }, {
54
+ message: `Resource '${name}' created successfully`
55
+ });
56
+ }
57
+
58
+ /**
59
+ * List all resources
60
+ */
61
+ async listResources() {
62
+ this.ensureConnected();
63
+
64
+ const resources = await this.database.listResources();
65
+
66
+ return this.formatResponse({
67
+ resources,
68
+ count: resources.length
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Insert a document
74
+ */
75
+ async insert(args) {
76
+ this.ensureConnected();
77
+ this.validateParams(args, ['resourceName', 'data']);
78
+
79
+ const { resourceName, data } = args;
80
+ const resource = this.getResource(resourceName);
81
+
82
+ const result = await resource.insert(data);
83
+ const partitionInfo = this.extractPartitionInfo(resource, result);
84
+
85
+ return this.formatResponse(result, {
86
+ partitionInfo,
87
+ cacheInvalidated: this.getCacheInvalidationPatterns(resource, result, 'insert')
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Insert multiple documents
93
+ */
94
+ async insertMany(args) {
95
+ this.ensureConnected();
96
+ this.validateParams(args, ['resourceName', 'data']);
97
+
98
+ const { resourceName, data } = args;
99
+ const resource = this.getResource(resourceName);
100
+
101
+ const results = await resource.insertMany(data);
102
+
103
+ return this.formatResponse({
104
+ documents: results,
105
+ count: results.length
106
+ }, {
107
+ message: `Inserted ${results.length} documents`
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Get a document by ID
113
+ */
114
+ async get(args) {
115
+ this.ensureConnected();
116
+ this.validateParams(args, ['resourceName', 'id']);
117
+
118
+ const { resourceName, id, partition, partitionValues } = args;
119
+ const resource = this.getResource(resourceName);
120
+
121
+ const options = {};
122
+ if (partition && partitionValues) {
123
+ options.partition = partition;
124
+ options.partitionValues = partitionValues;
125
+ }
126
+
127
+ const result = await resource.get(id, options);
128
+ const partitionInfo = this.extractPartitionInfo(resource, result);
129
+
130
+ return this.formatResponse(result, {
131
+ partitionInfo
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Get multiple documents by IDs
137
+ */
138
+ async getMany(args) {
139
+ this.ensureConnected();
140
+ this.validateParams(args, ['resourceName', 'ids']);
141
+
142
+ const { resourceName, ids } = args;
143
+ const resource = this.getResource(resourceName);
144
+
145
+ const results = await resource.getMany(ids);
146
+
147
+ return this.formatResponse({
148
+ documents: results,
149
+ count: results.length,
150
+ missing: ids.filter(id => !results.find(doc => doc.id === id))
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Update a document
156
+ */
157
+ async update(args) {
158
+ this.ensureConnected();
159
+ this.validateParams(args, ['resourceName', 'id', 'data']);
160
+
161
+ const { resourceName, id, data } = args;
162
+ const resource = this.getResource(resourceName);
163
+
164
+ const result = await resource.update(id, data);
165
+ const partitionInfo = this.extractPartitionInfo(resource, result);
166
+
167
+ return this.formatResponse(result, {
168
+ partitionInfo,
169
+ cacheInvalidated: this.getCacheInvalidationPatterns(resource, result, 'update')
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Upsert a document
175
+ */
176
+ async upsert(args) {
177
+ this.ensureConnected();
178
+ this.validateParams(args, ['resourceName', 'data']);
179
+
180
+ const { resourceName, data } = args;
181
+ const resource = this.getResource(resourceName);
182
+
183
+ const result = await resource.upsert(data);
184
+ const isNew = !data.id || !(await resource.exists(data.id));
185
+
186
+ return this.formatResponse(result, {
187
+ operation: isNew ? 'insert' : 'update'
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Delete a document
193
+ */
194
+ async delete(args) {
195
+ this.ensureConnected();
196
+ this.validateParams(args, ['resourceName', 'id']);
197
+
198
+ const { resourceName, id } = args;
199
+ const resource = this.getResource(resourceName);
200
+
201
+ await resource.delete(id);
202
+
203
+ return this.formatResponse({ id }, {
204
+ message: `Document ${id} deleted from ${resourceName}`
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Delete multiple documents
210
+ */
211
+ async deleteMany(args) {
212
+ this.ensureConnected();
213
+ this.validateParams(args, ['resourceName', 'ids']);
214
+
215
+ const { resourceName, ids } = args;
216
+ const resource = this.getResource(resourceName);
217
+
218
+ await resource.deleteMany(ids);
219
+
220
+ return this.formatResponse({
221
+ deletedIds: ids,
222
+ count: ids.length
223
+ }, {
224
+ message: `${ids.length} documents deleted from ${resourceName}`
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Delete all documents
230
+ */
231
+ async deleteAll(args) {
232
+ this.ensureConnected();
233
+ this.validateParams(args, ['resourceName', 'confirm']);
234
+
235
+ const { resourceName, confirm } = args;
236
+
237
+ if (!confirm) {
238
+ throw new Error('Confirmation required. Set confirm: true to proceed');
239
+ }
240
+
241
+ const resource = this.getResource(resourceName);
242
+ const count = await resource.count();
243
+ await resource.deleteAll();
244
+
245
+ return this.formatResponse({
246
+ deletedCount: count
247
+ }, {
248
+ message: `All documents deleted from ${resourceName}`
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Check if document exists
254
+ */
255
+ async exists(args) {
256
+ this.ensureConnected();
257
+ this.validateParams(args, ['resourceName', 'id']);
258
+
259
+ const { resourceName, id, partition, partitionValues } = args;
260
+ const resource = this.getResource(resourceName);
261
+
262
+ const options = {};
263
+ if (partition && partitionValues) {
264
+ options.partition = partition;
265
+ options.partitionValues = partitionValues;
266
+ }
267
+
268
+ const exists = await resource.exists(id, options);
269
+
270
+ return this.formatResponse({
271
+ exists,
272
+ id,
273
+ resourceName
274
+ }, {
275
+ partition,
276
+ partitionValues
277
+ });
278
+ }
279
+
280
+ /**
281
+ * List documents with pagination
282
+ */
283
+ async list(args) {
284
+ this.ensureConnected();
285
+ this.validateParams(args, ['resourceName']);
286
+
287
+ const {
288
+ resourceName,
289
+ limit = 100,
290
+ offset = 0,
291
+ partition,
292
+ partitionValues
293
+ } = args;
294
+
295
+ const resource = this.getResource(resourceName);
296
+ const options = { limit, offset };
297
+
298
+ if (partition && partitionValues) {
299
+ options.partition = partition;
300
+ options.partitionValues = partitionValues;
301
+ }
302
+
303
+ const results = await resource.list(options);
304
+
305
+ return this.formatResponse({
306
+ documents: results,
307
+ count: results.length,
308
+ pagination: {
309
+ limit,
310
+ offset,
311
+ hasMore: results.length === limit
312
+ }
313
+ }, {
314
+ cacheKey: this.generateCacheKey(resourceName, 'list', options)
315
+ });
316
+ }
317
+
318
+ /**
319
+ * List document IDs
320
+ */
321
+ async listIds(args) {
322
+ this.ensureConnected();
323
+ this.validateParams(args, ['resourceName']);
324
+
325
+ const { resourceName, limit = 1000, offset = 0 } = args;
326
+ const resource = this.getResource(resourceName);
327
+
328
+ const ids = await resource.listIds({ limit, offset });
329
+
330
+ return this.formatResponse({
331
+ ids,
332
+ count: ids.length,
333
+ pagination: {
334
+ limit,
335
+ offset,
336
+ hasMore: ids.length === limit
337
+ }
338
+ });
339
+ }
340
+
341
+ /**
342
+ * Count documents
343
+ */
344
+ async count(args) {
345
+ this.ensureConnected();
346
+ this.validateParams(args, ['resourceName']);
347
+
348
+ const { resourceName, partition, partitionValues } = args;
349
+ const resource = this.getResource(resourceName);
350
+
351
+ const options = {};
352
+ if (partition && partitionValues) {
353
+ options.partition = partition;
354
+ options.partitionValues = partitionValues;
355
+ }
356
+
357
+ const count = await resource.count(options);
358
+
359
+ return this.formatResponse({
360
+ count,
361
+ resourceName
362
+ }, {
363
+ cacheKey: this.generateCacheKey(resourceName, 'count', options)
364
+ });
365
+ }
366
+
367
+ /**
368
+ * Get all documents (use with caution)
369
+ */
370
+ async getAll(args) {
371
+ this.ensureConnected();
372
+ this.validateParams(args, ['resourceName']);
373
+
374
+ const { resourceName } = args;
375
+ const resource = this.getResource(resourceName);
376
+
377
+ const results = await resource.getAll();
378
+
379
+ return this.formatResponse({
380
+ documents: results,
381
+ count: results.length
382
+ }, {
383
+ warning: results.length > 1000
384
+ ? 'Large dataset returned. Consider using list with pagination.'
385
+ : undefined
386
+ });
387
+ }
388
+
389
+ // Private helper methods
390
+
391
+ private getCacheInvalidationPatterns(resource, data, action) {
392
+ const patterns = [];
393
+ const resourceName = resource.name;
394
+
395
+ // Invalidate list/count operations
396
+ patterns.push(
397
+ `resource=${resourceName}/action=list`,
398
+ `resource=${resourceName}/action=count`,
399
+ `resource=${resourceName}/action=getAll`
400
+ );
401
+
402
+ // Invalidate partition-specific cache
403
+ const partitionInfo = this.extractPartitionInfo(resource, data);
404
+ if (partitionInfo) {
405
+ for (const [partition, values] of Object.entries(partitionInfo)) {
406
+ const sortedValues = Object.entries(values)
407
+ .sort(([a], [b]) => a.localeCompare(b))
408
+ .map(([k, v]) => `${k}=${v}`)
409
+ .join('&');
410
+
411
+ patterns.push(
412
+ `resource=${resourceName}/action=list/partition=${partition}/values=${sortedValues}`,
413
+ `resource=${resourceName}/action=count/partition=${partition}/values=${sortedValues}`
414
+ );
415
+ }
416
+ }
417
+
418
+ // Invalidate document-specific cache
419
+ if (data.id) {
420
+ patterns.push(
421
+ `resource=${resourceName}/action=get/id=${data.id}`,
422
+ `resource=${resourceName}/action=exists/id=${data.id}`
423
+ );
424
+ }
425
+
426
+ return patterns;
427
+ }
428
+ }