@http-client-toolkit/store-sqlite 0.0.1 → 0.1.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/lib/index.cjs +467 -291
- package/lib/index.d.cts +573 -492
- package/lib/index.d.ts +573 -492
- package/lib/index.js +445 -284
- package/package.json +2 -2
package/lib/index.cjs
CHANGED
|
@@ -7,9 +7,11 @@ var sqliteCore = require('drizzle-orm/sqlite-core');
|
|
|
7
7
|
var crypto = require('crypto');
|
|
8
8
|
var core = require('@http-client-toolkit/core');
|
|
9
9
|
|
|
10
|
-
function _interopDefault
|
|
10
|
+
function _interopDefault(e) {
|
|
11
|
+
return e && e.__esModule ? e : { default: e };
|
|
12
|
+
}
|
|
11
13
|
|
|
12
|
-
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
14
|
+
var Database__default = /*#__PURE__*/ _interopDefault(Database);
|
|
13
15
|
|
|
14
16
|
var __async = (__this, __arguments, generator) => {
|
|
15
17
|
return new Promise((resolve, reject) => {
|
|
@@ -27,48 +29,51 @@ var __async = (__this, __arguments, generator) => {
|
|
|
27
29
|
reject(e);
|
|
28
30
|
}
|
|
29
31
|
};
|
|
30
|
-
var step = (x) =>
|
|
32
|
+
var step = (x) =>
|
|
33
|
+
x.done
|
|
34
|
+
? resolve(x.value)
|
|
35
|
+
: Promise.resolve(x.value).then(fulfilled, rejected);
|
|
31
36
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
32
37
|
});
|
|
33
38
|
};
|
|
34
|
-
var cacheTable = sqliteCore.sqliteTable(
|
|
35
|
-
hash: sqliteCore.text(
|
|
36
|
-
value: sqliteCore.blob(
|
|
37
|
-
expiresAt: sqliteCore.integer(
|
|
38
|
-
createdAt: sqliteCore.integer(
|
|
39
|
+
var cacheTable = sqliteCore.sqliteTable('cache', {
|
|
40
|
+
hash: sqliteCore.text('hash').primaryKey(),
|
|
41
|
+
value: sqliteCore.blob('value', { mode: 'json' }).notNull(),
|
|
42
|
+
expiresAt: sqliteCore.integer('expires_at').notNull(),
|
|
43
|
+
createdAt: sqliteCore.integer('created_at').notNull(),
|
|
39
44
|
});
|
|
40
|
-
var dedupeTable = sqliteCore.sqliteTable(
|
|
41
|
-
hash: sqliteCore.text(
|
|
42
|
-
jobId: sqliteCore.text(
|
|
43
|
-
status: sqliteCore.text(
|
|
45
|
+
var dedupeTable = sqliteCore.sqliteTable('dedupe_jobs', {
|
|
46
|
+
hash: sqliteCore.text('hash').primaryKey(),
|
|
47
|
+
jobId: sqliteCore.text('job_id').notNull(),
|
|
48
|
+
status: sqliteCore.text('status').notNull(),
|
|
44
49
|
// 'pending', 'completed', 'failed'
|
|
45
|
-
result: sqliteCore.blob(
|
|
46
|
-
error: sqliteCore.text(
|
|
47
|
-
createdAt: sqliteCore.integer(
|
|
48
|
-
updatedAt: sqliteCore.integer(
|
|
50
|
+
result: sqliteCore.blob('result', { mode: 'json' }),
|
|
51
|
+
error: sqliteCore.text('error'),
|
|
52
|
+
createdAt: sqliteCore.integer('created_at').notNull(),
|
|
53
|
+
updatedAt: sqliteCore.integer('updated_at').notNull(),
|
|
49
54
|
});
|
|
50
|
-
var rateLimitTable = sqliteCore.sqliteTable(
|
|
51
|
-
resource: sqliteCore.text(
|
|
52
|
-
timestamp: sqliteCore.integer(
|
|
53
|
-
id: sqliteCore.integer(
|
|
55
|
+
var rateLimitTable = sqliteCore.sqliteTable('rate_limits', {
|
|
56
|
+
resource: sqliteCore.text('resource').notNull(),
|
|
57
|
+
timestamp: sqliteCore.integer('timestamp').notNull(),
|
|
58
|
+
id: sqliteCore.integer('id').primaryKey({ autoIncrement: true }),
|
|
54
59
|
});
|
|
55
60
|
|
|
56
61
|
// src/sqlite-cache-store.ts
|
|
57
62
|
var SQLiteCacheStore = class {
|
|
58
63
|
constructor({
|
|
59
64
|
/** File path or existing `better-sqlite3` connection. Defaults to `':memory:'`. */
|
|
60
|
-
database =
|
|
65
|
+
database = ':memory:',
|
|
61
66
|
/** Cleanup interval in milliseconds. Defaults to 1 minute. */
|
|
62
67
|
cleanupIntervalMs = 6e4,
|
|
63
68
|
/** Maximum allowed size (in bytes) for a single cache entry. Defaults to 5 MiB. */
|
|
64
|
-
maxEntrySizeBytes = 5 * 1024 * 1024
|
|
69
|
+
maxEntrySizeBytes = 5 * 1024 * 1024,
|
|
65
70
|
} = {}) {
|
|
66
71
|
/** Indicates whether this store is responsible for managing (and therefore closing) the SQLite connection */
|
|
67
72
|
this.isConnectionManaged = false;
|
|
68
73
|
this.isDestroyed = false;
|
|
69
74
|
let sqliteInstance;
|
|
70
75
|
let isConnectionManaged = false;
|
|
71
|
-
if (typeof database ===
|
|
76
|
+
if (typeof database === 'string') {
|
|
72
77
|
sqliteInstance = new Database__default.default(database);
|
|
73
78
|
isConnectionManaged = true;
|
|
74
79
|
} else {
|
|
@@ -85,9 +90,13 @@ var SQLiteCacheStore = class {
|
|
|
85
90
|
get(hash) {
|
|
86
91
|
return __async(this, null, function* () {
|
|
87
92
|
if (this.isDestroyed) {
|
|
88
|
-
throw new Error(
|
|
93
|
+
throw new Error('Cache store has been destroyed');
|
|
89
94
|
}
|
|
90
|
-
const result = yield this.db
|
|
95
|
+
const result = yield this.db
|
|
96
|
+
.select()
|
|
97
|
+
.from(cacheTable)
|
|
98
|
+
.where(drizzleOrm.eq(cacheTable.hash, hash))
|
|
99
|
+
.limit(1);
|
|
91
100
|
if (result.length === 0) {
|
|
92
101
|
return void 0;
|
|
93
102
|
}
|
|
@@ -97,16 +106,20 @@ var SQLiteCacheStore = class {
|
|
|
97
106
|
}
|
|
98
107
|
const now = Date.now();
|
|
99
108
|
if (item.expiresAt > 0 && now >= item.expiresAt) {
|
|
100
|
-
yield this.db
|
|
109
|
+
yield this.db
|
|
110
|
+
.delete(cacheTable)
|
|
111
|
+
.where(drizzleOrm.eq(cacheTable.hash, hash));
|
|
101
112
|
return void 0;
|
|
102
113
|
}
|
|
103
114
|
try {
|
|
104
|
-
if (item.value ===
|
|
115
|
+
if (item.value === '__UNDEFINED__') {
|
|
105
116
|
return void 0;
|
|
106
117
|
}
|
|
107
118
|
return JSON.parse(item.value);
|
|
108
119
|
} catch (e) {
|
|
109
|
-
yield this.db
|
|
120
|
+
yield this.db
|
|
121
|
+
.delete(cacheTable)
|
|
122
|
+
.where(drizzleOrm.eq(cacheTable.hash, hash));
|
|
110
123
|
return void 0;
|
|
111
124
|
}
|
|
112
125
|
});
|
|
@@ -114,52 +127,58 @@ var SQLiteCacheStore = class {
|
|
|
114
127
|
set(hash, value, ttlSeconds) {
|
|
115
128
|
return __async(this, null, function* () {
|
|
116
129
|
if (this.isDestroyed) {
|
|
117
|
-
throw new Error(
|
|
130
|
+
throw new Error('Cache store has been destroyed');
|
|
118
131
|
}
|
|
119
132
|
const now = Date.now();
|
|
120
|
-
const expiresAt =
|
|
133
|
+
const expiresAt =
|
|
134
|
+
ttlSeconds < 0 ? now : ttlSeconds === 0 ? 0 : now + ttlSeconds * 1e3;
|
|
121
135
|
let serializedValue;
|
|
122
136
|
try {
|
|
123
137
|
if (value === void 0) {
|
|
124
|
-
serializedValue =
|
|
138
|
+
serializedValue = '__UNDEFINED__';
|
|
125
139
|
} else {
|
|
126
140
|
serializedValue = JSON.stringify(value);
|
|
127
141
|
}
|
|
128
142
|
} catch (error) {
|
|
129
143
|
throw new Error(
|
|
130
|
-
`Failed to serialize value: ${error instanceof Error ? error.message : String(error)}
|
|
144
|
+
`Failed to serialize value: ${error instanceof Error ? error.message : String(error)}`,
|
|
131
145
|
);
|
|
132
146
|
}
|
|
133
|
-
if (Buffer.byteLength(serializedValue,
|
|
147
|
+
if (Buffer.byteLength(serializedValue, 'utf8') > this.maxEntrySizeBytes) {
|
|
134
148
|
return;
|
|
135
149
|
}
|
|
136
|
-
yield this.db
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
createdAt: now
|
|
141
|
-
}).onConflictDoUpdate({
|
|
142
|
-
target: cacheTable.hash,
|
|
143
|
-
set: {
|
|
150
|
+
yield this.db
|
|
151
|
+
.insert(cacheTable)
|
|
152
|
+
.values({
|
|
153
|
+
hash,
|
|
144
154
|
value: serializedValue,
|
|
145
155
|
expiresAt,
|
|
146
|
-
createdAt: now
|
|
147
|
-
}
|
|
148
|
-
|
|
156
|
+
createdAt: now,
|
|
157
|
+
})
|
|
158
|
+
.onConflictDoUpdate({
|
|
159
|
+
target: cacheTable.hash,
|
|
160
|
+
set: {
|
|
161
|
+
value: serializedValue,
|
|
162
|
+
expiresAt,
|
|
163
|
+
createdAt: now,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
149
166
|
});
|
|
150
167
|
}
|
|
151
168
|
delete(hash) {
|
|
152
169
|
return __async(this, null, function* () {
|
|
153
170
|
if (this.isDestroyed) {
|
|
154
|
-
throw new Error(
|
|
171
|
+
throw new Error('Cache store has been destroyed');
|
|
155
172
|
}
|
|
156
|
-
yield this.db
|
|
173
|
+
yield this.db
|
|
174
|
+
.delete(cacheTable)
|
|
175
|
+
.where(drizzleOrm.eq(cacheTable.hash, hash));
|
|
157
176
|
});
|
|
158
177
|
}
|
|
159
178
|
clear() {
|
|
160
179
|
return __async(this, null, function* () {
|
|
161
180
|
if (this.isDestroyed) {
|
|
162
|
-
throw new Error(
|
|
181
|
+
throw new Error('Cache store has been destroyed');
|
|
163
182
|
}
|
|
164
183
|
yield this.db.delete(cacheTable);
|
|
165
184
|
});
|
|
@@ -171,19 +190,37 @@ var SQLiteCacheStore = class {
|
|
|
171
190
|
return __async(this, null, function* () {
|
|
172
191
|
var _a, _b, _c, _d;
|
|
173
192
|
const now = Date.now();
|
|
174
|
-
const totalResult = yield this.db
|
|
175
|
-
|
|
193
|
+
const totalResult = yield this.db
|
|
194
|
+
.select({ count: drizzleOrm.count() })
|
|
195
|
+
.from(cacheTable);
|
|
196
|
+
const expiredResult = yield this.db
|
|
197
|
+
.select({ count: drizzleOrm.count() })
|
|
198
|
+
.from(cacheTable)
|
|
199
|
+
.where(
|
|
200
|
+
drizzleOrm.and(
|
|
201
|
+
drizzleOrm.gt(cacheTable.expiresAt, 0),
|
|
202
|
+
drizzleOrm.lt(cacheTable.expiresAt, now),
|
|
203
|
+
),
|
|
204
|
+
);
|
|
176
205
|
const pageCount = Number(
|
|
177
|
-
this.sqlite.pragma(
|
|
206
|
+
this.sqlite.pragma('page_count', { simple: true }),
|
|
207
|
+
);
|
|
208
|
+
const pageSize = Number(
|
|
209
|
+
this.sqlite.pragma('page_size', { simple: true }),
|
|
178
210
|
);
|
|
179
|
-
const pageSize = Number(this.sqlite.pragma("page_size", { simple: true }));
|
|
180
211
|
const safePageCount = Number.isFinite(pageCount) ? pageCount : 0;
|
|
181
212
|
const safePageSize = Number.isFinite(pageSize) ? pageSize : 0;
|
|
182
|
-
const databaseSizeKB = Math.round(safePageCount * safePageSize / 1024);
|
|
213
|
+
const databaseSizeKB = Math.round((safePageCount * safePageSize) / 1024);
|
|
183
214
|
return {
|
|
184
215
|
databaseSizeKB,
|
|
185
|
-
expiredItems:
|
|
186
|
-
|
|
216
|
+
expiredItems:
|
|
217
|
+
(_b = (_a = expiredResult[0]) == null ? void 0 : _a.count) != null
|
|
218
|
+
? _b
|
|
219
|
+
: 0,
|
|
220
|
+
totalItems:
|
|
221
|
+
(_d = (_c = totalResult[0]) == null ? void 0 : _c.count) != null
|
|
222
|
+
? _d
|
|
223
|
+
: 0,
|
|
187
224
|
};
|
|
188
225
|
});
|
|
189
226
|
}
|
|
@@ -193,7 +230,14 @@ var SQLiteCacheStore = class {
|
|
|
193
230
|
cleanup() {
|
|
194
231
|
return __async(this, null, function* () {
|
|
195
232
|
const now = Date.now();
|
|
196
|
-
yield this.db
|
|
233
|
+
yield this.db
|
|
234
|
+
.delete(cacheTable)
|
|
235
|
+
.where(
|
|
236
|
+
drizzleOrm.and(
|
|
237
|
+
drizzleOrm.gt(cacheTable.expiresAt, 0),
|
|
238
|
+
drizzleOrm.lt(cacheTable.expiresAt, now),
|
|
239
|
+
),
|
|
240
|
+
);
|
|
197
241
|
});
|
|
198
242
|
}
|
|
199
243
|
/**
|
|
@@ -206,7 +250,7 @@ var SQLiteCacheStore = class {
|
|
|
206
250
|
this.cleanupInterval = void 0;
|
|
207
251
|
}
|
|
208
252
|
this.isDestroyed = true;
|
|
209
|
-
if (this.isConnectionManaged && typeof this.sqlite.close ===
|
|
253
|
+
if (this.isConnectionManaged && typeof this.sqlite.close === 'function') {
|
|
210
254
|
this.sqlite.close();
|
|
211
255
|
}
|
|
212
256
|
});
|
|
@@ -234,24 +278,30 @@ var SQLiteCacheStore = class {
|
|
|
234
278
|
if (this.cleanupIntervalMs <= 0) {
|
|
235
279
|
return;
|
|
236
280
|
}
|
|
237
|
-
this.cleanupInterval = setInterval(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
281
|
+
this.cleanupInterval = setInterval(
|
|
282
|
+
() =>
|
|
283
|
+
__async(this, null, function* () {
|
|
284
|
+
yield this.cleanup();
|
|
285
|
+
}),
|
|
286
|
+
this.cleanupIntervalMs,
|
|
287
|
+
);
|
|
288
|
+
if (typeof this.cleanupInterval.unref === 'function') {
|
|
241
289
|
this.cleanupInterval.unref();
|
|
242
290
|
}
|
|
243
291
|
}
|
|
244
292
|
cleanupExpiredItems() {
|
|
245
293
|
return __async(this, null, function* () {
|
|
246
294
|
const now = Date.now();
|
|
247
|
-
yield this.db
|
|
295
|
+
yield this.db
|
|
296
|
+
.delete(cacheTable)
|
|
297
|
+
.where(drizzleOrm.lt(cacheTable.expiresAt, now));
|
|
248
298
|
});
|
|
249
299
|
}
|
|
250
300
|
};
|
|
251
301
|
var SQLiteDedupeStore = class {
|
|
252
302
|
constructor({
|
|
253
303
|
/** File path or existing `better-sqlite3` Database instance. Defaults to `':memory:'`. */
|
|
254
|
-
database =
|
|
304
|
+
database = ':memory:',
|
|
255
305
|
/** Job timeout in milliseconds. Preferred over timeoutMs. */
|
|
256
306
|
jobTimeoutMs,
|
|
257
307
|
/** Legacy alias for jobTimeoutMs. */
|
|
@@ -259,7 +309,7 @@ var SQLiteDedupeStore = class {
|
|
|
259
309
|
/** Cleanup interval in milliseconds. Defaults to 1 minute. */
|
|
260
310
|
cleanupIntervalMs = 6e4,
|
|
261
311
|
/** Poll interval for checking pending jobs in milliseconds. Defaults to 100ms. */
|
|
262
|
-
pollIntervalMs = 100
|
|
312
|
+
pollIntervalMs = 100,
|
|
263
313
|
} = {}) {
|
|
264
314
|
/** Indicates whether this store manages (and should close) the SQLite connection */
|
|
265
315
|
this.isConnectionManaged = false;
|
|
@@ -269,7 +319,7 @@ var SQLiteDedupeStore = class {
|
|
|
269
319
|
var _a;
|
|
270
320
|
let sqliteInstance;
|
|
271
321
|
let isConnectionManaged = false;
|
|
272
|
-
if (typeof database ===
|
|
322
|
+
if (typeof database === 'string') {
|
|
273
323
|
sqliteInstance = new Database__default.default(database);
|
|
274
324
|
isConnectionManaged = true;
|
|
275
325
|
} else {
|
|
@@ -278,7 +328,8 @@ var SQLiteDedupeStore = class {
|
|
|
278
328
|
this.sqlite = sqliteInstance;
|
|
279
329
|
this.isConnectionManaged = isConnectionManaged;
|
|
280
330
|
this.db = betterSqlite3.drizzle(sqliteInstance);
|
|
281
|
-
this.jobTimeoutMs =
|
|
331
|
+
this.jobTimeoutMs =
|
|
332
|
+
(_a = timeoutMs != null ? timeoutMs : jobTimeoutMs) != null ? _a : 3e5;
|
|
282
333
|
this.cleanupIntervalMs = cleanupIntervalMs;
|
|
283
334
|
this.pollIntervalMs = pollIntervalMs;
|
|
284
335
|
this.initializeDatabase();
|
|
@@ -287,10 +338,9 @@ var SQLiteDedupeStore = class {
|
|
|
287
338
|
startCleanupInterval() {
|
|
288
339
|
if (this.cleanupIntervalMs > 0) {
|
|
289
340
|
this.cleanupInterval = setInterval(() => {
|
|
290
|
-
this.cleanupExpiredJobs().catch(() => {
|
|
291
|
-
});
|
|
341
|
+
this.cleanupExpiredJobs().catch(() => {});
|
|
292
342
|
}, this.cleanupIntervalMs);
|
|
293
|
-
if (typeof this.cleanupInterval.unref ===
|
|
343
|
+
if (typeof this.cleanupInterval.unref === 'function') {
|
|
294
344
|
this.cleanupInterval.unref();
|
|
295
345
|
}
|
|
296
346
|
}
|
|
@@ -303,18 +353,20 @@ var SQLiteDedupeStore = class {
|
|
|
303
353
|
}
|
|
304
354
|
const now = Date.now();
|
|
305
355
|
const expiredThreshold = now - this.jobTimeoutMs;
|
|
306
|
-
yield this.db
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
drizzleOrm.
|
|
310
|
-
|
|
311
|
-
|
|
356
|
+
yield this.db
|
|
357
|
+
.delete(dedupeTable)
|
|
358
|
+
.where(
|
|
359
|
+
drizzleOrm.and(
|
|
360
|
+
drizzleOrm.eq(dedupeTable.status, 'pending'),
|
|
361
|
+
drizzleOrm.lt(dedupeTable.createdAt, expiredThreshold),
|
|
362
|
+
),
|
|
363
|
+
);
|
|
312
364
|
});
|
|
313
365
|
}
|
|
314
366
|
waitFor(hash) {
|
|
315
367
|
return __async(this, null, function* () {
|
|
316
368
|
if (this.isDestroyed) {
|
|
317
|
-
throw new Error(
|
|
369
|
+
throw new Error('Dedupe store has been destroyed');
|
|
318
370
|
}
|
|
319
371
|
const existingPromise = this.jobPromises.get(hash);
|
|
320
372
|
if (existingPromise) {
|
|
@@ -322,7 +374,11 @@ var SQLiteDedupeStore = class {
|
|
|
322
374
|
}
|
|
323
375
|
let result;
|
|
324
376
|
try {
|
|
325
|
-
result = yield this.db
|
|
377
|
+
result = yield this.db
|
|
378
|
+
.select()
|
|
379
|
+
.from(dedupeTable)
|
|
380
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash))
|
|
381
|
+
.limit(1);
|
|
326
382
|
} catch (e) {
|
|
327
383
|
return void 0;
|
|
328
384
|
}
|
|
@@ -333,10 +389,10 @@ var SQLiteDedupeStore = class {
|
|
|
333
389
|
if (!job) {
|
|
334
390
|
return void 0;
|
|
335
391
|
}
|
|
336
|
-
if (job.status ===
|
|
392
|
+
if (job.status === 'completed') {
|
|
337
393
|
return this.deserializeResult(job.result);
|
|
338
394
|
}
|
|
339
|
-
if (job.status ===
|
|
395
|
+
if (job.status === 'failed') {
|
|
340
396
|
return void 0;
|
|
341
397
|
}
|
|
342
398
|
const promise = new Promise((resolve) => {
|
|
@@ -356,43 +412,53 @@ var SQLiteDedupeStore = class {
|
|
|
356
412
|
resolve(value);
|
|
357
413
|
};
|
|
358
414
|
this.jobSettlers.set(hash, settle);
|
|
359
|
-
const poll = () =>
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
try {
|
|
365
|
-
const latest = yield this.db.select().from(dedupeTable).where(drizzleOrm.eq(dedupeTable.hash, hash)).limit(1);
|
|
366
|
-
const latestJob = latest[0];
|
|
367
|
-
if (!latestJob) {
|
|
368
|
-
settle(void 0);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const isExpired = this.jobTimeoutMs > 0 && Date.now() - latestJob.createdAt >= this.jobTimeoutMs;
|
|
372
|
-
if (isExpired) {
|
|
373
|
-
yield this.db.update(dedupeTable).set({
|
|
374
|
-
status: "failed",
|
|
375
|
-
error: "Job timed out",
|
|
376
|
-
updatedAt: Date.now()
|
|
377
|
-
}).where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
415
|
+
const poll = () =>
|
|
416
|
+
__async(this, null, function* () {
|
|
417
|
+
if (this.isDestroyed) {
|
|
378
418
|
settle(void 0);
|
|
379
419
|
return;
|
|
380
420
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
421
|
+
try {
|
|
422
|
+
const latest = yield this.db
|
|
423
|
+
.select()
|
|
424
|
+
.from(dedupeTable)
|
|
425
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash))
|
|
426
|
+
.limit(1);
|
|
427
|
+
const latestJob = latest[0];
|
|
428
|
+
if (!latestJob) {
|
|
429
|
+
settle(void 0);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const isExpired =
|
|
433
|
+
this.jobTimeoutMs > 0 &&
|
|
434
|
+
Date.now() - latestJob.createdAt >= this.jobTimeoutMs;
|
|
435
|
+
if (isExpired) {
|
|
436
|
+
yield this.db
|
|
437
|
+
.update(dedupeTable)
|
|
438
|
+
.set({
|
|
439
|
+
status: 'failed',
|
|
440
|
+
error: 'Job timed out',
|
|
441
|
+
updatedAt: Date.now(),
|
|
442
|
+
})
|
|
443
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
444
|
+
settle(void 0);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (latestJob.status === 'completed') {
|
|
448
|
+
settle(this.deserializeResult(latestJob.result));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (latestJob.status === 'failed') {
|
|
452
|
+
settle(void 0);
|
|
453
|
+
}
|
|
454
|
+
} catch (e) {
|
|
386
455
|
settle(void 0);
|
|
387
456
|
}
|
|
388
|
-
}
|
|
389
|
-
settle(void 0);
|
|
390
|
-
}
|
|
391
|
-
});
|
|
457
|
+
});
|
|
392
458
|
const pollHandle = setInterval(() => {
|
|
393
459
|
void poll();
|
|
394
460
|
}, this.pollIntervalMs);
|
|
395
|
-
if (typeof pollHandle.unref ===
|
|
461
|
+
if (typeof pollHandle.unref === 'function') {
|
|
396
462
|
pollHandle.unref();
|
|
397
463
|
}
|
|
398
464
|
void poll();
|
|
@@ -402,20 +468,24 @@ var SQLiteDedupeStore = class {
|
|
|
402
468
|
settle(void 0);
|
|
403
469
|
return;
|
|
404
470
|
}
|
|
405
|
-
void (() =>
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
471
|
+
void (() =>
|
|
472
|
+
__async(this, null, function* () {
|
|
473
|
+
try {
|
|
474
|
+
yield this.db
|
|
475
|
+
.update(dedupeTable)
|
|
476
|
+
.set({
|
|
477
|
+
status: 'failed',
|
|
478
|
+
error: 'Job timed out',
|
|
479
|
+
updatedAt: Date.now(),
|
|
480
|
+
})
|
|
481
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
482
|
+
} catch (e) {
|
|
483
|
+
} finally {
|
|
484
|
+
settle(void 0);
|
|
485
|
+
}
|
|
486
|
+
}))();
|
|
417
487
|
}, this.jobTimeoutMs);
|
|
418
|
-
if (typeof timeoutHandle.unref ===
|
|
488
|
+
if (typeof timeoutHandle.unref === 'function') {
|
|
419
489
|
timeoutHandle.unref();
|
|
420
490
|
}
|
|
421
491
|
}
|
|
@@ -433,23 +503,26 @@ var SQLiteDedupeStore = class {
|
|
|
433
503
|
registerOrJoin(hash) {
|
|
434
504
|
return __async(this, null, function* () {
|
|
435
505
|
if (this.isDestroyed) {
|
|
436
|
-
throw new Error(
|
|
506
|
+
throw new Error('Dedupe store has been destroyed');
|
|
437
507
|
}
|
|
438
508
|
const now = Date.now();
|
|
439
509
|
const candidateJobId = crypto.randomUUID();
|
|
440
510
|
const registerTransaction = this.sqlite.transaction(
|
|
441
511
|
(requestHash, createdAt, jobId) => {
|
|
442
|
-
const existing = this.sqlite
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
512
|
+
const existing = this.sqlite
|
|
513
|
+
.prepare(
|
|
514
|
+
'SELECT job_id as jobId, status FROM dedupe_jobs WHERE hash = ? LIMIT 1',
|
|
515
|
+
)
|
|
516
|
+
.get(requestHash);
|
|
517
|
+
if (existing && existing.status === 'pending') {
|
|
446
518
|
return {
|
|
447
519
|
jobId: existing.jobId,
|
|
448
|
-
isOwner: false
|
|
520
|
+
isOwner: false,
|
|
449
521
|
};
|
|
450
522
|
}
|
|
451
|
-
this.sqlite
|
|
452
|
-
|
|
523
|
+
this.sqlite
|
|
524
|
+
.prepare(
|
|
525
|
+
`
|
|
453
526
|
INSERT INTO dedupe_jobs (hash, job_id, status, result, error, created_at, updated_at)
|
|
454
527
|
VALUES (?, ?, 'pending', NULL, NULL, ?, ?)
|
|
455
528
|
ON CONFLICT(hash) DO UPDATE SET
|
|
@@ -459,13 +532,14 @@ var SQLiteDedupeStore = class {
|
|
|
459
532
|
error = NULL,
|
|
460
533
|
created_at = excluded.created_at,
|
|
461
534
|
updated_at = excluded.updated_at
|
|
462
|
-
|
|
463
|
-
|
|
535
|
+
`,
|
|
536
|
+
)
|
|
537
|
+
.run(requestHash, jobId, createdAt, createdAt);
|
|
464
538
|
return {
|
|
465
539
|
jobId,
|
|
466
|
-
isOwner: true
|
|
540
|
+
isOwner: true,
|
|
467
541
|
};
|
|
468
|
-
}
|
|
542
|
+
},
|
|
469
543
|
);
|
|
470
544
|
return registerTransaction(hash, now, candidateJobId);
|
|
471
545
|
});
|
|
@@ -474,32 +548,42 @@ var SQLiteDedupeStore = class {
|
|
|
474
548
|
return __async(this, null, function* () {
|
|
475
549
|
var _a;
|
|
476
550
|
if (this.isDestroyed) {
|
|
477
|
-
throw new Error(
|
|
551
|
+
throw new Error('Dedupe store has been destroyed');
|
|
478
552
|
}
|
|
479
553
|
let serializedResult;
|
|
480
554
|
if (value === void 0) {
|
|
481
|
-
serializedResult =
|
|
555
|
+
serializedResult = '__UNDEFINED__';
|
|
482
556
|
} else if (value === null) {
|
|
483
|
-
serializedResult =
|
|
557
|
+
serializedResult = '__NULL__';
|
|
484
558
|
} else {
|
|
485
559
|
try {
|
|
486
560
|
serializedResult = JSON.stringify(value);
|
|
487
561
|
} catch (error) {
|
|
488
562
|
throw new Error(
|
|
489
|
-
`Failed to serialize result: ${error instanceof Error ? error.message : String(error)}
|
|
563
|
+
`Failed to serialize result: ${error instanceof Error ? error.message : String(error)}`,
|
|
490
564
|
);
|
|
491
565
|
}
|
|
492
566
|
}
|
|
493
567
|
const now = Date.now();
|
|
494
|
-
const existingJob = yield this.db
|
|
495
|
-
|
|
568
|
+
const existingJob = yield this.db
|
|
569
|
+
.select()
|
|
570
|
+
.from(dedupeTable)
|
|
571
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash))
|
|
572
|
+
.limit(1);
|
|
573
|
+
if (
|
|
574
|
+
existingJob.length > 0 &&
|
|
575
|
+
((_a = existingJob[0]) == null ? void 0 : _a.status) === 'completed'
|
|
576
|
+
) {
|
|
496
577
|
return;
|
|
497
578
|
}
|
|
498
|
-
yield this.db
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
579
|
+
yield this.db
|
|
580
|
+
.update(dedupeTable)
|
|
581
|
+
.set({
|
|
582
|
+
status: 'completed',
|
|
583
|
+
result: serializedResult,
|
|
584
|
+
updatedAt: now,
|
|
585
|
+
})
|
|
586
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
503
587
|
const settle = this.jobSettlers.get(hash);
|
|
504
588
|
if (settle) {
|
|
505
589
|
settle(value);
|
|
@@ -509,14 +593,17 @@ var SQLiteDedupeStore = class {
|
|
|
509
593
|
fail(hash, error) {
|
|
510
594
|
return __async(this, null, function* () {
|
|
511
595
|
if (this.isDestroyed) {
|
|
512
|
-
throw new Error(
|
|
596
|
+
throw new Error('Dedupe store has been destroyed');
|
|
513
597
|
}
|
|
514
598
|
const now = Date.now();
|
|
515
|
-
yield this.db
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
599
|
+
yield this.db
|
|
600
|
+
.update(dedupeTable)
|
|
601
|
+
.set({
|
|
602
|
+
status: 'failed',
|
|
603
|
+
error: error.message,
|
|
604
|
+
updatedAt: now,
|
|
605
|
+
})
|
|
606
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
520
607
|
const settle = this.jobSettlers.get(hash);
|
|
521
608
|
if (settle) {
|
|
522
609
|
settle(void 0);
|
|
@@ -526,9 +613,13 @@ var SQLiteDedupeStore = class {
|
|
|
526
613
|
isInProgress(hash) {
|
|
527
614
|
return __async(this, null, function* () {
|
|
528
615
|
if (this.isDestroyed) {
|
|
529
|
-
throw new Error(
|
|
616
|
+
throw new Error('Dedupe store has been destroyed');
|
|
530
617
|
}
|
|
531
|
-
const result = yield this.db
|
|
618
|
+
const result = yield this.db
|
|
619
|
+
.select()
|
|
620
|
+
.from(dedupeTable)
|
|
621
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash))
|
|
622
|
+
.limit(1);
|
|
532
623
|
if (result.length === 0) {
|
|
533
624
|
return false;
|
|
534
625
|
}
|
|
@@ -536,17 +627,25 @@ var SQLiteDedupeStore = class {
|
|
|
536
627
|
if (!job) {
|
|
537
628
|
return false;
|
|
538
629
|
}
|
|
539
|
-
const jobExpired =
|
|
630
|
+
const jobExpired =
|
|
631
|
+
this.jobTimeoutMs > 0 &&
|
|
632
|
+
Date.now() - job.createdAt >= this.jobTimeoutMs;
|
|
540
633
|
if (jobExpired) {
|
|
541
|
-
yield this.db
|
|
634
|
+
yield this.db
|
|
635
|
+
.delete(dedupeTable)
|
|
636
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
542
637
|
return false;
|
|
543
638
|
}
|
|
544
|
-
return job.status ===
|
|
639
|
+
return job.status === 'pending';
|
|
545
640
|
});
|
|
546
641
|
}
|
|
547
642
|
getResult(hash) {
|
|
548
643
|
return __async(this, null, function* () {
|
|
549
|
-
const result = yield this.db
|
|
644
|
+
const result = yield this.db
|
|
645
|
+
.select()
|
|
646
|
+
.from(dedupeTable)
|
|
647
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash))
|
|
648
|
+
.limit(1);
|
|
550
649
|
if (result.length === 0) {
|
|
551
650
|
return void 0;
|
|
552
651
|
}
|
|
@@ -557,14 +656,16 @@ var SQLiteDedupeStore = class {
|
|
|
557
656
|
const now = Date.now();
|
|
558
657
|
const isExpired = now - job.createdAt > this.jobTimeoutMs;
|
|
559
658
|
if (isExpired) {
|
|
560
|
-
yield this.db
|
|
659
|
+
yield this.db
|
|
660
|
+
.delete(dedupeTable)
|
|
661
|
+
.where(drizzleOrm.eq(dedupeTable.hash, hash));
|
|
561
662
|
return void 0;
|
|
562
663
|
}
|
|
563
|
-
if (job.status ===
|
|
664
|
+
if (job.status === 'completed') {
|
|
564
665
|
try {
|
|
565
|
-
if (job.result ===
|
|
666
|
+
if (job.result === '__UNDEFINED__') {
|
|
566
667
|
return void 0;
|
|
567
|
-
} else if (job.result ===
|
|
668
|
+
} else if (job.result === '__NULL__') {
|
|
568
669
|
return null;
|
|
569
670
|
} else if (job.result) {
|
|
570
671
|
return JSON.parse(job.result);
|
|
@@ -585,17 +686,32 @@ var SQLiteDedupeStore = class {
|
|
|
585
686
|
var _a, _b, _c, _d, _e;
|
|
586
687
|
const now = Date.now();
|
|
587
688
|
const expiredTime = now - this.jobTimeoutMs;
|
|
588
|
-
const totalResult = yield this.db
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const
|
|
592
|
-
|
|
689
|
+
const totalResult = yield this.db
|
|
690
|
+
.select({ count: drizzleOrm.count() })
|
|
691
|
+
.from(dedupeTable);
|
|
692
|
+
const pendingResult = yield this.db
|
|
693
|
+
.select({ count: drizzleOrm.count() })
|
|
694
|
+
.from(dedupeTable)
|
|
695
|
+
.where(drizzleOrm.eq(dedupeTable.status, 'pending'));
|
|
696
|
+
const completedResult = yield this.db
|
|
697
|
+
.select({ count: drizzleOrm.count() })
|
|
698
|
+
.from(dedupeTable)
|
|
699
|
+
.where(drizzleOrm.eq(dedupeTable.status, 'completed'));
|
|
700
|
+
const failedResult = yield this.db
|
|
701
|
+
.select({ count: drizzleOrm.count() })
|
|
702
|
+
.from(dedupeTable)
|
|
703
|
+
.where(drizzleOrm.eq(dedupeTable.status, 'failed'));
|
|
704
|
+
const expiredResult = yield this.db
|
|
705
|
+
.select({ count: drizzleOrm.count() })
|
|
706
|
+
.from(dedupeTable)
|
|
707
|
+
.where(drizzleOrm.lt(dedupeTable.createdAt, expiredTime));
|
|
593
708
|
return {
|
|
594
709
|
totalJobs: ((_a = totalResult[0]) == null ? void 0 : _a.count) || 0,
|
|
595
710
|
pendingJobs: ((_b = pendingResult[0]) == null ? void 0 : _b.count) || 0,
|
|
596
|
-
completedJobs:
|
|
711
|
+
completedJobs:
|
|
712
|
+
((_c = completedResult[0]) == null ? void 0 : _c.count) || 0,
|
|
597
713
|
failedJobs: ((_d = failedResult[0]) == null ? void 0 : _d.count) || 0,
|
|
598
|
-
expiredJobs: ((_e = expiredResult[0]) == null ? void 0 : _e.count) || 0
|
|
714
|
+
expiredJobs: ((_e = expiredResult[0]) == null ? void 0 : _e.count) || 0,
|
|
599
715
|
};
|
|
600
716
|
});
|
|
601
717
|
}
|
|
@@ -606,7 +722,9 @@ var SQLiteDedupeStore = class {
|
|
|
606
722
|
return __async(this, null, function* () {
|
|
607
723
|
const now = Date.now();
|
|
608
724
|
const expiredTime = now - this.jobTimeoutMs;
|
|
609
|
-
yield this.db
|
|
725
|
+
yield this.db
|
|
726
|
+
.delete(dedupeTable)
|
|
727
|
+
.where(drizzleOrm.lt(dedupeTable.createdAt, expiredTime));
|
|
610
728
|
});
|
|
611
729
|
}
|
|
612
730
|
/**
|
|
@@ -637,7 +755,7 @@ var SQLiteDedupeStore = class {
|
|
|
637
755
|
}
|
|
638
756
|
this.jobPromises.clear();
|
|
639
757
|
this.jobSettlers.clear();
|
|
640
|
-
if (this.isConnectionManaged && typeof this.sqlite.close ===
|
|
758
|
+
if (this.isConnectionManaged && typeof this.sqlite.close === 'function') {
|
|
641
759
|
this.sqlite.close();
|
|
642
760
|
}
|
|
643
761
|
});
|
|
@@ -650,10 +768,10 @@ var SQLiteDedupeStore = class {
|
|
|
650
768
|
}
|
|
651
769
|
deserializeResult(serializedResult) {
|
|
652
770
|
try {
|
|
653
|
-
if (serializedResult ===
|
|
771
|
+
if (serializedResult === '__UNDEFINED__') {
|
|
654
772
|
return void 0;
|
|
655
773
|
}
|
|
656
|
-
if (serializedResult ===
|
|
774
|
+
if (serializedResult === '__NULL__') {
|
|
657
775
|
return null;
|
|
658
776
|
}
|
|
659
777
|
if (serializedResult) {
|
|
@@ -684,11 +802,11 @@ var SQLiteDedupeStore = class {
|
|
|
684
802
|
var SQLiteRateLimitStore = class {
|
|
685
803
|
constructor({
|
|
686
804
|
/** File path or existing `better-sqlite3` Database instance. Defaults to `':memory:'`. */
|
|
687
|
-
database =
|
|
805
|
+
database = ':memory:',
|
|
688
806
|
/** Global/default rate-limit config applied when a resource-specific override is not provided. */
|
|
689
807
|
defaultConfig = core.DEFAULT_RATE_LIMIT,
|
|
690
808
|
/** Optional per-resource overrides. */
|
|
691
|
-
resourceConfigs = /* @__PURE__ */ new Map()
|
|
809
|
+
resourceConfigs = /* @__PURE__ */ new Map(),
|
|
692
810
|
} = {}) {
|
|
693
811
|
/** Indicates whether this store manages (and should close) the SQLite connection */
|
|
694
812
|
this.isConnectionManaged = false;
|
|
@@ -696,7 +814,7 @@ var SQLiteRateLimitStore = class {
|
|
|
696
814
|
this.isDestroyed = false;
|
|
697
815
|
let sqliteInstance;
|
|
698
816
|
let isConnectionManaged = false;
|
|
699
|
-
if (typeof database ===
|
|
817
|
+
if (typeof database === 'string') {
|
|
700
818
|
sqliteInstance = new Database__default.default(database);
|
|
701
819
|
isConnectionManaged = true;
|
|
702
820
|
} else {
|
|
@@ -713,18 +831,21 @@ var SQLiteRateLimitStore = class {
|
|
|
713
831
|
return __async(this, null, function* () {
|
|
714
832
|
var _a;
|
|
715
833
|
if (this.isDestroyed) {
|
|
716
|
-
throw new Error(
|
|
834
|
+
throw new Error('Rate limit store has been destroyed');
|
|
717
835
|
}
|
|
718
836
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
719
837
|
const now = Date.now();
|
|
720
838
|
const windowStart = now - config.windowMs;
|
|
721
839
|
yield this.cleanupExpiredRequests(resource, windowStart);
|
|
722
|
-
const result = yield this.db
|
|
723
|
-
drizzleOrm.
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
840
|
+
const result = yield this.db
|
|
841
|
+
.select({ count: drizzleOrm.count() })
|
|
842
|
+
.from(rateLimitTable)
|
|
843
|
+
.where(
|
|
844
|
+
drizzleOrm.and(
|
|
845
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
846
|
+
drizzleOrm.gte(rateLimitTable.timestamp, windowStart),
|
|
847
|
+
),
|
|
848
|
+
);
|
|
728
849
|
const currentCount = ((_a = result[0]) == null ? void 0 : _a.count) || 0;
|
|
729
850
|
return currentCount < config.limit;
|
|
730
851
|
});
|
|
@@ -732,12 +853,12 @@ var SQLiteRateLimitStore = class {
|
|
|
732
853
|
record(resource) {
|
|
733
854
|
return __async(this, null, function* () {
|
|
734
855
|
if (this.isDestroyed) {
|
|
735
|
-
throw new Error(
|
|
856
|
+
throw new Error('Rate limit store has been destroyed');
|
|
736
857
|
}
|
|
737
858
|
const now = Date.now();
|
|
738
859
|
yield this.db.insert(rateLimitTable).values({
|
|
739
860
|
resource,
|
|
740
|
-
timestamp: now
|
|
861
|
+
timestamp: now,
|
|
741
862
|
});
|
|
742
863
|
});
|
|
743
864
|
}
|
|
@@ -745,41 +866,47 @@ var SQLiteRateLimitStore = class {
|
|
|
745
866
|
return __async(this, null, function* () {
|
|
746
867
|
var _a;
|
|
747
868
|
if (this.isDestroyed) {
|
|
748
|
-
throw new Error(
|
|
869
|
+
throw new Error('Rate limit store has been destroyed');
|
|
749
870
|
}
|
|
750
871
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
751
872
|
const now = Date.now();
|
|
752
873
|
const windowStart = now - config.windowMs;
|
|
753
874
|
yield this.cleanupExpiredRequests(resource, windowStart);
|
|
754
|
-
const result = yield this.db
|
|
755
|
-
drizzleOrm.
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
875
|
+
const result = yield this.db
|
|
876
|
+
.select({ count: drizzleOrm.count() })
|
|
877
|
+
.from(rateLimitTable)
|
|
878
|
+
.where(
|
|
879
|
+
drizzleOrm.and(
|
|
880
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
881
|
+
drizzleOrm.gte(rateLimitTable.timestamp, windowStart),
|
|
882
|
+
),
|
|
883
|
+
);
|
|
884
|
+
const currentRequests =
|
|
885
|
+
((_a = result[0]) == null ? void 0 : _a.count) || 0;
|
|
761
886
|
const remaining = Math.max(0, config.limit - currentRequests);
|
|
762
887
|
const resetTime = new Date(now + config.windowMs);
|
|
763
888
|
return {
|
|
764
889
|
remaining,
|
|
765
890
|
resetTime,
|
|
766
|
-
limit: config.limit
|
|
891
|
+
limit: config.limit,
|
|
767
892
|
};
|
|
768
893
|
});
|
|
769
894
|
}
|
|
770
895
|
reset(resource) {
|
|
771
896
|
return __async(this, null, function* () {
|
|
772
897
|
if (this.isDestroyed) {
|
|
773
|
-
throw new Error(
|
|
898
|
+
throw new Error('Rate limit store has been destroyed');
|
|
774
899
|
}
|
|
775
|
-
yield this.db
|
|
900
|
+
yield this.db
|
|
901
|
+
.delete(rateLimitTable)
|
|
902
|
+
.where(drizzleOrm.eq(rateLimitTable.resource, resource));
|
|
776
903
|
});
|
|
777
904
|
}
|
|
778
905
|
getWaitTime(resource) {
|
|
779
906
|
return __async(this, null, function* () {
|
|
780
907
|
var _a, _b;
|
|
781
908
|
if (this.isDestroyed) {
|
|
782
|
-
throw new Error(
|
|
909
|
+
throw new Error('Rate limit store has been destroyed');
|
|
783
910
|
}
|
|
784
911
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
785
912
|
if (config.limit === 0) {
|
|
@@ -788,26 +915,36 @@ var SQLiteRateLimitStore = class {
|
|
|
788
915
|
const now = Date.now();
|
|
789
916
|
const windowStart = now - config.windowMs;
|
|
790
917
|
yield this.cleanupExpiredRequests(resource, windowStart);
|
|
791
|
-
const countResult = yield this.db
|
|
792
|
-
drizzleOrm.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
918
|
+
const countResult = yield this.db
|
|
919
|
+
.select({ count: drizzleOrm.count() })
|
|
920
|
+
.from(rateLimitTable)
|
|
921
|
+
.where(
|
|
922
|
+
drizzleOrm.and(
|
|
923
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
924
|
+
drizzleOrm.gte(rateLimitTable.timestamp, windowStart),
|
|
925
|
+
),
|
|
926
|
+
);
|
|
927
|
+
const currentRequests =
|
|
928
|
+
((_a = countResult[0]) == null ? void 0 : _a.count) || 0;
|
|
798
929
|
if (currentRequests < config.limit) {
|
|
799
930
|
return 0;
|
|
800
931
|
}
|
|
801
|
-
const oldestResult = yield this.db
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
932
|
+
const oldestResult = yield this.db
|
|
933
|
+
.select({ timestamp: rateLimitTable.timestamp })
|
|
934
|
+
.from(rateLimitTable)
|
|
935
|
+
.where(
|
|
936
|
+
drizzleOrm.and(
|
|
937
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
938
|
+
drizzleOrm.gte(rateLimitTable.timestamp, windowStart),
|
|
939
|
+
),
|
|
805
940
|
)
|
|
806
|
-
|
|
941
|
+
.orderBy(rateLimitTable.timestamp)
|
|
942
|
+
.limit(1);
|
|
807
943
|
if (oldestResult.length === 0) {
|
|
808
944
|
return 0;
|
|
809
945
|
}
|
|
810
|
-
const oldestTimestamp =
|
|
946
|
+
const oldestTimestamp =
|
|
947
|
+
(_b = oldestResult[0]) == null ? void 0 : _b.timestamp;
|
|
811
948
|
if (oldestTimestamp === void 0) {
|
|
812
949
|
return 0;
|
|
813
950
|
}
|
|
@@ -834,10 +971,15 @@ var SQLiteRateLimitStore = class {
|
|
|
834
971
|
return __async(this, null, function* () {
|
|
835
972
|
var _a;
|
|
836
973
|
if (this.isDestroyed) {
|
|
837
|
-
throw new Error(
|
|
838
|
-
}
|
|
839
|
-
const totalResult = yield this.db
|
|
840
|
-
|
|
974
|
+
throw new Error('Rate limit store has been destroyed');
|
|
975
|
+
}
|
|
976
|
+
const totalResult = yield this.db
|
|
977
|
+
.select({ count: drizzleOrm.count() })
|
|
978
|
+
.from(rateLimitTable);
|
|
979
|
+
const resourcesResult = yield this.db
|
|
980
|
+
.select({ resource: rateLimitTable.resource })
|
|
981
|
+
.from(rateLimitTable)
|
|
982
|
+
.groupBy(rateLimitTable.resource);
|
|
841
983
|
const uniqueResources = resourcesResult.length;
|
|
842
984
|
const rateLimitedResources = [];
|
|
843
985
|
for (const { resource } of resourcesResult) {
|
|
@@ -849,7 +991,7 @@ var SQLiteRateLimitStore = class {
|
|
|
849
991
|
return {
|
|
850
992
|
totalRequests: ((_a = totalResult[0]) == null ? void 0 : _a.count) || 0,
|
|
851
993
|
uniqueResources,
|
|
852
|
-
rateLimitedResources
|
|
994
|
+
rateLimitedResources,
|
|
853
995
|
};
|
|
854
996
|
});
|
|
855
997
|
}
|
|
@@ -859,7 +1001,7 @@ var SQLiteRateLimitStore = class {
|
|
|
859
1001
|
clear() {
|
|
860
1002
|
return __async(this, null, function* () {
|
|
861
1003
|
if (this.isDestroyed) {
|
|
862
|
-
throw new Error(
|
|
1004
|
+
throw new Error('Rate limit store has been destroyed');
|
|
863
1005
|
}
|
|
864
1006
|
yield this.db.delete(rateLimitTable);
|
|
865
1007
|
});
|
|
@@ -870,7 +1012,10 @@ var SQLiteRateLimitStore = class {
|
|
|
870
1012
|
cleanup() {
|
|
871
1013
|
return __async(this, null, function* () {
|
|
872
1014
|
const now = Date.now();
|
|
873
|
-
const resources = yield this.db
|
|
1015
|
+
const resources = yield this.db
|
|
1016
|
+
.select({ resource: rateLimitTable.resource })
|
|
1017
|
+
.from(rateLimitTable)
|
|
1018
|
+
.groupBy(rateLimitTable.resource);
|
|
874
1019
|
for (const { resource } of resources) {
|
|
875
1020
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
876
1021
|
const windowStart = now - config.windowMs;
|
|
@@ -884,7 +1029,7 @@ var SQLiteRateLimitStore = class {
|
|
|
884
1029
|
close() {
|
|
885
1030
|
return __async(this, null, function* () {
|
|
886
1031
|
this.isDestroyed = true;
|
|
887
|
-
if (this.isConnectionManaged && typeof this.sqlite.close ===
|
|
1032
|
+
if (this.isConnectionManaged && typeof this.sqlite.close === 'function') {
|
|
888
1033
|
this.sqlite.close();
|
|
889
1034
|
}
|
|
890
1035
|
});
|
|
@@ -897,12 +1042,14 @@ var SQLiteRateLimitStore = class {
|
|
|
897
1042
|
}
|
|
898
1043
|
cleanupExpiredRequests(resource, windowStart) {
|
|
899
1044
|
return __async(this, null, function* () {
|
|
900
|
-
yield this.db
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
drizzleOrm.
|
|
904
|
-
|
|
905
|
-
|
|
1045
|
+
yield this.db
|
|
1046
|
+
.delete(rateLimitTable)
|
|
1047
|
+
.where(
|
|
1048
|
+
drizzleOrm.and(
|
|
1049
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
1050
|
+
drizzleOrm.lt(rateLimitTable.timestamp, windowStart),
|
|
1051
|
+
),
|
|
1052
|
+
);
|
|
906
1053
|
});
|
|
907
1054
|
}
|
|
908
1055
|
initializeDatabase() {
|
|
@@ -923,15 +1070,15 @@ var SQLiteRateLimitStore = class {
|
|
|
923
1070
|
};
|
|
924
1071
|
var DEFAULT_RATE_LIMIT2 = {
|
|
925
1072
|
limit: 200,
|
|
926
|
-
windowMs: 36e5
|
|
1073
|
+
windowMs: 36e5,
|
|
927
1074
|
// 1 hour
|
|
928
1075
|
};
|
|
929
1076
|
var SqliteAdaptiveRateLimitStore = class {
|
|
930
1077
|
constructor({
|
|
931
|
-
database =
|
|
1078
|
+
database = ':memory:',
|
|
932
1079
|
defaultConfig = DEFAULT_RATE_LIMIT2,
|
|
933
1080
|
resourceConfigs = /* @__PURE__ */ new Map(),
|
|
934
|
-
adaptiveConfig = {}
|
|
1081
|
+
adaptiveConfig = {},
|
|
935
1082
|
} = {}) {
|
|
936
1083
|
/** Indicates whether this store manages (and should close) the SQLite connection */
|
|
937
1084
|
this.isConnectionManaged = false;
|
|
@@ -942,7 +1089,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
942
1089
|
this.cachedCapacity = /* @__PURE__ */ new Map();
|
|
943
1090
|
let sqliteInstance;
|
|
944
1091
|
let isConnectionManaged = false;
|
|
945
|
-
if (typeof database ===
|
|
1092
|
+
if (typeof database === 'string') {
|
|
946
1093
|
sqliteInstance = new Database__default.default(database);
|
|
947
1094
|
isConnectionManaged = true;
|
|
948
1095
|
} else {
|
|
@@ -953,36 +1100,38 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
953
1100
|
this.db = betterSqlite3.drizzle(sqliteInstance);
|
|
954
1101
|
this.defaultConfig = defaultConfig;
|
|
955
1102
|
this.resourceConfigs = resourceConfigs;
|
|
956
|
-
this.capacityCalculator = new core.AdaptiveCapacityCalculator(
|
|
1103
|
+
this.capacityCalculator = new core.AdaptiveCapacityCalculator(
|
|
1104
|
+
adaptiveConfig,
|
|
1105
|
+
);
|
|
957
1106
|
this.initializeDatabase();
|
|
958
1107
|
}
|
|
959
|
-
canProceed(resource, priority =
|
|
1108
|
+
canProceed(resource, priority = 'background') {
|
|
960
1109
|
return __async(this, null, function* () {
|
|
961
1110
|
if (this.isDestroyed) {
|
|
962
|
-
throw new Error(
|
|
1111
|
+
throw new Error('Rate limit store has been destroyed');
|
|
963
1112
|
}
|
|
964
1113
|
yield this.ensureActivityMetrics(resource);
|
|
965
1114
|
const metrics = this.getOrCreateActivityMetrics(resource);
|
|
966
1115
|
const capacity = this.calculateCurrentCapacity(resource, metrics);
|
|
967
|
-
if (priority ===
|
|
1116
|
+
if (priority === 'background' && capacity.backgroundPaused) {
|
|
968
1117
|
return false;
|
|
969
1118
|
}
|
|
970
|
-
const currentUserRequests = yield this.getCurrentUsage(resource,
|
|
1119
|
+
const currentUserRequests = yield this.getCurrentUsage(resource, 'user');
|
|
971
1120
|
const currentBackgroundRequests = yield this.getCurrentUsage(
|
|
972
1121
|
resource,
|
|
973
|
-
|
|
1122
|
+
'background',
|
|
974
1123
|
);
|
|
975
|
-
if (priority ===
|
|
1124
|
+
if (priority === 'user') {
|
|
976
1125
|
return currentUserRequests < capacity.userReserved;
|
|
977
1126
|
} else {
|
|
978
1127
|
return currentBackgroundRequests < capacity.backgroundMax;
|
|
979
1128
|
}
|
|
980
1129
|
});
|
|
981
1130
|
}
|
|
982
|
-
record(resource, priority =
|
|
1131
|
+
record(resource, priority = 'background') {
|
|
983
1132
|
return __async(this, null, function* () {
|
|
984
1133
|
if (this.isDestroyed) {
|
|
985
|
-
throw new Error(
|
|
1134
|
+
throw new Error('Rate limit store has been destroyed');
|
|
986
1135
|
}
|
|
987
1136
|
const now = Date.now();
|
|
988
1137
|
this.db.run(drizzleOrm.sql`
|
|
@@ -990,34 +1139,38 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
990
1139
|
VALUES (${resource}, ${now}, ${priority})
|
|
991
1140
|
`);
|
|
992
1141
|
const metrics = this.getOrCreateActivityMetrics(resource);
|
|
993
|
-
if (priority ===
|
|
1142
|
+
if (priority === 'user') {
|
|
994
1143
|
metrics.recentUserRequests.push(now);
|
|
995
1144
|
this.cleanupOldRequests(metrics.recentUserRequests);
|
|
996
1145
|
} else {
|
|
997
1146
|
metrics.recentBackgroundRequests.push(now);
|
|
998
1147
|
this.cleanupOldRequests(metrics.recentBackgroundRequests);
|
|
999
1148
|
}
|
|
1000
|
-
metrics.userActivityTrend =
|
|
1001
|
-
|
|
1002
|
-
|
|
1149
|
+
metrics.userActivityTrend =
|
|
1150
|
+
this.capacityCalculator.calculateActivityTrend(
|
|
1151
|
+
metrics.recentUserRequests,
|
|
1152
|
+
);
|
|
1003
1153
|
});
|
|
1004
1154
|
}
|
|
1005
1155
|
getStatus(resource) {
|
|
1006
1156
|
return __async(this, null, function* () {
|
|
1007
1157
|
if (this.isDestroyed) {
|
|
1008
|
-
throw new Error(
|
|
1158
|
+
throw new Error('Rate limit store has been destroyed');
|
|
1009
1159
|
}
|
|
1010
1160
|
yield this.ensureActivityMetrics(resource);
|
|
1011
1161
|
const metrics = this.getOrCreateActivityMetrics(resource);
|
|
1012
1162
|
const capacity = this.calculateCurrentCapacity(resource, metrics);
|
|
1013
|
-
const currentUserUsage = yield this.getCurrentUsage(resource,
|
|
1163
|
+
const currentUserUsage = yield this.getCurrentUsage(resource, 'user');
|
|
1014
1164
|
const currentBackgroundUsage = yield this.getCurrentUsage(
|
|
1015
1165
|
resource,
|
|
1016
|
-
|
|
1166
|
+
'background',
|
|
1017
1167
|
);
|
|
1018
1168
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
1019
1169
|
return {
|
|
1020
|
-
remaining:
|
|
1170
|
+
remaining:
|
|
1171
|
+
capacity.userReserved -
|
|
1172
|
+
currentUserUsage +
|
|
1173
|
+
(capacity.backgroundMax - currentBackgroundUsage),
|
|
1021
1174
|
resetTime: new Date(Date.now() + config.windowMs),
|
|
1022
1175
|
limit: this.getResourceLimit(resource),
|
|
1023
1176
|
adaptive: {
|
|
@@ -1025,28 +1178,30 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1025
1178
|
backgroundMax: capacity.backgroundMax,
|
|
1026
1179
|
backgroundPaused: capacity.backgroundPaused,
|
|
1027
1180
|
recentUserActivity: this.capacityCalculator.getRecentActivity(
|
|
1028
|
-
metrics.recentUserRequests
|
|
1181
|
+
metrics.recentUserRequests,
|
|
1029
1182
|
),
|
|
1030
|
-
reason: capacity.reason
|
|
1031
|
-
}
|
|
1183
|
+
reason: capacity.reason,
|
|
1184
|
+
},
|
|
1032
1185
|
};
|
|
1033
1186
|
});
|
|
1034
1187
|
}
|
|
1035
1188
|
reset(resource) {
|
|
1036
1189
|
return __async(this, null, function* () {
|
|
1037
1190
|
if (this.isDestroyed) {
|
|
1038
|
-
throw new Error(
|
|
1191
|
+
throw new Error('Rate limit store has been destroyed');
|
|
1039
1192
|
}
|
|
1040
|
-
yield this.db
|
|
1193
|
+
yield this.db
|
|
1194
|
+
.delete(rateLimitTable)
|
|
1195
|
+
.where(drizzleOrm.eq(rateLimitTable.resource, resource));
|
|
1041
1196
|
this.activityMetrics.delete(resource);
|
|
1042
1197
|
this.cachedCapacity.delete(resource);
|
|
1043
1198
|
this.lastCapacityUpdate.delete(resource);
|
|
1044
1199
|
});
|
|
1045
1200
|
}
|
|
1046
|
-
getWaitTime(resource, priority =
|
|
1201
|
+
getWaitTime(resource, priority = 'background') {
|
|
1047
1202
|
return __async(this, null, function* () {
|
|
1048
1203
|
if (this.isDestroyed) {
|
|
1049
|
-
throw new Error(
|
|
1204
|
+
throw new Error('Rate limit store has been destroyed');
|
|
1050
1205
|
}
|
|
1051
1206
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
1052
1207
|
if (config.limit === 0) {
|
|
@@ -1059,20 +1214,22 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1059
1214
|
yield this.ensureActivityMetrics(resource);
|
|
1060
1215
|
const metrics = this.getOrCreateActivityMetrics(resource);
|
|
1061
1216
|
const capacity = this.calculateCurrentCapacity(resource, metrics);
|
|
1062
|
-
if (priority ===
|
|
1217
|
+
if (priority === 'background' && capacity.backgroundPaused) {
|
|
1063
1218
|
return this.capacityCalculator.config.recalculationIntervalMs;
|
|
1064
1219
|
}
|
|
1065
1220
|
const now = Date.now();
|
|
1066
1221
|
const windowStart = now - config.windowMs;
|
|
1067
|
-
const oldestResult = this.sqlite
|
|
1068
|
-
|
|
1222
|
+
const oldestResult = this.sqlite
|
|
1223
|
+
.prepare(
|
|
1224
|
+
`
|
|
1069
1225
|
SELECT timestamp
|
|
1070
1226
|
FROM rate_limits
|
|
1071
1227
|
WHERE resource = ? AND COALESCE(priority, 'background') = ? AND timestamp >= ?
|
|
1072
1228
|
ORDER BY timestamp
|
|
1073
1229
|
LIMIT 1
|
|
1074
|
-
|
|
1075
|
-
|
|
1230
|
+
`,
|
|
1231
|
+
)
|
|
1232
|
+
.get(resource, priority, windowStart);
|
|
1076
1233
|
if (!oldestResult) {
|
|
1077
1234
|
return 0;
|
|
1078
1235
|
}
|
|
@@ -1103,10 +1260,15 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1103
1260
|
return __async(this, null, function* () {
|
|
1104
1261
|
var _a;
|
|
1105
1262
|
if (this.isDestroyed) {
|
|
1106
|
-
throw new Error(
|
|
1107
|
-
}
|
|
1108
|
-
const totalResult = yield this.db
|
|
1109
|
-
|
|
1263
|
+
throw new Error('Rate limit store has been destroyed');
|
|
1264
|
+
}
|
|
1265
|
+
const totalResult = yield this.db
|
|
1266
|
+
.select({ count: drizzleOrm.count() })
|
|
1267
|
+
.from(rateLimitTable);
|
|
1268
|
+
const resourcesResult = yield this.db
|
|
1269
|
+
.select({ resource: rateLimitTable.resource })
|
|
1270
|
+
.from(rateLimitTable)
|
|
1271
|
+
.groupBy(rateLimitTable.resource);
|
|
1110
1272
|
const uniqueResources = resourcesResult.length;
|
|
1111
1273
|
const rateLimitedResources = [];
|
|
1112
1274
|
for (const { resource } of resourcesResult) {
|
|
@@ -1118,7 +1280,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1118
1280
|
return {
|
|
1119
1281
|
totalRequests: ((_a = totalResult[0]) == null ? void 0 : _a.count) || 0,
|
|
1120
1282
|
uniqueResources,
|
|
1121
|
-
rateLimitedResources
|
|
1283
|
+
rateLimitedResources,
|
|
1122
1284
|
};
|
|
1123
1285
|
});
|
|
1124
1286
|
}
|
|
@@ -1128,7 +1290,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1128
1290
|
clear() {
|
|
1129
1291
|
return __async(this, null, function* () {
|
|
1130
1292
|
if (this.isDestroyed) {
|
|
1131
|
-
throw new Error(
|
|
1293
|
+
throw new Error('Rate limit store has been destroyed');
|
|
1132
1294
|
}
|
|
1133
1295
|
yield this.db.delete(rateLimitTable);
|
|
1134
1296
|
this.activityMetrics.clear();
|
|
@@ -1142,7 +1304,10 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1142
1304
|
cleanup() {
|
|
1143
1305
|
return __async(this, null, function* () {
|
|
1144
1306
|
const now = Date.now();
|
|
1145
|
-
const resources = yield this.db
|
|
1307
|
+
const resources = yield this.db
|
|
1308
|
+
.select({ resource: rateLimitTable.resource })
|
|
1309
|
+
.from(rateLimitTable)
|
|
1310
|
+
.groupBy(rateLimitTable.resource);
|
|
1146
1311
|
for (const { resource } of resources) {
|
|
1147
1312
|
const config = this.resourceConfigs.get(resource) || this.defaultConfig;
|
|
1148
1313
|
const windowStart = now - config.windowMs;
|
|
@@ -1156,7 +1321,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1156
1321
|
close() {
|
|
1157
1322
|
return __async(this, null, function* () {
|
|
1158
1323
|
this.isDestroyed = true;
|
|
1159
|
-
if (this.isConnectionManaged && typeof this.sqlite.close ===
|
|
1324
|
+
if (this.isConnectionManaged && typeof this.sqlite.close === 'function') {
|
|
1160
1325
|
this.sqlite.close();
|
|
1161
1326
|
}
|
|
1162
1327
|
});
|
|
@@ -1170,15 +1335,18 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1170
1335
|
// Private helper methods for adaptive functionality
|
|
1171
1336
|
calculateCurrentCapacity(resource, metrics) {
|
|
1172
1337
|
const lastUpdate = this.lastCapacityUpdate.get(resource) || 0;
|
|
1173
|
-
const recalcInterval =
|
|
1338
|
+
const recalcInterval =
|
|
1339
|
+
this.capacityCalculator.config.recalculationIntervalMs;
|
|
1174
1340
|
if (Date.now() - lastUpdate < recalcInterval) {
|
|
1175
|
-
return
|
|
1341
|
+
return (
|
|
1342
|
+
this.cachedCapacity.get(resource) || this.getDefaultCapacity(resource)
|
|
1343
|
+
);
|
|
1176
1344
|
}
|
|
1177
1345
|
const totalLimit = this.getResourceLimit(resource);
|
|
1178
1346
|
const capacity = this.capacityCalculator.calculateDynamicCapacity(
|
|
1179
1347
|
resource,
|
|
1180
1348
|
totalLimit,
|
|
1181
|
-
metrics
|
|
1349
|
+
metrics,
|
|
1182
1350
|
);
|
|
1183
1351
|
this.cachedCapacity.set(resource, capacity);
|
|
1184
1352
|
this.lastCapacityUpdate.set(resource, Date.now());
|
|
@@ -1189,7 +1357,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1189
1357
|
this.activityMetrics.set(resource, {
|
|
1190
1358
|
recentUserRequests: [],
|
|
1191
1359
|
recentBackgroundRequests: [],
|
|
1192
|
-
userActivityTrend:
|
|
1360
|
+
userActivityTrend: 'none',
|
|
1193
1361
|
});
|
|
1194
1362
|
}
|
|
1195
1363
|
return this.activityMetrics.get(resource);
|
|
@@ -1200,30 +1368,34 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1200
1368
|
return;
|
|
1201
1369
|
}
|
|
1202
1370
|
const now = Date.now();
|
|
1203
|
-
const windowStart =
|
|
1204
|
-
|
|
1205
|
-
|
|
1371
|
+
const windowStart =
|
|
1372
|
+
now - this.capacityCalculator.config.monitoringWindowMs;
|
|
1373
|
+
const recentRequests = this.sqlite
|
|
1374
|
+
.prepare(
|
|
1375
|
+
`
|
|
1206
1376
|
SELECT timestamp, COALESCE(priority, 'background') as priority
|
|
1207
1377
|
FROM rate_limits
|
|
1208
1378
|
WHERE resource = ? AND timestamp >= ?
|
|
1209
1379
|
ORDER BY timestamp
|
|
1210
|
-
|
|
1211
|
-
|
|
1380
|
+
`,
|
|
1381
|
+
)
|
|
1382
|
+
.all(resource, windowStart);
|
|
1212
1383
|
const metrics = {
|
|
1213
1384
|
recentUserRequests: [],
|
|
1214
1385
|
recentBackgroundRequests: [],
|
|
1215
|
-
userActivityTrend:
|
|
1386
|
+
userActivityTrend: 'none',
|
|
1216
1387
|
};
|
|
1217
1388
|
for (const request of recentRequests) {
|
|
1218
|
-
if (request.priority ===
|
|
1389
|
+
if (request.priority === 'user') {
|
|
1219
1390
|
metrics.recentUserRequests.push(request.timestamp);
|
|
1220
1391
|
} else {
|
|
1221
1392
|
metrics.recentBackgroundRequests.push(request.timestamp);
|
|
1222
1393
|
}
|
|
1223
1394
|
}
|
|
1224
|
-
metrics.userActivityTrend =
|
|
1225
|
-
|
|
1226
|
-
|
|
1395
|
+
metrics.userActivityTrend =
|
|
1396
|
+
this.capacityCalculator.calculateActivityTrend(
|
|
1397
|
+
metrics.recentUserRequests,
|
|
1398
|
+
);
|
|
1227
1399
|
this.activityMetrics.set(resource, metrics);
|
|
1228
1400
|
});
|
|
1229
1401
|
}
|
|
@@ -1233,18 +1405,21 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1233
1405
|
const now = Date.now();
|
|
1234
1406
|
const windowStart = now - config.windowMs;
|
|
1235
1407
|
yield this.cleanupExpiredRequests(resource, windowStart);
|
|
1236
|
-
const result = this.sqlite
|
|
1237
|
-
|
|
1408
|
+
const result = this.sqlite
|
|
1409
|
+
.prepare(
|
|
1410
|
+
`
|
|
1238
1411
|
SELECT COUNT(*) as count
|
|
1239
1412
|
FROM rate_limits
|
|
1240
1413
|
WHERE resource = ? AND priority = ? AND timestamp >= ?
|
|
1241
|
-
|
|
1242
|
-
|
|
1414
|
+
`,
|
|
1415
|
+
)
|
|
1416
|
+
.get(resource, priority, windowStart);
|
|
1243
1417
|
return result.count || 0;
|
|
1244
1418
|
});
|
|
1245
1419
|
}
|
|
1246
1420
|
cleanupOldRequests(requests) {
|
|
1247
|
-
const cutoff =
|
|
1421
|
+
const cutoff =
|
|
1422
|
+
Date.now() - this.capacityCalculator.config.monitoringWindowMs;
|
|
1248
1423
|
while (requests.length > 0 && requests[0] < cutoff) {
|
|
1249
1424
|
requests.shift();
|
|
1250
1425
|
}
|
|
@@ -1259,17 +1434,19 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1259
1434
|
userReserved: Math.floor(limit * 0.3),
|
|
1260
1435
|
backgroundMax: Math.floor(limit * 0.7),
|
|
1261
1436
|
backgroundPaused: false,
|
|
1262
|
-
reason:
|
|
1437
|
+
reason: 'Default capacity allocation',
|
|
1263
1438
|
};
|
|
1264
1439
|
}
|
|
1265
1440
|
cleanupExpiredRequests(resource, windowStart) {
|
|
1266
1441
|
return __async(this, null, function* () {
|
|
1267
|
-
yield this.db
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
drizzleOrm.
|
|
1271
|
-
|
|
1272
|
-
|
|
1442
|
+
yield this.db
|
|
1443
|
+
.delete(rateLimitTable)
|
|
1444
|
+
.where(
|
|
1445
|
+
drizzleOrm.and(
|
|
1446
|
+
drizzleOrm.eq(rateLimitTable.resource, resource),
|
|
1447
|
+
drizzleOrm.lt(rateLimitTable.timestamp, windowStart),
|
|
1448
|
+
),
|
|
1449
|
+
);
|
|
1273
1450
|
});
|
|
1274
1451
|
}
|
|
1275
1452
|
initializeDatabase() {
|
|
@@ -1285,8 +1462,7 @@ var SqliteAdaptiveRateLimitStore = class {
|
|
|
1285
1462
|
this.db.run(drizzleOrm.sql`
|
|
1286
1463
|
ALTER TABLE rate_limits ADD COLUMN priority TEXT DEFAULT 'background'
|
|
1287
1464
|
`);
|
|
1288
|
-
} catch (e) {
|
|
1289
|
-
}
|
|
1465
|
+
} catch (e) {}
|
|
1290
1466
|
this.db.run(drizzleOrm.sql`
|
|
1291
1467
|
CREATE INDEX IF NOT EXISTS idx_rate_limit_resource ON rate_limits(resource)
|
|
1292
1468
|
`);
|
|
@@ -1308,4 +1484,4 @@ exports.cacheTable = cacheTable;
|
|
|
1308
1484
|
exports.dedupeTable = dedupeTable;
|
|
1309
1485
|
exports.rateLimitTable = rateLimitTable;
|
|
1310
1486
|
//# sourceMappingURL=index.cjs.map
|
|
1311
|
-
//# sourceMappingURL=index.cjs.map
|
|
1487
|
+
//# sourceMappingURL=index.cjs.map
|