@burdenoff/be-sdk 2026.527.1 → 2026.527.3
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/README.md +33 -0
- package/dist/chunk-7BMKOD7N.js +265 -0
- package/dist/chunk-7BMKOD7N.js.map +1 -0
- package/dist/chunk-PHKK7725.js +476 -0
- package/dist/chunk-PHKK7725.js.map +1 -0
- package/dist/index-BQ1yJn1K.d.ts +222 -0
- package/dist/index.d.ts +4 -222
- package/dist/index.js +6 -473
- package/dist/index.js.map +1 -1
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +5 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/resource-caps/index.d.ts +51 -0
- package/dist/resource-caps/index.js +5 -0
- package/dist/resource-caps/index.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { logger } from './chunk-CBZNP6RF.js';
|
|
2
|
+
import { __export, __name } from './chunk-XE4OXN2W.js';
|
|
3
|
+
|
|
4
|
+
// src/platform/index.ts
|
|
5
|
+
var platform_exports = {};
|
|
6
|
+
__export(platform_exports, {
|
|
7
|
+
CircuitBreakerOpenError: () => CircuitBreakerOpenError,
|
|
8
|
+
LLMRouter: () => LLMRouter,
|
|
9
|
+
__resetHttpClientBreakers: () => __resetHttpClientBreakers,
|
|
10
|
+
buildCacheKey: () => buildCacheKey,
|
|
11
|
+
buildLoaders: () => buildLoaders,
|
|
12
|
+
clampLimit: () => clampLimit,
|
|
13
|
+
createHttpClient: () => createHttpClient,
|
|
14
|
+
dropOldPartitions: () => dropOldPartitions,
|
|
15
|
+
ensureMonthlyPartitions: () => ensureMonthlyPartitions,
|
|
16
|
+
getNamespaceVersion: () => getNamespaceVersion,
|
|
17
|
+
httpClient: () => httpClient,
|
|
18
|
+
invalidateNamespace: () => invalidateNamespace,
|
|
19
|
+
monthlyPartitionName: () => monthlyPartitionName,
|
|
20
|
+
paginate: () => paginate,
|
|
21
|
+
rateLimitIncr: () => rateLimitIncr,
|
|
22
|
+
resolveReferenceLoader: () => resolveReferenceLoader,
|
|
23
|
+
withLeaderLock: () => withLeaderLock
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// src/platform/httpClient.ts
|
|
27
|
+
var breakers = /* @__PURE__ */ new Map();
|
|
28
|
+
function hostOf(url) {
|
|
29
|
+
try {
|
|
30
|
+
return new URL(url).host;
|
|
31
|
+
} catch {
|
|
32
|
+
return url;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
__name(hostOf, "hostOf");
|
|
36
|
+
function getBreaker(host) {
|
|
37
|
+
let b = breakers.get(host);
|
|
38
|
+
if (!b) {
|
|
39
|
+
b = {
|
|
40
|
+
failures: 0,
|
|
41
|
+
openedAt: null,
|
|
42
|
+
state: "CLOSED"
|
|
43
|
+
};
|
|
44
|
+
breakers.set(host, b);
|
|
45
|
+
}
|
|
46
|
+
return b;
|
|
47
|
+
}
|
|
48
|
+
__name(getBreaker, "getBreaker");
|
|
49
|
+
function defaultRetryOn(status) {
|
|
50
|
+
return status === 408 || status === 425 || status === 429 || status >= 500 && status <= 599;
|
|
51
|
+
}
|
|
52
|
+
__name(defaultRetryOn, "defaultRetryOn");
|
|
53
|
+
function jitter(delay) {
|
|
54
|
+
return Math.floor(Math.random() * delay);
|
|
55
|
+
}
|
|
56
|
+
__name(jitter, "jitter");
|
|
57
|
+
function sleep(ms) {
|
|
58
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
59
|
+
}
|
|
60
|
+
__name(sleep, "sleep");
|
|
61
|
+
var CircuitBreakerOpenError = class extends Error {
|
|
62
|
+
static {
|
|
63
|
+
__name(this, "CircuitBreakerOpenError");
|
|
64
|
+
}
|
|
65
|
+
constructor(host) {
|
|
66
|
+
super(`circuit breaker OPEN for host=${host}`);
|
|
67
|
+
this.name = "CircuitBreakerOpenError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function __resetHttpClientBreakers() {
|
|
71
|
+
breakers.clear();
|
|
72
|
+
}
|
|
73
|
+
__name(__resetHttpClientBreakers, "__resetHttpClientBreakers");
|
|
74
|
+
async function executeOnce(url, init, timeoutMs) {
|
|
75
|
+
const signal = init.signal ?? AbortSignal.timeout(timeoutMs);
|
|
76
|
+
return fetch(url, {
|
|
77
|
+
...init,
|
|
78
|
+
signal
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
__name(executeOnce, "executeOnce");
|
|
82
|
+
async function httpClient(url, init = {}, config = {}) {
|
|
83
|
+
const fullUrl = config.baseUrl && !/^https?:\/\//i.test(url) ? `${config.baseUrl.replace(/\/$/, "")}/${url.replace(/^\//, "")}` : url;
|
|
84
|
+
const host = hostOf(fullUrl);
|
|
85
|
+
const breaker = getBreaker(host);
|
|
86
|
+
const breakerThreshold = config.breaker?.threshold ?? 5;
|
|
87
|
+
const cooldownMs = config.breaker?.cooldownMs ?? 3e4;
|
|
88
|
+
if (breaker.state === "OPEN") {
|
|
89
|
+
if (breaker.openedAt && Date.now() - breaker.openedAt >= cooldownMs) {
|
|
90
|
+
breaker.state = "HALF_OPEN";
|
|
91
|
+
logger.info("httpClient.breaker.halfOpen", {
|
|
92
|
+
host
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
throw new CircuitBreakerOpenError(host);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const attempts = init.retry?.attempts ?? config.retry?.attempts ?? 3;
|
|
99
|
+
const initialDelay = init.retry?.initialDelayMs ?? config.retry?.initialDelayMs ?? 100;
|
|
100
|
+
const maxDelay = init.retry?.maxDelayMs ?? config.retry?.maxDelayMs ?? 2e3;
|
|
101
|
+
const retryOn = init.retry?.retryOn ?? config.retry?.retryOn ?? defaultRetryOn;
|
|
102
|
+
const timeoutMs = init.timeoutMs ?? config.defaultTimeoutMs ?? 5e3;
|
|
103
|
+
const headers = {
|
|
104
|
+
...config.defaultHeaders ?? {},
|
|
105
|
+
...init.headers ?? {}
|
|
106
|
+
};
|
|
107
|
+
let lastErr;
|
|
108
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
109
|
+
try {
|
|
110
|
+
const response = await executeOnce(fullUrl, {
|
|
111
|
+
...init,
|
|
112
|
+
headers
|
|
113
|
+
}, timeoutMs);
|
|
114
|
+
if (response.status >= 500) {
|
|
115
|
+
breaker.failures += 1;
|
|
116
|
+
if (breaker.failures >= breakerThreshold) {
|
|
117
|
+
breaker.state = "OPEN";
|
|
118
|
+
breaker.openedAt = Date.now();
|
|
119
|
+
logger.warn("httpClient.breaker.open", {
|
|
120
|
+
host,
|
|
121
|
+
failures: breaker.failures
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (attempt < attempts && retryOn(response.status)) {
|
|
125
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
126
|
+
await sleep(jitter(delay));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
return response;
|
|
130
|
+
}
|
|
131
|
+
breaker.failures = 0;
|
|
132
|
+
if (breaker.state === "HALF_OPEN") {
|
|
133
|
+
breaker.state = "CLOSED";
|
|
134
|
+
breaker.openedAt = null;
|
|
135
|
+
logger.info("httpClient.breaker.closed", {
|
|
136
|
+
host
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (attempt < attempts && retryOn(response.status)) {
|
|
140
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
141
|
+
await sleep(jitter(delay));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
return response;
|
|
145
|
+
} catch (err) {
|
|
146
|
+
lastErr = err;
|
|
147
|
+
breaker.failures += 1;
|
|
148
|
+
if (breaker.failures >= breakerThreshold) {
|
|
149
|
+
breaker.state = "OPEN";
|
|
150
|
+
breaker.openedAt = Date.now();
|
|
151
|
+
logger.warn("httpClient.breaker.open", {
|
|
152
|
+
host,
|
|
153
|
+
failures: breaker.failures
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (attempt >= attempts) break;
|
|
157
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
158
|
+
await sleep(jitter(delay));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
logger.error("httpClient.exhausted", {
|
|
162
|
+
url: fullUrl,
|
|
163
|
+
attempts,
|
|
164
|
+
host
|
|
165
|
+
});
|
|
166
|
+
throw lastErr instanceof Error ? lastErr : new Error(`httpClient failed: ${String(lastErr)}`);
|
|
167
|
+
}
|
|
168
|
+
__name(httpClient, "httpClient");
|
|
169
|
+
function createHttpClient(config = {}) {
|
|
170
|
+
return (url, init = {}) => httpClient(url, init, config);
|
|
171
|
+
}
|
|
172
|
+
__name(createHttpClient, "createHttpClient");
|
|
173
|
+
|
|
174
|
+
// src/platform/leaderLock.ts
|
|
175
|
+
var RELEASE_LUA = `
|
|
176
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
177
|
+
return redis.call("del", KEYS[1])
|
|
178
|
+
else
|
|
179
|
+
return 0
|
|
180
|
+
end
|
|
181
|
+
`;
|
|
182
|
+
var RENEW_LUA = `
|
|
183
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
184
|
+
return redis.call("pexpire", KEYS[1], ARGV[2])
|
|
185
|
+
else
|
|
186
|
+
return 0
|
|
187
|
+
end
|
|
188
|
+
`;
|
|
189
|
+
function generateToken() {
|
|
190
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}-${process.pid}`;
|
|
191
|
+
}
|
|
192
|
+
__name(generateToken, "generateToken");
|
|
193
|
+
async function withLeaderLock(redis, name, fn, options) {
|
|
194
|
+
const key = `besdk:leader:${name}`;
|
|
195
|
+
const token = generateToken();
|
|
196
|
+
const ttlMs = options.ttlMs;
|
|
197
|
+
const renewMs = options.renewMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
|
|
198
|
+
const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1e3));
|
|
199
|
+
const acquired = await redis.set(key, token, "EX", ttlSeconds, "NX");
|
|
200
|
+
if (acquired !== "OK") {
|
|
201
|
+
return {
|
|
202
|
+
acquired: false
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
logger.info("leaderLock.acquired", {
|
|
206
|
+
name,
|
|
207
|
+
token,
|
|
208
|
+
ttlMs
|
|
209
|
+
});
|
|
210
|
+
let stopped = false;
|
|
211
|
+
const renewTimer = setInterval(() => {
|
|
212
|
+
if (stopped) return;
|
|
213
|
+
redis.eval(RENEW_LUA, 1, key, token, String(ttlMs)).catch((err) => {
|
|
214
|
+
logger.warn("leaderLock.renew.failed", {
|
|
215
|
+
name,
|
|
216
|
+
err: String(err)
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}, renewMs);
|
|
220
|
+
if (typeof renewTimer.unref === "function") {
|
|
221
|
+
renewTimer.unref();
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const result = await fn();
|
|
225
|
+
return {
|
|
226
|
+
acquired: true,
|
|
227
|
+
result
|
|
228
|
+
};
|
|
229
|
+
} finally {
|
|
230
|
+
stopped = true;
|
|
231
|
+
clearInterval(renewTimer);
|
|
232
|
+
try {
|
|
233
|
+
await redis.eval(RELEASE_LUA, 1, key, token);
|
|
234
|
+
logger.info("leaderLock.released", {
|
|
235
|
+
name,
|
|
236
|
+
token
|
|
237
|
+
});
|
|
238
|
+
} catch (err) {
|
|
239
|
+
logger.warn("leaderLock.release.failed", {
|
|
240
|
+
name,
|
|
241
|
+
err: String(err)
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
__name(withLeaderLock, "withLeaderLock");
|
|
247
|
+
|
|
248
|
+
// src/platform/rateLimit.ts
|
|
249
|
+
async function rateLimitIncr(redis, input) {
|
|
250
|
+
const fullKey = `besdk:rl:${input.key}`;
|
|
251
|
+
const count = await redis.incr(fullKey);
|
|
252
|
+
if (count === 1) {
|
|
253
|
+
await redis.pexpire(fullKey, input.windowMs);
|
|
254
|
+
}
|
|
255
|
+
let ttl = -1;
|
|
256
|
+
try {
|
|
257
|
+
ttl = await redis.pttl(fullKey);
|
|
258
|
+
} catch {
|
|
259
|
+
ttl = input.windowMs;
|
|
260
|
+
}
|
|
261
|
+
const resetAt = Date.now() + (ttl > 0 ? ttl : input.windowMs);
|
|
262
|
+
const remaining = Math.max(0, input.limit - count);
|
|
263
|
+
return {
|
|
264
|
+
allowed: count <= input.limit,
|
|
265
|
+
count,
|
|
266
|
+
remaining,
|
|
267
|
+
resetAt
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
__name(rateLimitIncr, "rateLimitIncr");
|
|
271
|
+
|
|
272
|
+
// src/platform/paginator.ts
|
|
273
|
+
function clampLimit(input, opts = {}) {
|
|
274
|
+
const def = opts.defaultLimit ?? 50;
|
|
275
|
+
const max = opts.maxLimit ?? 200;
|
|
276
|
+
const min = opts.minLimit ?? 1;
|
|
277
|
+
if (input == null || Number.isNaN(input)) return def;
|
|
278
|
+
return Math.min(Math.max(Math.floor(input), min), max);
|
|
279
|
+
}
|
|
280
|
+
__name(clampLimit, "clampLimit");
|
|
281
|
+
async function paginate(model, input, opts = {}) {
|
|
282
|
+
const take = clampLimit(input.take ?? void 0, opts);
|
|
283
|
+
const items = await model.findMany({
|
|
284
|
+
take: take + 1,
|
|
285
|
+
skip: input.cursor ? 1 : 0,
|
|
286
|
+
cursor: input.cursor ? {
|
|
287
|
+
id: input.cursor
|
|
288
|
+
} : void 0,
|
|
289
|
+
where: input.where,
|
|
290
|
+
orderBy: input.orderBy
|
|
291
|
+
});
|
|
292
|
+
const hasMore = items.length > take;
|
|
293
|
+
const trimmed = hasMore ? items.slice(0, take) : items;
|
|
294
|
+
const nextCursor = hasMore && trimmed.length > 0 ? trimmed[trimmed.length - 1].id : null;
|
|
295
|
+
return {
|
|
296
|
+
items: trimmed,
|
|
297
|
+
hasMore,
|
|
298
|
+
nextCursor
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
__name(paginate, "paginate");
|
|
302
|
+
|
|
303
|
+
// src/platform/dataLoaderFactory.ts
|
|
304
|
+
function buildLoaders(ctx, builder) {
|
|
305
|
+
return builder(ctx);
|
|
306
|
+
}
|
|
307
|
+
__name(buildLoaders, "buildLoaders");
|
|
308
|
+
function resolveReferenceLoader(loaderName, keyField = "id") {
|
|
309
|
+
return async (reference, ctx) => {
|
|
310
|
+
const loader = ctx.loaders[loaderName];
|
|
311
|
+
if (!loader) {
|
|
312
|
+
throw new Error(`resolveReferenceLoader: loader "${loaderName}" not found on ctx.loaders`);
|
|
313
|
+
}
|
|
314
|
+
const key = reference[keyField];
|
|
315
|
+
if (typeof key !== "string") {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
return loader.load(key);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
__name(resolveReferenceLoader, "resolveReferenceLoader");
|
|
322
|
+
|
|
323
|
+
// src/platform/cacheInvalidation.ts
|
|
324
|
+
var VERSION_PREFIX = "besdk:cv:";
|
|
325
|
+
async function getNamespaceVersion(redis, namespace) {
|
|
326
|
+
const v = await redis.get(`${VERSION_PREFIX}${namespace}`);
|
|
327
|
+
if (!v) return 1;
|
|
328
|
+
const n = Number.parseInt(v, 10);
|
|
329
|
+
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
330
|
+
}
|
|
331
|
+
__name(getNamespaceVersion, "getNamespaceVersion");
|
|
332
|
+
async function buildCacheKey(redis, namespace, ...parts) {
|
|
333
|
+
const ver = await getNamespaceVersion(redis, namespace);
|
|
334
|
+
return `${namespace}:v${ver}:${parts.join(":")}`;
|
|
335
|
+
}
|
|
336
|
+
__name(buildCacheKey, "buildCacheKey");
|
|
337
|
+
async function invalidateNamespace(redis, namespace) {
|
|
338
|
+
return redis.incr(`${VERSION_PREFIX}${namespace}`);
|
|
339
|
+
}
|
|
340
|
+
__name(invalidateNamespace, "invalidateNamespace");
|
|
341
|
+
|
|
342
|
+
// src/platform/pgPartition.ts
|
|
343
|
+
function pad2(n) {
|
|
344
|
+
return n < 10 ? `0${n}` : `${n}`;
|
|
345
|
+
}
|
|
346
|
+
__name(pad2, "pad2");
|
|
347
|
+
function monthlyPartitionName(table, year, month) {
|
|
348
|
+
return `${table}_y${year}m${pad2(month)}`;
|
|
349
|
+
}
|
|
350
|
+
__name(monthlyPartitionName, "monthlyPartitionName");
|
|
351
|
+
async function ensureMonthlyPartitions(prisma, opts) {
|
|
352
|
+
const created = [];
|
|
353
|
+
const cur = new Date(Date.UTC(opts.from.getUTCFullYear(), opts.from.getUTCMonth(), 1));
|
|
354
|
+
const end = new Date(Date.UTC(opts.to.getUTCFullYear(), opts.to.getUTCMonth(), 1));
|
|
355
|
+
while (cur <= end) {
|
|
356
|
+
const y = cur.getUTCFullYear();
|
|
357
|
+
const m = cur.getUTCMonth() + 1;
|
|
358
|
+
const next = new Date(Date.UTC(y, m, 1));
|
|
359
|
+
const partition = monthlyPartitionName(opts.table, y, m);
|
|
360
|
+
const lower = `${y}-${pad2(m)}-01`;
|
|
361
|
+
const upper = `${next.getUTCFullYear()}-${pad2(next.getUTCMonth() + 1)}-01`;
|
|
362
|
+
await prisma.$executeRawUnsafe(`CREATE TABLE IF NOT EXISTS "${partition}" PARTITION OF "${opts.table}" FOR VALUES FROM ('${lower}') TO ('${upper}');`);
|
|
363
|
+
created.push(partition);
|
|
364
|
+
cur.setUTCMonth(cur.getUTCMonth() + 1);
|
|
365
|
+
}
|
|
366
|
+
return created;
|
|
367
|
+
}
|
|
368
|
+
__name(ensureMonthlyPartitions, "ensureMonthlyPartitions");
|
|
369
|
+
async function dropOldPartitions(prisma, opts) {
|
|
370
|
+
const rows = await prisma.$queryRawUnsafe(`SELECT child.relname AS partition_name
|
|
371
|
+
FROM pg_inherits
|
|
372
|
+
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
|
373
|
+
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
|
374
|
+
WHERE parent.relname = $1`, opts.table);
|
|
375
|
+
const dropped = [];
|
|
376
|
+
const cutoffYear = opts.beforeDate.getUTCFullYear();
|
|
377
|
+
const cutoffMonth = opts.beforeDate.getUTCMonth() + 1;
|
|
378
|
+
for (const row of rows) {
|
|
379
|
+
const partitionRe = /_y(\d{4})m(\d{2})$/;
|
|
380
|
+
const m = partitionRe.exec(row.partition_name);
|
|
381
|
+
if (!m) continue;
|
|
382
|
+
const y = Number(m[1]);
|
|
383
|
+
const mo = Number(m[2]);
|
|
384
|
+
if (y < cutoffYear || y === cutoffYear && mo < cutoffMonth) {
|
|
385
|
+
await prisma.$executeRawUnsafe(`DROP TABLE IF EXISTS "${row.partition_name}";`);
|
|
386
|
+
dropped.push(row.partition_name);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return dropped;
|
|
390
|
+
}
|
|
391
|
+
__name(dropOldPartitions, "dropOldPartitions");
|
|
392
|
+
|
|
393
|
+
// src/platform/llmClient.ts
|
|
394
|
+
var DEFAULTS = {
|
|
395
|
+
timeoutMs: 3e4,
|
|
396
|
+
maxAttempts: 3,
|
|
397
|
+
initialDelayMs: 500,
|
|
398
|
+
maxOutputTokensCap: 4096
|
|
399
|
+
};
|
|
400
|
+
function jitter2(n) {
|
|
401
|
+
return Math.floor(Math.random() * n);
|
|
402
|
+
}
|
|
403
|
+
__name(jitter2, "jitter");
|
|
404
|
+
function sleep2(ms) {
|
|
405
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
406
|
+
}
|
|
407
|
+
__name(sleep2, "sleep");
|
|
408
|
+
var LLMRouter = class {
|
|
409
|
+
static {
|
|
410
|
+
__name(this, "LLMRouter");
|
|
411
|
+
}
|
|
412
|
+
adapters = /* @__PURE__ */ new Map();
|
|
413
|
+
opts;
|
|
414
|
+
constructor(opts = {}) {
|
|
415
|
+
this.opts = {
|
|
416
|
+
...DEFAULTS,
|
|
417
|
+
...opts
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
register(adapter) {
|
|
421
|
+
this.adapters.set(adapter.name, adapter);
|
|
422
|
+
}
|
|
423
|
+
resolveChain() {
|
|
424
|
+
const envChain = process.env["LLM_PROVIDER_CHAIN"]?.trim();
|
|
425
|
+
if (envChain) {
|
|
426
|
+
return envChain.split(",").map((s) => s.trim()).filter(Boolean);
|
|
427
|
+
}
|
|
428
|
+
return Array.from(this.adapters.keys());
|
|
429
|
+
}
|
|
430
|
+
async complete(req) {
|
|
431
|
+
const cappedReq = {
|
|
432
|
+
...req,
|
|
433
|
+
maxTokens: Math.min(req.maxTokens ?? this.opts.maxOutputTokensCap, this.opts.maxOutputTokensCap)
|
|
434
|
+
};
|
|
435
|
+
const chain = this.resolveChain();
|
|
436
|
+
if (chain.length === 0) {
|
|
437
|
+
throw new Error("LLMRouter: no providers registered");
|
|
438
|
+
}
|
|
439
|
+
let lastErr = null;
|
|
440
|
+
for (let attempt = 0; attempt < this.opts.maxAttempts; attempt++) {
|
|
441
|
+
const providerName = chain[attempt % chain.length];
|
|
442
|
+
const adapter = providerName ? this.adapters.get(providerName) : void 0;
|
|
443
|
+
if (!adapter) {
|
|
444
|
+
logger.warn("llmRouter.provider.notRegistered", {
|
|
445
|
+
providerName
|
|
446
|
+
});
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const timeoutMs = req.timeoutMs ?? this.opts.timeoutMs;
|
|
450
|
+
const ctrl = new AbortController();
|
|
451
|
+
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
452
|
+
try {
|
|
453
|
+
const response = await adapter.complete(cappedReq, ctrl.signal);
|
|
454
|
+
return response;
|
|
455
|
+
} catch (err) {
|
|
456
|
+
lastErr = err;
|
|
457
|
+
logger.warn("llmRouter.attempt.failed", {
|
|
458
|
+
provider: adapter.name,
|
|
459
|
+
attempt,
|
|
460
|
+
err: String(err)
|
|
461
|
+
});
|
|
462
|
+
if (attempt < this.opts.maxAttempts - 1) {
|
|
463
|
+
const delay = this.opts.initialDelayMs * 2 ** attempt;
|
|
464
|
+
await sleep2(jitter2(delay));
|
|
465
|
+
}
|
|
466
|
+
} finally {
|
|
467
|
+
clearTimeout(t);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
throw lastErr instanceof Error ? lastErr : new Error(`LLMRouter exhausted ${this.opts.maxAttempts} attempts`);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
export { CircuitBreakerOpenError, LLMRouter, __resetHttpClientBreakers, buildCacheKey, buildLoaders, clampLimit, createHttpClient, dropOldPartitions, ensureMonthlyPartitions, getNamespaceVersion, httpClient, invalidateNamespace, monthlyPartitionName, paginate, platform_exports, rateLimitIncr, resolveReferenceLoader, withLeaderLock };
|
|
475
|
+
//# sourceMappingURL=chunk-PHKK7725.js.map
|
|
476
|
+
//# sourceMappingURL=chunk-PHKK7725.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform/index.ts","../src/platform/httpClient.ts","../src/platform/leaderLock.ts","../src/platform/rateLimit.ts","../src/platform/paginator.ts","../src/platform/dataLoaderFactory.ts","../src/platform/cacheInvalidation.ts","../src/platform/pgPartition.ts","../src/platform/llmClient.ts"],"names":["breakers","Map","hostOf","url","URL","host","getBreaker","b","get","failures","openedAt","state","set","defaultRetryOn","status","jitter","delay","Math","floor","random","sleep","ms","Promise","r","setTimeout","CircuitBreakerOpenError","Error","name","__resetHttpClientBreakers","clear","executeOnce","init","timeoutMs","signal","AbortSignal","timeout","fetch","httpClient","config","fullUrl","baseUrl","test","replace","breaker","breakerThreshold","threshold","cooldownMs","Date","now","logger","info","attempts","retry","initialDelay","initialDelayMs","maxDelay","maxDelayMs","retryOn","defaultTimeoutMs","headers","defaultHeaders","lastErr","attempt","response","warn","min","err","error","String","createHttpClient","RELEASE_LUA","RENEW_LUA","generateToken","toString","slice","process","pid","withLeaderLock","redis","fn","options","key","token","ttlMs","renewMs","max","ttlSeconds","ceil","acquired","stopped","renewTimer","setInterval","eval","catch","unref","result","clearInterval","rateLimitIncr","input","fullKey","count","incr","pexpire","windowMs","ttl","pttl","resetAt","remaining","limit","allowed","clampLimit","opts","def","defaultLimit","maxLimit","minLimit","Number","isNaN","paginate","model","take","undefined","items","findMany","skip","cursor","id","where","orderBy","hasMore","length","trimmed","nextCursor","buildLoaders","ctx","builder","resolveReferenceLoader","loaderName","keyField","reference","loader","loaders","load","VERSION_PREFIX","getNamespaceVersion","namespace","v","n","parseInt","isFinite","buildCacheKey","parts","ver","join","invalidateNamespace","pad2","monthlyPartitionName","table","year","month","ensureMonthlyPartitions","prisma","created","cur","UTC","from","getUTCFullYear","getUTCMonth","end","to","y","m","next","partition","lower","upper","$executeRawUnsafe","push","setUTCMonth","dropOldPartitions","rows","$queryRawUnsafe","dropped","cutoffYear","beforeDate","cutoffMonth","row","partitionRe","exec","partition_name","mo","DEFAULTS","maxAttempts","maxOutputTokensCap","LLMRouter","adapters","register","adapter","resolveChain","envChain","env","trim","split","map","s","filter","Boolean","Array","keys","complete","req","cappedReq","maxTokens","chain","providerName","ctrl","AbortController","t","abort","provider","clearTimeout"],"mappings":"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","file":"chunk-PHKK7725.js","sourcesContent":["/**\n * @burdenoff/be-sdk/platform\n *\n * Reusable platform primitives consumed fleet-wide by Burdenoff svcs.\n */\nexport { httpClient, createHttpClient, CircuitBreakerOpenError, __resetHttpClientBreakers } from './httpClient.js';\nexport type { HttpClientConfig, HttpClientInit, RetryConfig, BreakerConfig } from './httpClient.js';\n\nexport { withLeaderLock } from './leaderLock.js';\nexport type { LeaderLockRedis, LeaderLockOptions, LeaderLockResult } from './leaderLock.js';\n\nexport { rateLimitIncr } from './rateLimit.js';\nexport type { RateLimitRedis, RateLimitInput, RateLimitResult } from './rateLimit.js';\n\nexport { clampLimit, paginate } from './paginator.js';\nexport type { ClampLimitOptions, PaginateInput, PaginateResult, PrismaModelLike } from './paginator.js';\n\nexport { buildLoaders, resolveReferenceLoader } from './dataLoaderFactory.js';\nexport type { DataLoaderLike, LoaderMap, LoaderBuilder, ReferenceObject } from './dataLoaderFactory.js';\n\nexport { buildCacheKey, getNamespaceVersion, invalidateNamespace } from './cacheInvalidation.js';\nexport type { CacheVersionRedis } from './cacheInvalidation.js';\n\nexport { monthlyPartitionName, ensureMonthlyPartitions, dropOldPartitions } from './pgPartition.js';\nexport type { PrismaRawClient, EnsureMonthlyOptions, DropOldOptions } from './pgPartition.js';\n\nexport { LLMRouter } from './llmClient.js';\nexport type { LLMRequest, LLMResponse, LLMProviderAdapter, LLMRouterOptions } from './llmClient.js';\n","/**\n * httpClient — hardened wrapper around global `fetch` with:\n * - per-request AbortSignal.timeout (default 5s)\n * - retries (default 3) with exponential backoff + full jitter\n * - per-host circuit breaker (opens at 5 consecutive 5xx, half-open after 30s)\n * - structured logging via existing be-sdk logger\n *\n * No required upstream dependency — we use the runtime `fetch` so the same\n * code path works on Bun and Node 20+. Consumers may pass an undici Agent\n * via `init.dispatcher` if they need keep-alive pooling; we forward unknown\n * init fields through.\n */\n\nimport { logger } from '../logger/index.js';\n\nexport interface RetryConfig {\n /** Total attempts including the first (default 3) */\n attempts?: number;\n /** Initial backoff in ms (default 100) */\n initialDelayMs?: number;\n /** Maximum backoff in ms (default 2000) */\n maxDelayMs?: number;\n /** Status codes that trigger a retry (default 408, 425, 429, 5xx) */\n retryOn?: (status: number) => boolean;\n}\n\nexport interface BreakerConfig {\n /** Consecutive 5xx failures to open breaker (default 5) */\n threshold?: number;\n /** Time in ms before HALF_OPEN trial (default 30000) */\n cooldownMs?: number;\n}\n\nexport interface HttpClientConfig {\n baseUrl?: string;\n defaultTimeoutMs?: number;\n retry?: RetryConfig;\n breaker?: BreakerConfig;\n /** Default headers merged into every request */\n defaultHeaders?: Record<string, string>;\n}\n\ninterface BreakerState {\n failures: number;\n openedAt: number | null;\n state: 'CLOSED' | 'OPEN' | 'HALF_OPEN';\n}\n\nconst breakers = new Map<string, BreakerState>();\n\nfunction hostOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return url;\n }\n}\n\nfunction getBreaker(host: string): BreakerState {\n let b = breakers.get(host);\n if (!b) {\n b = { failures: 0, openedAt: null, state: 'CLOSED' };\n breakers.set(host, b);\n }\n return b;\n}\n\nfunction defaultRetryOn(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);\n}\n\nfunction jitter(delay: number): number {\n return Math.floor(Math.random() * delay);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport class CircuitBreakerOpenError extends Error {\n constructor(host: string) {\n super(`circuit breaker OPEN for host=${host}`);\n this.name = 'CircuitBreakerOpenError';\n }\n}\n\nexport interface HttpClientInit extends RequestInit {\n timeoutMs?: number;\n retry?: RetryConfig;\n}\n\n/**\n * Reset all breaker state (test helper).\n */\nexport function __resetHttpClientBreakers(): void {\n breakers.clear();\n}\n\nasync function executeOnce(url: string, init: HttpClientInit, timeoutMs: number): Promise<Response> {\n const signal = init.signal ?? AbortSignal.timeout(timeoutMs);\n return fetch(url, { ...init, signal });\n}\n\nexport async function httpClient(\n url: string,\n init: HttpClientInit = {},\n config: HttpClientConfig = {}\n): Promise<Response> {\n const fullUrl =\n config.baseUrl && !/^https?:\\/\\//i.test(url)\n ? `${config.baseUrl.replace(/\\/$/, '')}/${url.replace(/^\\//, '')}`\n : url;\n const host = hostOf(fullUrl);\n const breaker = getBreaker(host);\n const breakerThreshold = config.breaker?.threshold ?? 5;\n const cooldownMs = config.breaker?.cooldownMs ?? 30_000;\n\n if (breaker.state === 'OPEN') {\n if (breaker.openedAt && Date.now() - breaker.openedAt >= cooldownMs) {\n breaker.state = 'HALF_OPEN';\n logger.info('httpClient.breaker.halfOpen', { host });\n } else {\n throw new CircuitBreakerOpenError(host);\n }\n }\n\n const attempts = init.retry?.attempts ?? config.retry?.attempts ?? 3;\n const initialDelay = init.retry?.initialDelayMs ?? config.retry?.initialDelayMs ?? 100;\n const maxDelay = init.retry?.maxDelayMs ?? config.retry?.maxDelayMs ?? 2_000;\n const retryOn = init.retry?.retryOn ?? config.retry?.retryOn ?? defaultRetryOn;\n const timeoutMs = init.timeoutMs ?? config.defaultTimeoutMs ?? 5_000;\n\n const headers = { ...(config.defaultHeaders ?? {}), ...((init.headers as Record<string, string> | undefined) ?? {}) };\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n const response = await executeOnce(fullUrl, { ...init, headers }, timeoutMs);\n if (response.status >= 500) {\n breaker.failures += 1;\n if (breaker.failures >= breakerThreshold) {\n breaker.state = 'OPEN';\n breaker.openedAt = Date.now();\n logger.warn('httpClient.breaker.open', { host, failures: breaker.failures });\n }\n if (attempt < attempts && retryOn(response.status)) {\n const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);\n await sleep(jitter(delay));\n continue;\n }\n return response;\n }\n // success path\n breaker.failures = 0;\n if (breaker.state === 'HALF_OPEN') {\n breaker.state = 'CLOSED';\n breaker.openedAt = null;\n logger.info('httpClient.breaker.closed', { host });\n }\n if (attempt < attempts && retryOn(response.status)) {\n const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);\n await sleep(jitter(delay));\n continue;\n }\n return response;\n } catch (err) {\n lastErr = err;\n breaker.failures += 1;\n if (breaker.failures >= breakerThreshold) {\n breaker.state = 'OPEN';\n breaker.openedAt = Date.now();\n logger.warn('httpClient.breaker.open', { host, failures: breaker.failures });\n }\n if (attempt >= attempts) break;\n const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);\n await sleep(jitter(delay));\n }\n }\n\n logger.error('httpClient.exhausted', { url: fullUrl, attempts, host });\n throw lastErr instanceof Error ? lastErr : new Error(`httpClient failed: ${String(lastErr)}`);\n}\n\nexport function createHttpClient(\n config: HttpClientConfig = {}\n): (url: string, init?: HttpClientInit) => Promise<Response> {\n return (url: string, init: HttpClientInit = {}) => httpClient(url, init, config);\n}\n","/**\n * withLeaderLock — Redis-based leader election.\n *\n * Acquires `SET NX EX` on `besdk:leader:{name}`. If acquired, runs fn() and\n * auto-renews the lease every `renewMs` (default ttlMs/3). On exit (success\n * or throw) the holder DELs the key only if it still owns it (token check\n * via a Redis LUA script — Redis-side, NOT JS eval).\n * If the lock cannot be acquired, fn() is NOT called and the function\n * resolves with `{ acquired: false }`.\n *\n * Redis client is duck-typed (ioredis-compatible) so consumers can pass\n * either ioredis or the be-sdk cache provider's underlying client.\n */\n\nimport { logger } from '../logger/index.js';\n\nexport interface LeaderLockRedis {\n /** ioredis: set(key, val, 'EX', seconds, 'NX') -> 'OK' | null */\n set(key: string, value: string, ex: 'EX', seconds: number, nx: 'NX'): Promise<'OK' | null>;\n /** Redis LUA EVAL for compare-and-delete / compare-and-renew (server-side script) */\n eval(script: string, numKeys: number, ...args: string[]): Promise<unknown>;\n}\n\nexport interface LeaderLockOptions {\n ttlMs: number;\n renewMs?: number;\n}\n\nexport interface LeaderLockResult<T> {\n acquired: boolean;\n result?: T;\n}\n\nconst RELEASE_LUA = `\nif redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\nelse\n return 0\nend\n`;\n\nconst RENEW_LUA = `\nif redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"pexpire\", KEYS[1], ARGV[2])\nelse\n return 0\nend\n`;\n\nfunction generateToken(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2)}-${process.pid}`;\n}\n\nexport async function withLeaderLock<T>(\n redis: LeaderLockRedis,\n name: string,\n fn: () => Promise<T>,\n options: LeaderLockOptions\n): Promise<LeaderLockResult<T>> {\n const key = `besdk:leader:${name}`;\n const token = generateToken();\n const ttlMs = options.ttlMs;\n const renewMs = options.renewMs ?? Math.max(1_000, Math.floor(ttlMs / 3));\n const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1000));\n\n const acquired = await redis.set(key, token, 'EX', ttlSeconds, 'NX');\n if (acquired !== 'OK') {\n return { acquired: false };\n }\n\n logger.info('leaderLock.acquired', { name, token, ttlMs });\n\n let stopped = false;\n const renewTimer = setInterval(() => {\n if (stopped) return;\n redis.eval(RENEW_LUA, 1, key, token, String(ttlMs)).catch((err: unknown) => {\n logger.warn('leaderLock.renew.failed', { name, err: String(err) });\n });\n }, renewMs);\n // Don't keep the process alive just for this timer.\n if (typeof (renewTimer as unknown as { unref?: () => void }).unref === 'function') {\n (renewTimer as unknown as { unref: () => void }).unref();\n }\n\n try {\n const result = await fn();\n return { acquired: true, result };\n } finally {\n stopped = true;\n clearInterval(renewTimer);\n try {\n await redis.eval(RELEASE_LUA, 1, key, token);\n logger.info('leaderLock.released', { name, token });\n } catch (err) {\n logger.warn('leaderLock.release.failed', { name, err: String(err) });\n }\n }\n}\n","/**\n * rateLimitIncr — atomic INCR-based rate limit using EXPIRE NX.\n *\n * Strategy:\n * 1. INCR key\n * 2. If returned value === 1 (i.e. first increment), EXPIRE key with windowMs\n * 3. Compute remaining + resetAt\n *\n * This is two round-trips in the worst case. A future Lua/EVALSHA variant\n * can collapse it into a single round-trip; the public API is stable.\n *\n * Returns `allowed=false` if `count > limit`. The key is `besdk:rl:{key}`.\n */\n\nexport interface RateLimitRedis {\n incr(key: string): Promise<number>;\n pexpire(key: string, ms: number): Promise<number | 'OK'>;\n pttl(key: string): Promise<number>;\n}\n\nexport interface RateLimitInput {\n key: string;\n limit: number;\n windowMs: number;\n}\n\nexport interface RateLimitResult {\n allowed: boolean;\n count: number;\n remaining: number;\n resetAt: number;\n}\n\nexport async function rateLimitIncr(redis: RateLimitRedis, input: RateLimitInput): Promise<RateLimitResult> {\n const fullKey = `besdk:rl:${input.key}`;\n const count = await redis.incr(fullKey);\n if (count === 1) {\n await redis.pexpire(fullKey, input.windowMs);\n }\n let ttl = -1;\n try {\n ttl = await redis.pttl(fullKey);\n } catch {\n ttl = input.windowMs;\n }\n const resetAt = Date.now() + (ttl > 0 ? ttl : input.windowMs);\n const remaining = Math.max(0, input.limit - count);\n return {\n allowed: count <= input.limit,\n count,\n remaining,\n resetAt,\n };\n}\n","/**\n * Paginator — Relay-style cursor pagination + safe limit clamping.\n *\n * Use `clampLimit` at resolver entry to enforce sane defaults/maxes.\n * Use `paginate` to translate a Relay-style { first, after } into a\n * Prisma-ready { take, skip, cursor } and return a connection-shaped\n * result.\n */\n\nexport interface ClampLimitOptions {\n defaultLimit?: number;\n maxLimit?: number;\n minLimit?: number;\n}\n\nexport function clampLimit(input: number | undefined | null, opts: ClampLimitOptions = {}): number {\n const def = opts.defaultLimit ?? 50;\n const max = opts.maxLimit ?? 200;\n const min = opts.minLimit ?? 1;\n if (input == null || Number.isNaN(input)) return def;\n return Math.min(Math.max(Math.floor(input), min), max);\n}\n\nexport interface PaginateInput<TWhere, TOrderBy> {\n take?: number | null;\n cursor?: string | null;\n where?: TWhere;\n orderBy?: TOrderBy;\n}\n\nexport interface PaginateResult<TItem> {\n items: TItem[];\n hasMore: boolean;\n nextCursor: string | null;\n}\n\nexport interface PrismaModelLike<TItem, TWhere, TOrderBy> {\n findMany(args: {\n take?: number;\n skip?: number;\n cursor?: { id: string };\n where?: TWhere;\n orderBy?: TOrderBy;\n }): Promise<TItem[]>;\n}\n\nexport async function paginate<TItem extends { id: string }, TWhere = unknown, TOrderBy = unknown>(\n model: PrismaModelLike<TItem, TWhere, TOrderBy>,\n input: PaginateInput<TWhere, TOrderBy>,\n opts: ClampLimitOptions = {}\n): Promise<PaginateResult<TItem>> {\n const take = clampLimit(input.take ?? undefined, opts);\n // fetch +1 to detect hasMore\n const items = await model.findMany({\n take: take + 1,\n skip: input.cursor ? 1 : 0,\n cursor: input.cursor ? { id: input.cursor } : undefined,\n where: input.where,\n orderBy: input.orderBy,\n });\n const hasMore = items.length > take;\n const trimmed = hasMore ? items.slice(0, take) : items;\n const nextCursor = hasMore && trimmed.length > 0 ? (trimmed[trimmed.length - 1] as { id: string }).id : null;\n return { items: trimmed, hasMore, nextCursor };\n}\n","/**\n * DataLoaderFactory — per-request DataLoader collection.\n *\n * Each GraphQL request creates a fresh loader set scoped to its context\n * (db, workspaceId, requestId). Loaders are addressed by name and reused\n * within the request to coalesce N+1 fan-outs into a single batched query.\n *\n * Also ships `resolveReferenceLoader` — a convenience for Apollo Federation\n * `__resolveReference` resolvers so entity hydration stays N+1-free.\n *\n * This module is intentionally framework-agnostic; consumers may pair it\n * with the existing `SimpleDataLoader` in `src/dataloader/index.ts` or the\n * upstream `dataloader` npm package.\n */\n\nexport interface DataLoaderLike<K, V> {\n load(key: K): Promise<V>;\n loadMany(keys: readonly K[]): Promise<(V | Error)[]>;\n clear(key: K): this;\n clearAll(): this;\n prime(key: K, value: V): this;\n}\n\nexport type LoaderMap = Record<string, DataLoaderLike<unknown, unknown>>;\n\nexport type LoaderBuilder<Ctx, M extends LoaderMap> = (ctx: Ctx) => M;\n\n/**\n * Build a fresh loader collection from a context.\n *\n * @example\n * const loaders = buildLoaders(ctx, (c) => ({\n * userById: createSimpleLoader(async ids => c.db.user.findMany({ where: { id: { in: [...ids] } } }), u => u.id),\n * tagById: createSimpleLoader(...),\n * }));\n * await loaders.userById.load('user-123');\n */\nexport function buildLoaders<Ctx, M extends LoaderMap>(ctx: Ctx, builder: LoaderBuilder<Ctx, M>): M {\n return builder(ctx);\n}\n\n/**\n * `__resolveReference` helper for Apollo Federation.\n *\n * Given a context that contains a `loaders` map, returns a resolver\n * function bound to a specific loader name. Field references shaped as\n * `{ id: string }` (or `{ __keyField: string }`) are forwarded to\n * `loader.load(...)`.\n */\nexport interface ReferenceObject {\n id?: string;\n [key: string]: unknown;\n}\n\nexport function resolveReferenceLoader<T, Ctx extends { loaders: LoaderMap }>(\n loaderName: string,\n keyField: string = 'id'\n): (reference: ReferenceObject, ctx: Ctx) => Promise<T | null> {\n return async (reference, ctx) => {\n const loader = ctx.loaders[loaderName] as DataLoaderLike<string, T> | undefined;\n if (!loader) {\n throw new Error(`resolveReferenceLoader: loader \"${loaderName}\" not found on ctx.loaders`);\n }\n const key = reference[keyField];\n if (typeof key !== 'string') {\n return null;\n }\n return loader.load(key);\n };\n}\n","/**\n * Versioned-key cache invalidation helper.\n *\n * Pattern:\n * - Store `v:{namespace}` integer in Redis\n * - Compose keys as `${namespace}:v${ver}:${rest}`\n * - Invalidate via INCR `v:{namespace}` — no KEYS / SCAN required\n *\n * Use this instead of cacheClearPattern() for Azure Managed Redis (O(N)\n * blocking on KEYS is unsafe in prod).\n */\n\nexport interface CacheVersionRedis {\n get(key: string): Promise<string | null>;\n incr(key: string): Promise<number>;\n}\n\nconst VERSION_PREFIX = 'besdk:cv:';\n\nexport async function getNamespaceVersion(redis: CacheVersionRedis, namespace: string): Promise<number> {\n const v = await redis.get(`${VERSION_PREFIX}${namespace}`);\n if (!v) return 1;\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) && n > 0 ? n : 1;\n}\n\nexport async function buildCacheKey(redis: CacheVersionRedis, namespace: string, ...parts: string[]): Promise<string> {\n const ver = await getNamespaceVersion(redis, namespace);\n return `${namespace}:v${ver}:${parts.join(':')}`;\n}\n\nexport async function invalidateNamespace(redis: CacheVersionRedis, namespace: string): Promise<number> {\n return redis.incr(`${VERSION_PREFIX}${namespace}`);\n}\n","/**\n * PG declarative partition + retention DSL.\n *\n * Helpers:\n * - ensureMonthlyPartitions(prisma, { table, from, to })\n * - dropOldPartitions(prisma, { table, beforeDate })\n *\n * Used by retention jobs / migrations to keep append-only tables\n * (ActivityLog, Message, TrackingEvent, ApplicationUsage, AuditLog)\n * bounded. Caller is responsible for ensuring the parent table is\n * already declared as PARTITION BY RANGE (created_at).\n */\n\nexport interface PrismaRawClient {\n $executeRawUnsafe(query: string, ...values: unknown[]): Promise<unknown>;\n $queryRawUnsafe<T = unknown>(query: string, ...values: unknown[]): Promise<T>;\n}\n\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : `${n}`;\n}\n\nexport function monthlyPartitionName(table: string, year: number, month: number): string {\n return `${table}_y${year}m${pad2(month)}`;\n}\n\nexport interface EnsureMonthlyOptions {\n table: string;\n from: Date;\n to: Date;\n}\n\nexport async function ensureMonthlyPartitions(prisma: PrismaRawClient, opts: EnsureMonthlyOptions): Promise<string[]> {\n const created: string[] = [];\n const cur = new Date(Date.UTC(opts.from.getUTCFullYear(), opts.from.getUTCMonth(), 1));\n const end = new Date(Date.UTC(opts.to.getUTCFullYear(), opts.to.getUTCMonth(), 1));\n while (cur <= end) {\n const y = cur.getUTCFullYear();\n const m = cur.getUTCMonth() + 1;\n const next = new Date(Date.UTC(y, m, 1));\n const partition = monthlyPartitionName(opts.table, y, m);\n const lower = `${y}-${pad2(m)}-01`;\n const upper = `${next.getUTCFullYear()}-${pad2(next.getUTCMonth() + 1)}-01`;\n await prisma.$executeRawUnsafe(\n `CREATE TABLE IF NOT EXISTS \"${partition}\" PARTITION OF \"${opts.table}\" FOR VALUES FROM ('${lower}') TO ('${upper}');`\n );\n created.push(partition);\n cur.setUTCMonth(cur.getUTCMonth() + 1);\n }\n return created;\n}\n\nexport interface DropOldOptions {\n table: string;\n beforeDate: Date;\n}\n\ninterface PartitionRow {\n partition_name: string;\n}\n\nexport async function dropOldPartitions(prisma: PrismaRawClient, opts: DropOldOptions): Promise<string[]> {\n // Find inherited tables of the parent, parse year/month from suffix, drop ones before threshold.\n const rows = await prisma.$queryRawUnsafe<PartitionRow[]>(\n `SELECT child.relname AS partition_name\n FROM pg_inherits\n JOIN pg_class parent ON pg_inherits.inhparent = parent.oid\n JOIN pg_class child ON pg_inherits.inhrelid = child.oid\n WHERE parent.relname = $1`,\n opts.table\n );\n const dropped: string[] = [];\n const cutoffYear = opts.beforeDate.getUTCFullYear();\n const cutoffMonth = opts.beforeDate.getUTCMonth() + 1;\n for (const row of rows) {\n const partitionRe = /_y(\\d{4})m(\\d{2})$/;\n const m = partitionRe.exec(row.partition_name);\n if (!m) continue;\n const y = Number(m[1]);\n const mo = Number(m[2]);\n if (y < cutoffYear || (y === cutoffYear && mo < cutoffMonth)) {\n await prisma.$executeRawUnsafe(`DROP TABLE IF EXISTS \"${row.partition_name}\";`);\n dropped.push(row.partition_name);\n }\n }\n return dropped;\n}\n","/**\n * Multi-provider LLM wrapper with timeout, retries, jitter, provider\n * fallback chain, token cap, and streaming passthrough.\n *\n * No hard dependency on a specific SDK — consumers register adapters.\n * Provider chain order is controlled by env `LLM_PROVIDER_CHAIN`\n * (comma-separated, e.g. `anthropic,openai,openrouter`).\n *\n * The wrapper is intentionally minimal — callers should construct it\n * once at boot, register adapters, and inject the singleton.\n */\n\nimport { logger } from '../logger/index.js';\n\nexport interface LLMRequest {\n model?: string;\n messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>;\n maxTokens?: number;\n temperature?: number;\n stream?: boolean;\n /** Override timeout for this request. */\n timeoutMs?: number;\n}\n\nexport interface LLMResponse {\n provider: string;\n content: string;\n inputTokens?: number;\n outputTokens?: number;\n raw?: unknown;\n}\n\nexport interface LLMProviderAdapter {\n name: string;\n complete(req: LLMRequest, signal: AbortSignal): Promise<LLMResponse>;\n}\n\nexport interface LLMRouterOptions {\n /** Default per-attempt timeout (ms). */\n timeoutMs?: number;\n /** Max attempts across providers. */\n maxAttempts?: number;\n /** Initial backoff (ms). */\n initialDelayMs?: number;\n /** Hard cap on output tokens regardless of caller request. */\n maxOutputTokensCap?: number;\n}\n\nconst DEFAULTS: Required<LLMRouterOptions> = {\n timeoutMs: 30_000,\n maxAttempts: 3,\n initialDelayMs: 500,\n maxOutputTokensCap: 4096,\n};\n\nfunction jitter(n: number): number {\n return Math.floor(Math.random() * n);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport class LLMRouter {\n private readonly adapters: Map<string, LLMProviderAdapter> = new Map();\n private readonly opts: Required<LLMRouterOptions>;\n\n constructor(opts: LLMRouterOptions = {}) {\n this.opts = { ...DEFAULTS, ...opts };\n }\n\n register(adapter: LLMProviderAdapter): void {\n this.adapters.set(adapter.name, adapter);\n }\n\n private resolveChain(): string[] {\n const envChain = process.env['LLM_PROVIDER_CHAIN']?.trim();\n if (envChain) {\n return envChain\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n }\n return Array.from(this.adapters.keys());\n }\n\n async complete(req: LLMRequest): Promise<LLMResponse> {\n const cappedReq: LLMRequest = {\n ...req,\n maxTokens: Math.min(req.maxTokens ?? this.opts.maxOutputTokensCap, this.opts.maxOutputTokensCap),\n };\n const chain = this.resolveChain();\n if (chain.length === 0) {\n throw new Error('LLMRouter: no providers registered');\n }\n\n let lastErr: unknown = null;\n for (let attempt = 0; attempt < this.opts.maxAttempts; attempt++) {\n const providerName = chain[attempt % chain.length];\n const adapter = providerName ? this.adapters.get(providerName) : undefined;\n if (!adapter) {\n logger.warn('llmRouter.provider.notRegistered', { providerName });\n continue;\n }\n const timeoutMs = req.timeoutMs ?? this.opts.timeoutMs;\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), timeoutMs);\n try {\n const response = await adapter.complete(cappedReq, ctrl.signal);\n return response;\n } catch (err) {\n lastErr = err;\n logger.warn('llmRouter.attempt.failed', { provider: adapter.name, attempt, err: String(err) });\n if (attempt < this.opts.maxAttempts - 1) {\n const delay = this.opts.initialDelayMs * 2 ** attempt;\n await sleep(jitter(delay));\n }\n } finally {\n clearTimeout(t);\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(`LLMRouter exhausted ${this.opts.maxAttempts} attempts`);\n }\n}\n"]}
|