@forestadmin-experimental/datasource-cosmos 1.6.2 → 1.6.4

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.
@@ -3,8 +3,38 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configurePaginationCache = exports.getSharedPaginationCache = exports.getSharedRetryOptions = exports.configureRetryOptions = void 0;
6
7
  const crypto_1 = require("crypto");
8
+ const pagination_cache_1 = __importDefault(require("../utils/pagination-cache"));
9
+ const retry_handler_1 = require("../utils/retry-handler");
10
+ Object.defineProperty(exports, "configureRetryOptions", { enumerable: true, get: function () { return retry_handler_1.configureRetryOptions; } });
11
+ Object.defineProperty(exports, "getSharedRetryOptions", { enumerable: true, get: function () { return retry_handler_1.getSharedRetryOptions; } });
7
12
  const serializer_1 = __importDefault(require("../utils/serializer"));
13
+ /**
14
+ * Shared pagination cache instance for all models
15
+ * This is shared to allow cache reuse across collections and reduce memory usage
16
+ */
17
+ let sharedPaginationCache = null;
18
+ /**
19
+ * Get or create the shared pagination cache instance
20
+ * @param options Optional configuration options (only used on first call)
21
+ */
22
+ function getSharedPaginationCache(options) {
23
+ if (!sharedPaginationCache) {
24
+ sharedPaginationCache = new pagination_cache_1.default(options);
25
+ }
26
+ return sharedPaginationCache;
27
+ }
28
+ exports.getSharedPaginationCache = getSharedPaginationCache;
29
+ /**
30
+ * Configure the shared pagination cache with custom options
31
+ * Must be called before any queries are made
32
+ * @param options Pagination cache configuration
33
+ */
34
+ function configurePaginationCache(options) {
35
+ sharedPaginationCache = new pagination_cache_1.default(options);
36
+ }
37
+ exports.configurePaginationCache = configurePaginationCache;
8
38
  class ModelCosmos {
9
39
  constructor(cosmosClient, name, databaseName, containerName, partitionKeyPath, schema, overrideTypeConverter, enableCount) {
10
40
  this.name = name;
@@ -16,6 +46,8 @@ class ModelCosmos {
16
46
  this.enableCount = enableCount;
17
47
  this.cosmosClient = cosmosClient;
18
48
  this.container = this.cosmosClient.database(databaseName).container(containerName);
49
+ this.paginationCache = getSharedPaginationCache();
50
+ this.retryOptions = (0, retry_handler_1.getSharedRetryOptions)();
19
51
  }
20
52
  getCosmosClient() {
21
53
  return this.cosmosClient;
@@ -41,136 +73,164 @@ class ModelCosmos {
41
73
  ...unflattenedRecord,
42
74
  };
43
75
  // eslint-disable-next-line no-await-in-loop -- Sequential execution maintains order
44
- const { resource } = await this.container.items.create(itemToCreate);
76
+ const { resource } = await (0, retry_handler_1.withRetry)(() => this.container.items.create(itemToCreate), this.retryOptions);
45
77
  // Flatten and serialize the response
46
78
  createdRecords.push(serializer_1.default.serialize(resource));
47
79
  }
48
80
  return createdRecords;
49
81
  }
50
- async update(ids, patch) {
51
- // Cosmos DB requires both id and partition key for updates
52
- // We need to fetch the items first to get their partition keys
53
- const itemsToUpdate = await this.getItemsByIds(ids);
82
+ async update(items, patch) {
54
83
  // Unflatten the patch to restore nested structure for Cosmos DB
55
84
  const unflattenedPatch = serializer_1.default.unflatten(patch);
56
- // Sequential execution required: Cosmos DB updates must be performed one at a time
57
- // to ensure consistency and prevent conflicts with optimistic concurrency control
58
- for (const item of itemsToUpdate) {
59
- const partitionKeyValue = this.getPartitionKeyValue(item);
60
- // Deep merge the unflattened patch with the existing item
61
- const updatedItem = serializer_1.default.deepMerge(item, unflattenedPatch);
85
+ // Use point reads to fetch items (1 RU each) instead of cross-partition query
86
+ // Then perform point updates (also optimized with partition key)
87
+ for (const { id, partitionKey } of items) {
88
+ // Point read: directly fetch item using id + partition key (1 RU)
62
89
  // eslint-disable-next-line no-await-in-loop -- Sequential maintains consistency
63
- await this.container.item(item.id, partitionKeyValue).replace(updatedItem);
90
+ const { resource: existingItem } = await (0, retry_handler_1.withRetry)(() => this.container.item(id, partitionKey).read(), this.retryOptions);
91
+ if (existingItem) {
92
+ // Deep merge the unflattened patch with the existing item
93
+ const updatedItem = serializer_1.default.deepMerge(existingItem, unflattenedPatch);
94
+ // Point update: directly update using id + partition key
95
+ // eslint-disable-next-line no-await-in-loop -- Sequential maintains consistency
96
+ await (0, retry_handler_1.withRetry)(() => this.container.item(id, partitionKey).replace(updatedItem), this.retryOptions);
97
+ }
64
98
  }
65
99
  }
66
- async delete(ids) {
67
- // Similar to update, we need partition keys for deletion
68
- const itemsToDelete = await this.getItemsByIds(ids);
69
- // Sequential execution required: Cosmos DB deletes must be performed one at a time
70
- // to ensure consistency and prevent conflicts
71
- for (const item of itemsToDelete) {
72
- const partitionKeyValue = this.getPartitionKeyValue(item);
100
+ async delete(items) {
101
+ // Use point deletes with id + partition key (optimized, no cross-partition scan needed)
102
+ for (const { id, partitionKey } of items) {
103
+ // Point delete: directly delete using id + partition key
73
104
  // eslint-disable-next-line no-await-in-loop -- Sequential maintains consistency
74
- await this.container.item(item.id, partitionKeyValue).delete();
105
+ await (0, retry_handler_1.withRetry)(() => this.container.item(id, partitionKey).delete(), this.retryOptions);
75
106
  }
76
107
  }
77
- async query(querySpec, offset, limit) {
108
+ async query(querySpec, offset, limit, partitionKey) {
109
+ // Build query options
110
+ const queryOptions = {};
111
+ // Add partition key if provided (enables single-partition query optimization)
112
+ if (partitionKey !== undefined) {
113
+ queryOptions.partitionKey = partitionKey;
114
+ }
78
115
  // If no pagination parameters, fetch all (backward compatibility)
79
116
  if (offset === undefined && limit === undefined) {
80
- const { resources } = await this.container.items.query(querySpec).fetchAll();
117
+ const { resources } = await (0, retry_handler_1.withRetry)(() => this.container.items.query(querySpec, queryOptions).fetchAll(), this.retryOptions);
81
118
  return resources.map(item => serializer_1.default.serialize(item));
82
119
  }
83
- // Use efficient pagination when limit is specified
84
- // Note: Cosmos DB doesn't support native OFFSET, so we need to skip items client-side
85
- // but we can still benefit from maxItemCount to limit network transfers
86
- const query = this.container.items.query(querySpec, {
87
- maxItemCount: limit ? (offset || 0) + limit : undefined,
88
- });
120
+ const effectiveOffset = offset || 0;
121
+ const effectiveLimit = limit || 100; // Default limit if not specified
122
+ // Check if offset exceeds maximum allowed
123
+ const maxOffset = this.paginationCache.getMaxOffset();
124
+ if (effectiveOffset > maxOffset) {
125
+ throw new Error(`Offset ${effectiveOffset} exceeds maximum allowed offset of ${maxOffset}. ` +
126
+ `Consider using filters to narrow down your results instead of deep pagination.`);
127
+ }
128
+ // Generate query hash for cache lookup
129
+ const queryHash = this.paginationCache.generateQueryHash(querySpec.query, querySpec.parameters, partitionKey);
130
+ // Try to find a cached continuation token close to our target offset
131
+ const cachedEntry = this.paginationCache.findBestToken(queryHash, effectiveOffset);
132
+ let startOffset = 0;
133
+ if (cachedEntry) {
134
+ // Resume from cached position
135
+ startOffset = cachedEntry.offset;
136
+ queryOptions.continuationToken = cachedEntry.continuationToken;
137
+ }
138
+ // Calculate how many items we need to skip from the start position
139
+ const itemsToSkip = effectiveOffset - startOffset;
140
+ const totalItemsNeeded = itemsToSkip + effectiveLimit;
141
+ // Set page size - use a reasonable batch size for efficiency
142
+ // Larger batches = fewer round trips but more memory
143
+ const pageSize = Math.min(Math.max(effectiveLimit, 100), 1000);
144
+ queryOptions.maxItemCount = pageSize;
89
145
  const results = [];
90
146
  let itemsFetched = 0;
91
- const targetCount = (offset || 0) + (limit || 0);
147
+ let lastContinuationToken;
148
+ let currentOffset = startOffset;
149
+ // Create query iterator
150
+ const queryIterator = this.container.items.query(querySpec, queryOptions);
92
151
  // Iterate through pages until we have enough items
93
152
  // eslint-disable-next-line no-restricted-syntax
94
- for await (const { resources: page } of query.getAsyncIterator()) {
153
+ for await (const response of queryIterator.getAsyncIterator()) {
154
+ const { resources: page, continuationToken } = response;
155
+ // Store continuation token for future use at regular intervals
156
+ const cacheInterval = this.paginationCache.getCacheInterval();
157
+ if (continuationToken && currentOffset > 0 && currentOffset % cacheInterval < pageSize) {
158
+ this.paginationCache.storeToken(queryHash, currentOffset, continuationToken);
159
+ }
95
160
  results.push(...page);
96
161
  itemsFetched += page.length;
162
+ currentOffset += page.length;
163
+ lastContinuationToken = continuationToken;
97
164
  // Stop fetching if we have enough items
98
- if (limit && itemsFetched >= targetCount) {
165
+ if (itemsFetched >= totalItemsNeeded) {
166
+ break;
167
+ }
168
+ // Safety check: if no more pages, break
169
+ if (!continuationToken) {
99
170
  break;
100
171
  }
101
172
  }
102
- // Apply offset and limit
173
+ // Store the final continuation token if we have one
174
+ if (lastContinuationToken && currentOffset > startOffset) {
175
+ this.paginationCache.storeToken(queryHash, currentOffset, lastContinuationToken);
176
+ }
177
+ // Apply offset (skip items we don't need) and limit
103
178
  let finalResults = results;
104
- if (offset) {
105
- finalResults = finalResults.slice(offset);
179
+ if (itemsToSkip > 0) {
180
+ finalResults = finalResults.slice(itemsToSkip);
106
181
  }
107
- if (limit) {
108
- finalResults = finalResults.slice(0, limit);
182
+ if (effectiveLimit) {
183
+ finalResults = finalResults.slice(0, effectiveLimit);
109
184
  }
110
185
  return finalResults.map(item => serializer_1.default.serialize(item));
111
186
  }
112
- async aggregateQuery(querySpec) {
113
- const { resources } = await this.container.items.query(querySpec).fetchAll();
187
+ async aggregateQuery(querySpec, partitionKey) {
188
+ // Build query options with partition key if provided
189
+ const queryOptions = {};
190
+ if (partitionKey !== undefined) {
191
+ queryOptions.partitionKey = partitionKey;
192
+ }
193
+ const { resources } = await (0, retry_handler_1.withRetry)(() => this.container.items.query(querySpec, queryOptions).fetchAll(), this.retryOptions);
114
194
  return resources.map(item => serializer_1.default.serialize(item));
115
195
  }
116
- async count(querySpec) {
196
+ async count(querySpec, partitionKey) {
197
+ // Build query options with partition key if provided
198
+ const queryOptions = {};
199
+ if (partitionKey !== undefined) {
200
+ queryOptions.partitionKey = partitionKey;
201
+ }
117
202
  if (!querySpec) {
118
203
  // Simple count without filters
119
204
  const countQuery = {
120
205
  query: 'SELECT VALUE COUNT(1) FROM c',
121
206
  };
122
- const { resources } = await this.container.items.query(countQuery).fetchAll();
207
+ const { resources } = await (0, retry_handler_1.withRetry)(() => this.container.items.query(countQuery, queryOptions).fetchAll(), this.retryOptions);
123
208
  return resources[0] || 0;
124
209
  }
125
- // Count with filters - we need to modify the query to use COUNT
126
- const { resources } = await this.container.items.query(querySpec).fetchAll();
127
- return resources.length;
210
+ // Convert the query to a COUNT query to avoid fetching all records
211
+ const countQuery = this.convertToCountQuery(querySpec);
212
+ const { resources } = await (0, retry_handler_1.withRetry)(() => this.container.items.query(countQuery, queryOptions).fetchAll(), this.retryOptions);
213
+ return resources[0] || 0;
128
214
  }
129
- // INTERNAL HELPER METHODS
130
215
  /**
131
- * Get items by their IDs (requires scanning as we don't have partition keys)
216
+ * Convert a SELECT query to a COUNT query
217
+ * Extracts the WHERE clause and creates a COUNT query with the same filters
132
218
  */
133
- async getItemsByIds(ids) {
134
- const querySpec = {
135
- query: 'SELECT * FROM c WHERE ARRAY_CONTAINS(@ids, c.id)',
136
- parameters: [
137
- {
138
- name: '@ids',
139
- value: ids,
140
- },
141
- ],
219
+ convertToCountQuery(querySpec) {
220
+ const { query, parameters } = querySpec;
221
+ // Extract WHERE clause from original query
222
+ // Query format: "SELECT ... FROM c WHERE ... ORDER BY ..."
223
+ const whereMatch = query.match(/WHERE\s+(.+?)(?:\s+ORDER\s+BY|$)/i);
224
+ const whereClause = whereMatch ? whereMatch[1].trim() : '';
225
+ const countQueryString = whereClause
226
+ ? `SELECT VALUE COUNT(1) FROM c WHERE ${whereClause}`
227
+ : 'SELECT VALUE COUNT(1) FROM c';
228
+ return {
229
+ query: countQueryString,
230
+ parameters,
142
231
  };
143
- const { resources } = await this.container.items.query(querySpec).fetchAll();
144
- return resources;
145
- }
146
- /**
147
- * Extract the partition key value from an item
148
- */
149
- getPartitionKeyValue(item) {
150
- // Remove leading slash from partition key path
151
- const keyPath = this.partitionKeyPath.replace(/^\//, '');
152
- // Handle nested paths (e.g., "/address/city")
153
- const keys = keyPath.split('/');
154
- let value = item;
155
- for (const key of keys) {
156
- if (value && typeof value === 'object' && !Array.isArray(value)) {
157
- value = value[key];
158
- }
159
- else {
160
- break;
161
- }
162
- }
163
- // Validate that we found a valid partition key value
164
- if (value === undefined || value === null) {
165
- throw new Error(`Partition key '${this.partitionKeyPath}' is undefined or null in item. ` +
166
- `Item ID: ${item.id || 'unknown'}`);
167
- }
168
- if (typeof value !== 'string' && typeof value !== 'number') {
169
- throw new Error(`Partition key '${this.partitionKeyPath}' must be string or number, ` +
170
- `but got ${typeof value}. Item ID: ${item.id || 'unknown'}`);
171
- }
172
- return value;
173
232
  }
233
+ // INTERNAL HELPER METHODS
174
234
  /**
175
235
  * Generate a unique ID for new items
176
236
  */
@@ -209,4 +269,4 @@ class ModelCosmos {
209
269
  }
210
270
  }
211
271
  exports.default = ModelCosmos;
212
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwtYnVpbGRlci9tb2RlbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUVBLG1DQUFvQztBQUdwQyxxRUFBNkM7QUFVN0MsTUFBcUIsV0FBVztJQXFDOUIsWUFDRSxZQUEwQixFQUMxQixJQUFZLEVBQ1osWUFBb0IsRUFDcEIsYUFBcUIsRUFDckIsZ0JBQXdCLEVBQ3hCLE1BQW9CLEVBQ3BCLHFCQUE2QyxFQUM3QyxXQUFxQjtRQUVyQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsYUFBYSxHQUFHLGFBQWEsQ0FBQztRQUNuQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDekMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLHFCQUFxQixHQUFHLHFCQUFxQixDQUFDO1FBQ25ELElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBRS9CLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3JGLENBQUM7SUFFTSxlQUFlO1FBQ3BCLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQztJQUMzQixDQUFDO0lBRU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFrQjtRQUNwQyxNQUFNLGNBQWMsR0FBaUIsRUFBRSxDQUFDO1FBRXhDLHlDQUF5QztRQUN6QyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRTtnQkFDdkIsU0FBUyxDQUFD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
272
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwtYnVpbGRlci9tb2RlbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFFQSxtQ0FBb0M7QUFHcEMsaUZBQW9GO0FBQ3BGLDBEQUtnQztBQUdULHNHQU5yQixxQ0FBcUIsT0FNcUI7QUFBRSxzR0FMNUMscUNBQXFCLE9BSzRDO0FBRm5FLHFFQUE2QztBQWlCN0M7OztHQUdHO0FBQ0gsSUFBSSxxQkFBcUIsR0FBMkIsSUFBSSxDQUFDO0FBRXpEOzs7R0FHRztBQUNILFNBQWdCLHdCQUF3QixDQUFDLE9BQWdDO0lBQ3ZFLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQzNCLHFCQUFxQixHQUFHLElBQUksMEJBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRUQsT0FBTyxxQkFBcUIsQ0FBQztBQUMvQixDQUFDO0FBTkQsNERBTUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQUMsT0FBK0I7SUFDdEUscUJBQXFCLEdBQUcsSUFBSSwwQkFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ3ZELENBQUM7QUFGRCw0REFFQztBQUVELE1BQXFCLFdBQVc7SUErQzlCLFlBQ0UsWUFBMEIsRUFDMUIsSUFBWSxFQUNaLFlBQW9CLEVBQ3BCLGFBQXFCLEVBQ3JCLGdCQUF3QixFQUN4QixNQUFvQixFQUNwQixxQkFBNkMsRUFDN0MsV0FBcUI7UUFFckIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUM7UUFDbkMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDO1FBQ3pDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxxQkFBcUIsR0FBRyxxQkFBcUIs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
@@ -1,9 +1,15 @@
1
1
  import { SqlQuerySpec } from '@azure/cosmos';
2
2
  import { AggregateResult, Aggregation, AggregationOperation, ConditionTree } from '@forestadmin/datasource-toolkit';
3
3
  export default class AggregationConverter {
4
+ /**
5
+ * Shared validator instance for field name validation
6
+ * Using permissive options since aggregation fields come from Forest Admin SDK
7
+ */
8
+ private static validator;
4
9
  private static AGGREGATION_OPERATION;
5
10
  /**
6
11
  * Convert Forest Admin field notation (arrow ->) to Cosmos DB notation (dot .)
12
+ * Also validates the field name to prevent SQL injection
7
13
  */
8
14
  private static toCosmosField;
9
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"aggregation-converter.d.ts","sourceRoot":"","sources":["../../src/utils/aggregation-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EAEd,MAAM,iCAAiC,CAAC;AAKzC,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAMlC;IAEF;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAC1B,WAAW,EAAE,WAAW,EACxB,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;IAiBf;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAavC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA8D3C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IA0BvC;;OAEG;IACH,MAAM,CAAC,yBAAyB,CAC9B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,WAAW,GACvB,eAAe,EAAE;IAoCpB;;OAEG;IACH,MAAM,CAAC,2BAA2B,CAChC,SAAS,EAAE,oBAAoB,EAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;CAuChB"}
1
+ {"version":3,"file":"aggregation-converter.d.ts","sourceRoot":"","sources":["../../src/utils/aggregation-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EAEd,MAAM,iCAAiC,CAAC;AAMzC,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS,CAGrB;IAEH,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAMlC;IAEF;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAC1B,WAAW,EAAE,WAAW,EACxB,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;IAiBf;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAavC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA8D3C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IA0BvC;;OAEG;IACH,MAAM,CAAC,yBAAyB,CAC9B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,WAAW,GACvB,eAAe,EAAE;IAoCpB;;OAEG;IACH,MAAM,CAAC,2BAA2B,CAChC,SAAS,EAAE,oBAAoB,EAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;CAyChB"}
@@ -4,12 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const query_converter_1 = __importDefault(require("./query-converter"));
7
+ const query_validator_1 = __importDefault(require("./query-validator"));
7
8
  const serializer_1 = __importDefault(require("./serializer"));
8
9
  class AggregationConverter {
9
10
  /**
10
11
  * Convert Forest Admin field notation (arrow ->) to Cosmos DB notation (dot .)
12
+ * Also validates the field name to prevent SQL injection
11
13
  */
12
- static toCosmosField(field) {
14
+ static toCosmosField(field, context = 'Aggregation field') {
15
+ // Validate field name to prevent SQL injection
16
+ this.validator.validateFieldName(field, context);
13
17
  return field.replace(/->/g, '.');
14
18
  }
15
19
  /**
@@ -37,7 +41,7 @@ class AggregationConverter {
37
41
  return 'COUNT(1)';
38
42
  }
39
43
  const operation = this.AGGREGATION_OPERATION[aggregation.operation];
40
- const cosmosField = this.toCosmosField(aggregation.field);
44
+ const cosmosField = this.toCosmosField(aggregation.field, 'Aggregation target field');
41
45
  const fieldPath = `c.${cosmosField}`;
42
46
  return `${operation}(${fieldPath})`;
43
47
  }
@@ -54,7 +58,7 @@ class AggregationConverter {
54
58
  // For now, we'll use VALUE syntax with DISTINCT for simple grouping
55
59
  if (groups.length === 1 && !groups[0].operation) {
56
60
  // Simple single field grouping
57
- const cosmosGroupField = this.toCosmosField(groups[0].field);
61
+ const cosmosGroupField = this.toCosmosField(groups[0].field, 'Group by field');
58
62
  const groupField = `c.${cosmosGroupField}`;
59
63
  // For Count operation without field, we need a different approach
60
64
  if (aggregation.operation === 'Count' && !aggregation.field) {
@@ -94,7 +98,7 @@ class AggregationConverter {
94
98
  * Build date grouping expression
95
99
  */
96
100
  static buildDateGroupExpression(field, operation, alias) {
97
- const cosmosField = this.toCosmosField(field);
101
+ const cosmosField = this.toCosmosField(field, 'Date group field');
98
102
  const fieldPath = `c.${cosmosField}`;
99
103
  switch (operation) {
100
104
  case 'Year':
@@ -154,8 +158,10 @@ class AggregationConverter {
154
158
  let selectClause;
155
159
  let groupByClause = '';
156
160
  let orderByClause = '';
157
- const cosmosField = field ? this.toCosmosField(field) : null;
158
- const cosmosGroupByField = groupByField ? this.toCosmosField(groupByField) : null;
161
+ const cosmosField = field ? this.toCosmosField(field, 'Aggregation target field') : null;
162
+ const cosmosGroupByField = groupByField
163
+ ? this.toCosmosField(groupByField, 'Group by field')
164
+ : null;
159
165
  if (!cosmosField) {
160
166
  // COUNT(*) case
161
167
  selectClause = cosmosGroupByField
@@ -180,6 +186,14 @@ class AggregationConverter {
180
186
  return { query, parameters };
181
187
  }
182
188
  }
189
+ /**
190
+ * Shared validator instance for field name validation
191
+ * Using permissive options since aggregation fields come from Forest Admin SDK
192
+ */
193
+ AggregationConverter.validator = new query_validator_1.default(undefined, {
194
+ allowUnknownFields: true,
195
+ maxFieldDepth: 10,
196
+ });
183
197
  AggregationConverter.AGGREGATION_OPERATION = {
184
198
  Sum: 'SUM',
185
199
  Avg: 'AVG',
@@ -188,4 +202,4 @@ AggregationConverter.AGGREGATION_OPERATION = {
188
202
  Min: 'MIN',
189
203
  };
190
204
  exports.default = AggregationConverter;
191
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdncmVnYXRpb24tY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2FnZ3JlZ2F0aW9uLWNvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVNBLHdFQUErQztBQUMvQyw4REFBc0M7QUFFdEMsTUFBcUIsb0JBQW9CO0lBU3ZDOztPQUVHO0lBQ0ssTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFhO1FBQ3hDLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLHFCQUFxQixDQUMxQixXQUF3QixFQUN4QixhQUE2QixFQUM3QixLQUFjO1FBRWQsTUFBTSxjQUFjLEdBQUcsSUFBSSx5QkFBYyxFQUFFLENBQUM7UUFDNUMsTUFBTSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsR0FBRyxjQUFjLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzNFLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBRXBELDZDQUE2QztRQUM3QyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sSUFBSSxXQUFXLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMzRCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDaEUsTUFBTSxLQUFLLEdBQUcsVUFBVSxZQUFZLDZCQUE2QixhQUFhLEVBQUUsQ0FBQztZQUVqRixPQUFPLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxDQUFDO1FBQy9CLENBQUM7UUFFRCxtQ0FBbUM7UUFDbkMsT0FBTyxJQUFJLENBQUMsNEJBQTRCLENBQUMsV0FBVyxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsS0FB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
205
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdncmVnYXRpb24tY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2FnZ3JlZ2F0aW9uLWNvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVNBLHdFQUErQztBQUMvQyx3RUFBK0M7QUFDL0MsOERBQXNDO0FBRXRDLE1BQXFCLG9CQUFvQjtJQWtCdkM7OztPQUdHO0lBQ0ssTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFhLEVBQUUsT0FBTyxHQUFHLG1CQUFtQjtRQUN2RSwrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFakQsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMscUJBQXFCLENBQzFCLFdBQXdCLEVBQ3hCLGFBQTZCLEVBQzdCLEtBQWM7UUFFZCxNQUFNLGNBQWMsR0FBRyxJQUFJLHlCQUFjLEVBQUUsQ0FBQztRQUM1QyxNQUFNLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxHQUFHLGNBQWMsQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDM0UsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFcEQsNkNBQTZDO1FBQzdDLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxJQUFJLFdBQVcsQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzNELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUNoRSxNQUFNLEtBQUssR0FBRyxVQUFVLFlBQVksNkJBQTZCLGFBQWEsRUFBRSxDQUFDO1lBRWpGLE9BQU8sRUFBRSxL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
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Cache entry for storing continuation tokens with their associated offset
3
+ */
4
+ interface CacheEntry {
5
+ /** The Cosmos DB continuation token */
6
+ continuationToken: string;
7
+ /** The offset (number of items) this token represents */
8
+ offset: number;
9
+ /** Timestamp when this entry was created */
10
+ createdAt: number;
11
+ }
12
+ /**
13
+ * Configuration options for the pagination cache
14
+ */
15
+ export interface PaginationCacheOptions {
16
+ /**
17
+ * Maximum number of entries to store per query
18
+ * Default: 100
19
+ */
20
+ maxEntriesPerQuery?: number;
21
+ /**
22
+ * Time-to-live for cache entries in milliseconds
23
+ * Default: 300000 (5 minutes)
24
+ */
25
+ ttlMs?: number;
26
+ /**
27
+ * Maximum offset allowed for pagination
28
+ * Requests beyond this offset will throw an error
29
+ * Default: 100000
30
+ */
31
+ maxOffset?: number;
32
+ /**
33
+ * Interval (in number of records) at which to cache continuation tokens
34
+ *
35
+ * Lower values = more cache hits, faster page jumps, but more memory usage
36
+ * Higher values = fewer cache entries, less memory, but slower random page access
37
+ *
38
+ * Recommended values:
39
+ * - Small collections (<10K): 500
40
+ * - Medium collections (10K-100K): 1000 (default)
41
+ * - Large collections (>100K): 2000-5000
42
+ *
43
+ * Default: 1000
44
+ */
45
+ cacheInterval?: number;
46
+ }
47
+ /**
48
+ * Cache for storing Cosmos DB continuation tokens to enable efficient cursor-based pagination
49
+ *
50
+ * This cache stores continuation tokens indexed by query hash and offset, allowing
51
+ * the system to resume pagination from the nearest cached position rather than
52
+ * fetching all items from the beginning.
53
+ *
54
+ * Example:
55
+ * - User requests offset=1000, limit=10
56
+ * - Cache has token for offset=900
57
+ * - System resumes from offset=900 and skips only 100 items instead of 1000
58
+ */
59
+ export default class PaginationCache {
60
+ private cache;
61
+ private options;
62
+ constructor(options?: PaginationCacheOptions);
63
+ /**
64
+ * Get the maximum allowed offset
65
+ */
66
+ getMaxOffset(): number;
67
+ /**
68
+ * Get the cache interval (how often to store continuation tokens)
69
+ */
70
+ getCacheInterval(): number;
71
+ /**
72
+ * Generate a unique hash for a query to use as cache key
73
+ * @param queryString The SQL query string
74
+ * @param parameters Query parameters
75
+ * @param partitionKey Optional partition key
76
+ */
77
+ generateQueryHash(queryString: string, parameters?: {
78
+ name: string;
79
+ value: unknown;
80
+ }[], partitionKey?: string | number): string;
81
+ /**
82
+ * Find the best cached continuation token for a given offset
83
+ * Returns the token with the highest offset that is still <= requested offset
84
+ *
85
+ * @param queryHash The query hash
86
+ * @param targetOffset The target offset to reach
87
+ * @returns The best matching cache entry, or null if none found
88
+ */
89
+ findBestToken(queryHash: string, targetOffset: number): CacheEntry | null;
90
+ /**
91
+ * Store a continuation token for a specific offset
92
+ *
93
+ * @param queryHash The query hash
94
+ * @param offset The offset this token represents
95
+ * @param continuationToken The Cosmos DB continuation token
96
+ */
97
+ storeToken(queryHash: string, offset: number, continuationToken: string): void;
98
+ /**
99
+ * Clear all cached tokens for a specific query
100
+ * @param queryHash The query hash
101
+ */
102
+ clearQuery(queryHash: string): void;
103
+ /**
104
+ * Clear all cached tokens
105
+ */
106
+ clearAll(): void;
107
+ /**
108
+ * Get cache statistics for monitoring
109
+ */
110
+ getStats(): {
111
+ totalQueries: number;
112
+ totalEntries: number;
113
+ };
114
+ }
115
+ export {};
116
+ //# sourceMappingURL=pagination-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination-cache.d.ts","sourceRoot":"","sources":["../../src/utils/pagination-cache.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,UAAU,UAAU;IAClB,uCAAuC;IACvC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,OAAO,CAAC,KAAK,CAAwC;IAErD,OAAO,CAAC,OAAO,CAAmC;gBAEtC,OAAO,GAAE,sBAA2B;IAShD;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;;;;OAKG;IACH,iBAAiB,CACf,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,EAC/C,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAC7B,MAAM;IAUT;;;;;;;OAOG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAoCzE;;;;;;OAMG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAsC9E;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,QAAQ,IAAI,IAAI;IAIhB;;OAEG;IACH,QAAQ,IAAI;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAY3D"}