skedyul 1.2.50 → 1.3.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/dist/cli/index.js +171 -142
- package/dist/config/app-config.d.ts +6 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/queue-config.d.ts +33 -0
- package/dist/dedicated/server.js +43 -15
- package/dist/esm/index.mjs +701 -39
- package/dist/index.d.ts +2 -0
- package/dist/index.js +715 -39
- package/dist/ratelimit/backends/index.d.ts +10 -0
- package/dist/ratelimit/backends/memory.d.ts +14 -0
- package/dist/ratelimit/backends/platform.d.ts +9 -0
- package/dist/ratelimit/config-loader.d.ts +10 -0
- package/dist/ratelimit/context.d.ts +7 -0
- package/dist/ratelimit/errors.d.ts +27 -0
- package/dist/ratelimit/index.d.ts +9 -0
- package/dist/ratelimit/queued-fetch.d.ts +11 -0
- package/dist/ratelimit/resolve-queue-key.d.ts +14 -0
- package/dist/ratelimit/should-retry.d.ts +5 -0
- package/dist/ratelimit/types.d.ts +43 -0
- package/dist/server.js +43 -15
- package/dist/serverless/server.mjs +43 -15
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -169,10 +169,15 @@ __export(index_exports, {
|
|
|
169
169
|
ParticipantEventPayloadSchema: () => ParticipantEventPayloadSchema,
|
|
170
170
|
ParticipantKindSchema: () => ParticipantKindSchema,
|
|
171
171
|
ProvisionConfigSchema: () => ProvisionConfigSchema,
|
|
172
|
+
QueueContextError: () => QueueContextError,
|
|
173
|
+
QueueNotFoundError: () => QueueNotFoundError,
|
|
174
|
+
QueuedFetchExhaustedError: () => QueuedFetchExhaustedError,
|
|
175
|
+
RateLimitBackendError: () => RateLimitBackendError,
|
|
172
176
|
RelationshipCardinalitySchema: () => RelationshipCardinalitySchema,
|
|
173
177
|
RelationshipDefinitionSchema: () => RelationshipDefinitionSchema,
|
|
174
178
|
RelationshipExtensionSchema: () => RelationshipExtensionSchema,
|
|
175
179
|
RelationshipLinkSchema: () => RelationshipLinkSchema,
|
|
180
|
+
RequeueOutsideContextError: () => RequeueOutsideContextError,
|
|
176
181
|
ResolvedSkillSchema: () => ResolvedSkillSchema,
|
|
177
182
|
ResolvedTriggerSchema: () => ResolvedTriggerSchema,
|
|
178
183
|
ResourceDependencySchema: () => ResourceDependencySchema,
|
|
@@ -250,6 +255,7 @@ __export(index_exports, {
|
|
|
250
255
|
createListResponse: () => createListResponse,
|
|
251
256
|
createNotFoundError: () => createNotFoundError,
|
|
252
257
|
createPermissionError: () => createPermissionError,
|
|
258
|
+
createQueueHandle: () => createQueueHandle,
|
|
253
259
|
createRateLimitError: () => createRateLimitError,
|
|
254
260
|
createServerHookContext: () => createServerHookContext,
|
|
255
261
|
createSuccessResponse: () => createSuccessResponse,
|
|
@@ -260,6 +266,7 @@ __export(index_exports, {
|
|
|
260
266
|
createWorkflowStepContext: () => createWorkflowStepContext,
|
|
261
267
|
cron: () => cron,
|
|
262
268
|
default: () => index_default,
|
|
269
|
+
defaultShouldRetry: () => defaultShouldRetry,
|
|
263
270
|
defineAgent: () => defineAgent,
|
|
264
271
|
defineChannel: () => defineChannel,
|
|
265
272
|
defineConfig: () => defineConfig,
|
|
@@ -282,6 +289,7 @@ __export(index_exports, {
|
|
|
282
289
|
getConfig: () => getConfig,
|
|
283
290
|
getContextByHandle: () => getContextByHandle,
|
|
284
291
|
getContextByModel: () => getContextByModel,
|
|
292
|
+
getRateLimitExecutionContext: () => getRateLimitExecutionContext,
|
|
285
293
|
getRequiredInstallEnvKeys: () => getRequiredInstallEnvKeys,
|
|
286
294
|
getRetryDelay: () => getRetryDelay,
|
|
287
295
|
instance: () => instance,
|
|
@@ -301,10 +309,16 @@ __export(index_exports, {
|
|
|
301
309
|
loadConfig: () => loadConfig,
|
|
302
310
|
matchesTrigger: () => matchesTrigger,
|
|
303
311
|
parseCRMSchema: () => parseCRMSchema,
|
|
312
|
+
queuedFetch: () => queuedFetch,
|
|
313
|
+
queuedFetchResponse: () => queuedFetchResponse,
|
|
314
|
+
registerQueueConfig: () => registerQueueConfig,
|
|
304
315
|
report: () => report,
|
|
316
|
+
requeue: () => requeue,
|
|
305
317
|
resolveInputMappings: () => resolveInputMappings,
|
|
318
|
+
resolveQueue: () => resolveQueue,
|
|
306
319
|
resource: () => resource,
|
|
307
320
|
runWithConfig: () => runWithConfig,
|
|
321
|
+
runWithRateLimitExecutionContext: () => runWithRateLimitExecutionContext,
|
|
308
322
|
safeParseCRMSchema: () => safeParseCRMSchema,
|
|
309
323
|
safeParseConfig: () => safeParseConfig,
|
|
310
324
|
server: () => server,
|
|
@@ -2163,6 +2177,68 @@ var CoreApiService = class {
|
|
|
2163
2177
|
};
|
|
2164
2178
|
var coreApiService = new CoreApiService();
|
|
2165
2179
|
|
|
2180
|
+
// src/ratelimit/config-loader.ts
|
|
2181
|
+
var fs = __toESM(require("fs"));
|
|
2182
|
+
var path = __toESM(require("path"));
|
|
2183
|
+
var cachedRuntimeQueues = null;
|
|
2184
|
+
function getRuntimeConfigPath() {
|
|
2185
|
+
if (process.env.LAMBDA_TASK_ROOT) {
|
|
2186
|
+
return path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json");
|
|
2187
|
+
}
|
|
2188
|
+
return path.join(process.cwd(), ".skedyul", "config.json");
|
|
2189
|
+
}
|
|
2190
|
+
function loadRuntimeConfigFile() {
|
|
2191
|
+
const configPath = getRuntimeConfigPath();
|
|
2192
|
+
if (!fs.existsSync(configPath)) {
|
|
2193
|
+
return null;
|
|
2194
|
+
}
|
|
2195
|
+
try {
|
|
2196
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
2197
|
+
return JSON.parse(raw);
|
|
2198
|
+
} catch {
|
|
2199
|
+
return null;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
var registeredServerConfig = null;
|
|
2203
|
+
function registerQueueConfig(config) {
|
|
2204
|
+
registeredServerConfig = config;
|
|
2205
|
+
cachedRuntimeQueues = config.queues ?? null;
|
|
2206
|
+
}
|
|
2207
|
+
function getQueueDefinitions() {
|
|
2208
|
+
if (cachedRuntimeQueues) {
|
|
2209
|
+
return cachedRuntimeQueues;
|
|
2210
|
+
}
|
|
2211
|
+
if (registeredServerConfig?.queues) {
|
|
2212
|
+
cachedRuntimeQueues = stripNonSerializableQueues(registeredServerConfig.queues);
|
|
2213
|
+
return cachedRuntimeQueues;
|
|
2214
|
+
}
|
|
2215
|
+
const fileConfig = loadRuntimeConfigFile();
|
|
2216
|
+
if (fileConfig?.queues) {
|
|
2217
|
+
cachedRuntimeQueues = fileConfig.queues;
|
|
2218
|
+
return cachedRuntimeQueues;
|
|
2219
|
+
}
|
|
2220
|
+
return {};
|
|
2221
|
+
}
|
|
2222
|
+
function stripNonSerializableQueues(queues) {
|
|
2223
|
+
const result = {};
|
|
2224
|
+
for (const [name, config] of Object.entries(queues)) {
|
|
2225
|
+
const { shouldRetry: _shouldRetry, ...serializable } = config;
|
|
2226
|
+
result[name] = serializable;
|
|
2227
|
+
}
|
|
2228
|
+
return result;
|
|
2229
|
+
}
|
|
2230
|
+
function getQueueConfig(queueName) {
|
|
2231
|
+
return getQueueDefinitions()[queueName];
|
|
2232
|
+
}
|
|
2233
|
+
function getQueueConfigWithRetry(queueName) {
|
|
2234
|
+
const fromServer = registeredServerConfig?.queues?.[queueName];
|
|
2235
|
+
if (fromServer) {
|
|
2236
|
+
return fromServer;
|
|
2237
|
+
}
|
|
2238
|
+
const serializable = getQueueConfig(queueName);
|
|
2239
|
+
return serializable;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2166
2242
|
// src/server/utils/schema.ts
|
|
2167
2243
|
var z8 = __toESM(require("zod"));
|
|
2168
2244
|
function normalizeBilling(billing) {
|
|
@@ -3484,6 +3560,35 @@ var report = {
|
|
|
3484
3560
|
}
|
|
3485
3561
|
};
|
|
3486
3562
|
|
|
3563
|
+
// src/ratelimit/context.ts
|
|
3564
|
+
var import_async_hooks2 = require("async_hooks");
|
|
3565
|
+
var executionContextStorage = new import_async_hooks2.AsyncLocalStorage();
|
|
3566
|
+
var activeOperationStorage = new import_async_hooks2.AsyncLocalStorage();
|
|
3567
|
+
function runWithRateLimitExecutionContext(ctx, fn) {
|
|
3568
|
+
return executionContextStorage.run(ctx, fn);
|
|
3569
|
+
}
|
|
3570
|
+
function getRateLimitExecutionContext() {
|
|
3571
|
+
return executionContextStorage.getStore();
|
|
3572
|
+
}
|
|
3573
|
+
function runWithActiveQueuedOperation(operation, fn) {
|
|
3574
|
+
return activeOperationStorage.run(operation, fn);
|
|
3575
|
+
}
|
|
3576
|
+
function getActiveQueuedOperation() {
|
|
3577
|
+
return activeOperationStorage.getStore();
|
|
3578
|
+
}
|
|
3579
|
+
function setActiveQueuedOperationLease(lease) {
|
|
3580
|
+
const op = activeOperationStorage.getStore();
|
|
3581
|
+
if (op) {
|
|
3582
|
+
op.lease = lease;
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
function updateActiveQueuedOperationAttempt(attempt) {
|
|
3586
|
+
const op = activeOperationStorage.getStore();
|
|
3587
|
+
if (op) {
|
|
3588
|
+
op.attempt = attempt;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3487
3592
|
// src/errors.ts
|
|
3488
3593
|
var InstallError = class extends Error {
|
|
3489
3594
|
// Optional: which field caused the error
|
|
@@ -3538,8 +3643,8 @@ var AppAuthInvalidError = class extends Error {
|
|
|
3538
3643
|
};
|
|
3539
3644
|
|
|
3540
3645
|
// src/server/context-logger.ts
|
|
3541
|
-
var
|
|
3542
|
-
var logContextStorage = new
|
|
3646
|
+
var import_async_hooks3 = require("async_hooks");
|
|
3647
|
+
var logContextStorage = new import_async_hooks3.AsyncLocalStorage();
|
|
3543
3648
|
function runWithLogContext(context, fn) {
|
|
3544
3649
|
return logContextStorage.run(context, fn);
|
|
3545
3650
|
}
|
|
@@ -3752,9 +3857,17 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
3752
3857
|
baseUrl: toolEnv.SKEDYUL_API_URL ?? "",
|
|
3753
3858
|
apiToken: toolEnv.SKEDYUL_API_TOKEN ?? ""
|
|
3754
3859
|
};
|
|
3860
|
+
const rateLimitContext = {
|
|
3861
|
+
app,
|
|
3862
|
+
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
3863
|
+
invocation,
|
|
3864
|
+
isProvisionContext: trigger === "provision"
|
|
3865
|
+
};
|
|
3755
3866
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
3756
|
-
return await
|
|
3757
|
-
return await
|
|
3867
|
+
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
3868
|
+
return await runWithLogContext({ invocation }, async () => {
|
|
3869
|
+
return await fn(inputs, executionContext);
|
|
3870
|
+
});
|
|
3758
3871
|
});
|
|
3759
3872
|
});
|
|
3760
3873
|
const billing = normalizeBilling(functionResult.billing);
|
|
@@ -3891,7 +4004,7 @@ function printStartupLog(config, tools, port) {
|
|
|
3891
4004
|
|
|
3892
4005
|
// src/server/route-handlers/adapters.ts
|
|
3893
4006
|
function fromLambdaEvent(event2) {
|
|
3894
|
-
const
|
|
4007
|
+
const path6 = event2.path || event2.rawPath || "/";
|
|
3895
4008
|
const method = event2.httpMethod || event2.requestContext?.http?.method || "POST";
|
|
3896
4009
|
const forwardedProto = event2.headers?.["x-forwarded-proto"] ?? event2.headers?.["X-Forwarded-Proto"];
|
|
3897
4010
|
const protocol = forwardedProto ?? "https";
|
|
@@ -3899,9 +4012,9 @@ function fromLambdaEvent(event2) {
|
|
|
3899
4012
|
const queryString = event2.queryStringParameters ? "?" + new URLSearchParams(
|
|
3900
4013
|
event2.queryStringParameters
|
|
3901
4014
|
).toString() : "";
|
|
3902
|
-
const url = `${protocol}://${host}${
|
|
4015
|
+
const url = `${protocol}://${host}${path6}${queryString}`;
|
|
3903
4016
|
return {
|
|
3904
|
-
path:
|
|
4017
|
+
path: path6,
|
|
3905
4018
|
method,
|
|
3906
4019
|
headers: event2.headers,
|
|
3907
4020
|
query: event2.queryStringParameters ?? {},
|
|
@@ -3983,8 +4096,8 @@ function parseBodyByContentType(req) {
|
|
|
3983
4096
|
}
|
|
3984
4097
|
|
|
3985
4098
|
// src/server/route-handlers/handlers.ts
|
|
3986
|
-
var
|
|
3987
|
-
var
|
|
4099
|
+
var fs2 = __toESM(require("fs"));
|
|
4100
|
+
var path2 = __toESM(require("path"));
|
|
3988
4101
|
|
|
3989
4102
|
// src/server/core-api-handler.ts
|
|
3990
4103
|
async function handleCoreMethod(method, params) {
|
|
@@ -4152,7 +4265,8 @@ function serializeConfig(config) {
|
|
|
4152
4265
|
type: w.type ?? "WEBHOOK"
|
|
4153
4266
|
})) : [],
|
|
4154
4267
|
provision: isProvisionConfig(config.provision) ? config.provision : void 0,
|
|
4155
|
-
agents: config.agents
|
|
4268
|
+
agents: config.agents,
|
|
4269
|
+
queues: config.queues
|
|
4156
4270
|
};
|
|
4157
4271
|
}
|
|
4158
4272
|
function isProvisionConfig(value) {
|
|
@@ -4448,7 +4562,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
|
|
|
4448
4562
|
}
|
|
4449
4563
|
|
|
4450
4564
|
// src/server/handlers/webhook-handler.ts
|
|
4451
|
-
function parseWebhookRequest(parsedBody, method, url,
|
|
4565
|
+
function parseWebhookRequest(parsedBody, method, url, path6, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
|
|
4452
4566
|
const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
|
|
4453
4567
|
if (isEnvelope) {
|
|
4454
4568
|
const envelope = parsedBody;
|
|
@@ -4502,7 +4616,7 @@ function parseWebhookRequest(parsedBody, method, url, path5, headers, query, raw
|
|
|
4502
4616
|
const webhookRequest = {
|
|
4503
4617
|
method,
|
|
4504
4618
|
url,
|
|
4505
|
-
path:
|
|
4619
|
+
path: path6,
|
|
4506
4620
|
headers,
|
|
4507
4621
|
query,
|
|
4508
4622
|
body: parsedBody,
|
|
@@ -4560,7 +4674,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
4560
4674
|
|
|
4561
4675
|
// src/server/route-handlers/handlers.ts
|
|
4562
4676
|
function getConfigFilePath() {
|
|
4563
|
-
return process.env.LAMBDA_TASK_ROOT ?
|
|
4677
|
+
return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
4564
4678
|
}
|
|
4565
4679
|
function findToolInRegistry(registry, toolName) {
|
|
4566
4680
|
for (const [key, t] of Object.entries(registry)) {
|
|
@@ -4599,8 +4713,8 @@ function handleHealthRoute(ctx) {
|
|
|
4599
4713
|
function handleConfigRoute(ctx) {
|
|
4600
4714
|
const configFilePath = getConfigFilePath();
|
|
4601
4715
|
try {
|
|
4602
|
-
if (
|
|
4603
|
-
const fileConfig = JSON.parse(
|
|
4716
|
+
if (fs2.existsSync(configFilePath)) {
|
|
4717
|
+
const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
|
|
4604
4718
|
return { status: 200, body: fileConfig };
|
|
4605
4719
|
}
|
|
4606
4720
|
} catch (err) {
|
|
@@ -5285,6 +5399,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
5285
5399
|
installContextLogger();
|
|
5286
5400
|
function createSkedyulServer(config) {
|
|
5287
5401
|
mergeRuntimeEnv();
|
|
5402
|
+
registerQueueConfig(config);
|
|
5288
5403
|
const registry = config.tools;
|
|
5289
5404
|
const webhookRegistry = config.webhooks;
|
|
5290
5405
|
if (config.coreApi?.service) {
|
|
@@ -5553,8 +5668,8 @@ function defineNavigation(navigation) {
|
|
|
5553
5668
|
}
|
|
5554
5669
|
|
|
5555
5670
|
// src/config/loader.ts
|
|
5556
|
-
var
|
|
5557
|
-
var
|
|
5671
|
+
var fs3 = __toESM(require("fs"));
|
|
5672
|
+
var path3 = __toESM(require("path"));
|
|
5558
5673
|
var os = __toESM(require("os"));
|
|
5559
5674
|
var CONFIG_FILE_NAMES = [
|
|
5560
5675
|
"skedyul.config.ts",
|
|
@@ -5563,13 +5678,13 @@ var CONFIG_FILE_NAMES = [
|
|
|
5563
5678
|
"skedyul.config.cjs"
|
|
5564
5679
|
];
|
|
5565
5680
|
async function transpileTypeScript(filePath) {
|
|
5566
|
-
const content =
|
|
5567
|
-
const configDir =
|
|
5681
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5682
|
+
const configDir = path3.dirname(path3.resolve(filePath));
|
|
5568
5683
|
let transpiled = content.replace(/import\s+type\s+\{[^}]+\}\s+from\s+['"][^'"]+['"]\s*;?\n?/g, "").replace(/import\s+\{\s*defineConfig\s*\}\s+from\s+['"]skedyul['"]\s*;?\n?/g, "").replace(/:\s*SkedyulConfig/g, "").replace(/export\s+default\s+/, "module.exports = ").replace(/defineConfig\s*\(\s*\{/, "{").replace(/\}\s*\)\s*;?\s*$/, "}");
|
|
5569
5684
|
transpiled = transpiled.replace(
|
|
5570
5685
|
/import\s+(\w+)\s+from\s+['"](\.[^'"]+)['"]\s*(?:with\s*\{[^}]*\})?/g,
|
|
5571
5686
|
(match, varName, relativePath) => {
|
|
5572
|
-
const absolutePath =
|
|
5687
|
+
const absolutePath = path3.resolve(configDir, relativePath);
|
|
5573
5688
|
return `const ${varName} = require('${absolutePath.replace(/\\/g, "/")}')`;
|
|
5574
5689
|
}
|
|
5575
5690
|
);
|
|
@@ -5577,8 +5692,8 @@ async function transpileTypeScript(filePath) {
|
|
|
5577
5692
|
return transpiled;
|
|
5578
5693
|
}
|
|
5579
5694
|
async function loadConfig(configPath) {
|
|
5580
|
-
const absolutePath =
|
|
5581
|
-
if (!
|
|
5695
|
+
const absolutePath = path3.resolve(configPath);
|
|
5696
|
+
if (!fs3.existsSync(absolutePath)) {
|
|
5582
5697
|
throw new Error(`Config file not found: ${absolutePath}`);
|
|
5583
5698
|
}
|
|
5584
5699
|
const isTypeScript = absolutePath.endsWith(".ts");
|
|
@@ -5587,8 +5702,8 @@ async function loadConfig(configPath) {
|
|
|
5587
5702
|
if (isTypeScript) {
|
|
5588
5703
|
const transpiled = await transpileTypeScript(absolutePath);
|
|
5589
5704
|
const tempDir = os.tmpdir();
|
|
5590
|
-
const tempFile =
|
|
5591
|
-
|
|
5705
|
+
const tempFile = path3.join(tempDir, `skedyul-config-${Date.now()}.cjs`);
|
|
5706
|
+
fs3.writeFileSync(tempFile, transpiled);
|
|
5592
5707
|
moduleToLoad = tempFile;
|
|
5593
5708
|
try {
|
|
5594
5709
|
const module3 = require(moduleToLoad);
|
|
@@ -5602,7 +5717,7 @@ async function loadConfig(configPath) {
|
|
|
5602
5717
|
return config2;
|
|
5603
5718
|
} finally {
|
|
5604
5719
|
try {
|
|
5605
|
-
|
|
5720
|
+
fs3.unlinkSync(tempFile);
|
|
5606
5721
|
} catch {
|
|
5607
5722
|
}
|
|
5608
5723
|
}
|
|
@@ -5637,13 +5752,13 @@ function validateConfig(config) {
|
|
|
5637
5752
|
}
|
|
5638
5753
|
|
|
5639
5754
|
// src/config/schema-loader.ts
|
|
5640
|
-
var
|
|
5641
|
-
var
|
|
5755
|
+
var fs4 = __toESM(require("fs"));
|
|
5756
|
+
var path4 = __toESM(require("path"));
|
|
5642
5757
|
var os2 = __toESM(require("os"));
|
|
5643
5758
|
|
|
5644
5759
|
// src/config/resolver.ts
|
|
5645
|
-
var
|
|
5646
|
-
var
|
|
5760
|
+
var fs5 = __toESM(require("fs"));
|
|
5761
|
+
var path5 = __toESM(require("path"));
|
|
5647
5762
|
|
|
5648
5763
|
// src/config/utils.ts
|
|
5649
5764
|
function getAllEnvKeys(config) {
|
|
@@ -5660,6 +5775,553 @@ function getRequiredInstallEnvKeys(config) {
|
|
|
5660
5775
|
return Object.entries(provision.env).filter(([, def]) => def.required).map(([key]) => key);
|
|
5661
5776
|
}
|
|
5662
5777
|
|
|
5778
|
+
// src/ratelimit/errors.ts
|
|
5779
|
+
var QueueContextError = class extends Error {
|
|
5780
|
+
constructor(message) {
|
|
5781
|
+
super(message);
|
|
5782
|
+
this.code = "QUEUE_CONTEXT_ERROR";
|
|
5783
|
+
this.name = "QueueContextError";
|
|
5784
|
+
}
|
|
5785
|
+
};
|
|
5786
|
+
var QueueNotFoundError = class extends Error {
|
|
5787
|
+
constructor(queueName) {
|
|
5788
|
+
super(`Queue "${queueName}" is not defined in skedyul.config queues`);
|
|
5789
|
+
this.code = "QUEUE_NOT_FOUND";
|
|
5790
|
+
this.name = "QueueNotFoundError";
|
|
5791
|
+
}
|
|
5792
|
+
};
|
|
5793
|
+
var QueuedFetchExhaustedError = class extends Error {
|
|
5794
|
+
constructor(attempts, maxRetries, causeError) {
|
|
5795
|
+
super(
|
|
5796
|
+
`queuedFetch exhausted retries after ${attempts} attempts (maxRetries=${maxRetries})`
|
|
5797
|
+
);
|
|
5798
|
+
this.code = "QUEUED_FETCH_EXHAUSTED";
|
|
5799
|
+
this.name = "QueuedFetchExhaustedError";
|
|
5800
|
+
this.attempts = attempts;
|
|
5801
|
+
this.maxRetries = maxRetries;
|
|
5802
|
+
this.causeError = causeError;
|
|
5803
|
+
}
|
|
5804
|
+
};
|
|
5805
|
+
var RequeueOutsideContextError = class extends Error {
|
|
5806
|
+
constructor() {
|
|
5807
|
+
super("requeue() can only be called inside an active queuedFetch operation");
|
|
5808
|
+
this.code = "REQUEUE_OUTSIDE_CONTEXT";
|
|
5809
|
+
this.name = "RequeueOutsideContextError";
|
|
5810
|
+
}
|
|
5811
|
+
};
|
|
5812
|
+
var RateLimitBackendError = class extends Error {
|
|
5813
|
+
constructor(message, statusCode) {
|
|
5814
|
+
super(message);
|
|
5815
|
+
this.code = "RATE_LIMIT_BACKEND_ERROR";
|
|
5816
|
+
this.name = "RateLimitBackendError";
|
|
5817
|
+
this.statusCode = statusCode;
|
|
5818
|
+
}
|
|
5819
|
+
};
|
|
5820
|
+
|
|
5821
|
+
// src/ratelimit/resolve-queue-key.ts
|
|
5822
|
+
function resolveEndpointHandle(config, invocation) {
|
|
5823
|
+
if (config.endpoint) {
|
|
5824
|
+
return config.endpoint;
|
|
5825
|
+
}
|
|
5826
|
+
if (invocation?.toolHandle) {
|
|
5827
|
+
return invocation.toolHandle;
|
|
5828
|
+
}
|
|
5829
|
+
if (invocation?.serverHookHandle) {
|
|
5830
|
+
return invocation.serverHookHandle;
|
|
5831
|
+
}
|
|
5832
|
+
throw new QueueContextError(
|
|
5833
|
+
`Cannot resolve endpoint handle for queue scope "${config.scope}". Set endpoint in queue config or ensure invocation.toolHandle/serverHookHandle is available.`
|
|
5834
|
+
);
|
|
5835
|
+
}
|
|
5836
|
+
function appendSubKey(base, subKey) {
|
|
5837
|
+
return subKey ? `${base}:${subKey}` : base;
|
|
5838
|
+
}
|
|
5839
|
+
function resolveQueueKey(queueName, config, ctx, subKey) {
|
|
5840
|
+
const { app, appInstallationId, invocation, isProvisionContext: isProvisionContext2 } = ctx;
|
|
5841
|
+
switch (config.scope) {
|
|
5842
|
+
case "provision": {
|
|
5843
|
+
const base = `rl:pv:${app.versionId}:${queueName}`;
|
|
5844
|
+
return appendSubKey(base, subKey);
|
|
5845
|
+
}
|
|
5846
|
+
case "install": {
|
|
5847
|
+
if (!appInstallationId) {
|
|
5848
|
+
throw new QueueContextError(
|
|
5849
|
+
`Queue "${queueName}" with scope "install" requires appInstallationId in context`
|
|
5850
|
+
);
|
|
5851
|
+
}
|
|
5852
|
+
const base = `rl:in:${appInstallationId}:${queueName}`;
|
|
5853
|
+
return appendSubKey(base, subKey);
|
|
5854
|
+
}
|
|
5855
|
+
case "provision_endpoint": {
|
|
5856
|
+
if (!isProvisionContext2 && appInstallationId) {
|
|
5857
|
+
throw new QueueContextError(
|
|
5858
|
+
`Queue "${queueName}" with scope "provision_endpoint" requires provision context (no install)`
|
|
5859
|
+
);
|
|
5860
|
+
}
|
|
5861
|
+
const endpointHandle = resolveEndpointHandle(config, invocation);
|
|
5862
|
+
const base = `rl:pep:${app.versionId}:${endpointHandle}:${queueName}`;
|
|
5863
|
+
return appendSubKey(base, subKey);
|
|
5864
|
+
}
|
|
5865
|
+
case "install_endpoint": {
|
|
5866
|
+
if (!appInstallationId) {
|
|
5867
|
+
throw new QueueContextError(
|
|
5868
|
+
`Queue "${queueName}" with scope "install_endpoint" requires appInstallationId in context`
|
|
5869
|
+
);
|
|
5870
|
+
}
|
|
5871
|
+
const endpointHandle = resolveEndpointHandle(config, invocation);
|
|
5872
|
+
const base = `rl:iep:${appInstallationId}:${endpointHandle}:${queueName}`;
|
|
5873
|
+
return appendSubKey(base, subKey);
|
|
5874
|
+
}
|
|
5875
|
+
case "global": {
|
|
5876
|
+
const base = `rl:gl:${app.versionId}:${queueName}`;
|
|
5877
|
+
return appendSubKey(base, subKey);
|
|
5878
|
+
}
|
|
5879
|
+
default: {
|
|
5880
|
+
const _exhaustive = config.scope;
|
|
5881
|
+
throw new QueueContextError(`Unknown queue scope: ${String(_exhaustive)}`);
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
function toQueueLimits(config) {
|
|
5886
|
+
return {
|
|
5887
|
+
maxConcurrent: config.maxConcurrent,
|
|
5888
|
+
minTime: config.minTime,
|
|
5889
|
+
reservoir: config.reservoir,
|
|
5890
|
+
reservoirRefreshAmount: config.reservoirRefreshAmount,
|
|
5891
|
+
reservoirRefreshInterval: config.reservoirRefreshInterval
|
|
5892
|
+
};
|
|
5893
|
+
}
|
|
5894
|
+
|
|
5895
|
+
// src/ratelimit/backends/platform.ts
|
|
5896
|
+
function getApiBaseUrl() {
|
|
5897
|
+
const config = getConfig();
|
|
5898
|
+
if (config.baseUrl) {
|
|
5899
|
+
return config.baseUrl.replace(/\/+$/, "");
|
|
5900
|
+
}
|
|
5901
|
+
return (process.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_NODE_URL ?? "").replace(/\/+$/, "");
|
|
5902
|
+
}
|
|
5903
|
+
function getApiToken() {
|
|
5904
|
+
const config = getConfig();
|
|
5905
|
+
return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
|
|
5906
|
+
}
|
|
5907
|
+
async function callRateLimitApi(path6, body) {
|
|
5908
|
+
const baseUrl = getApiBaseUrl();
|
|
5909
|
+
const token2 = getApiToken();
|
|
5910
|
+
if (!baseUrl || !token2) {
|
|
5911
|
+
throw new RateLimitBackendError(
|
|
5912
|
+
"SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform rate limiting"
|
|
5913
|
+
);
|
|
5914
|
+
}
|
|
5915
|
+
const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path6}`, {
|
|
5916
|
+
method: "POST",
|
|
5917
|
+
headers: {
|
|
5918
|
+
Authorization: `Bearer ${token2}`,
|
|
5919
|
+
"Content-Type": "application/json"
|
|
5920
|
+
},
|
|
5921
|
+
body: JSON.stringify(body)
|
|
5922
|
+
});
|
|
5923
|
+
const payload = await response.json().catch(() => null);
|
|
5924
|
+
if (!response.ok || payload?.success === false) {
|
|
5925
|
+
const message = payload?.errors?.[0]?.message ?? `Rate limit API ${path6} failed with status ${response.status}`;
|
|
5926
|
+
throw new RateLimitBackendError(message, response.status);
|
|
5927
|
+
}
|
|
5928
|
+
if (!payload?.data) {
|
|
5929
|
+
throw new RateLimitBackendError(`Rate limit API ${path6} returned empty data`);
|
|
5930
|
+
}
|
|
5931
|
+
return payload.data;
|
|
5932
|
+
}
|
|
5933
|
+
var PlatformRateLimitBackend = class {
|
|
5934
|
+
async acquire(queueKey, limits, timeoutMs) {
|
|
5935
|
+
const data = await callRateLimitApi("acquire", {
|
|
5936
|
+
queueKey,
|
|
5937
|
+
limits,
|
|
5938
|
+
timeoutMs
|
|
5939
|
+
});
|
|
5940
|
+
return {
|
|
5941
|
+
leaseId: data.leaseId,
|
|
5942
|
+
acquiredAt: data.acquiredAt,
|
|
5943
|
+
queueKey: data.queueKey
|
|
5944
|
+
};
|
|
5945
|
+
}
|
|
5946
|
+
async release(lease) {
|
|
5947
|
+
await callRateLimitApi("release", {
|
|
5948
|
+
leaseId: lease.leaseId
|
|
5949
|
+
});
|
|
5950
|
+
}
|
|
5951
|
+
};
|
|
5952
|
+
var platformRateLimitBackend = new PlatformRateLimitBackend();
|
|
5953
|
+
|
|
5954
|
+
// src/ratelimit/backends/memory.ts
|
|
5955
|
+
var DEFAULT_MAX_CONCURRENT = 10;
|
|
5956
|
+
function getMaxConcurrent(limits) {
|
|
5957
|
+
return limits.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
|
|
5958
|
+
}
|
|
5959
|
+
function getMinTime(limits) {
|
|
5960
|
+
return limits.minTime ?? 0;
|
|
5961
|
+
}
|
|
5962
|
+
function generateLeaseId() {
|
|
5963
|
+
return `mem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5964
|
+
}
|
|
5965
|
+
function refillTokens(state, limits) {
|
|
5966
|
+
if (limits.reservoir === void 0) {
|
|
5967
|
+
return;
|
|
5968
|
+
}
|
|
5969
|
+
const capacity = limits.reservoir;
|
|
5970
|
+
const refreshAmount = limits.reservoirRefreshAmount ?? limits.reservoir;
|
|
5971
|
+
const refreshInterval = limits.reservoirRefreshInterval ?? 6e4;
|
|
5972
|
+
const now = Date.now();
|
|
5973
|
+
const elapsed = now - state.lastRefillAt;
|
|
5974
|
+
if (elapsed >= refreshInterval) {
|
|
5975
|
+
const intervals = Math.floor(elapsed / refreshInterval);
|
|
5976
|
+
state.tokens = Math.min(capacity, state.tokens + intervals * refreshAmount);
|
|
5977
|
+
state.lastRefillAt = now;
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
5980
|
+
function canAcquire(state, limits) {
|
|
5981
|
+
if (state.running >= getMaxConcurrent(limits)) {
|
|
5982
|
+
return false;
|
|
5983
|
+
}
|
|
5984
|
+
refillTokens(state, limits);
|
|
5985
|
+
if (limits.reservoir !== void 0 && state.tokens <= 0) {
|
|
5986
|
+
return false;
|
|
5987
|
+
}
|
|
5988
|
+
const minTime = getMinTime(limits);
|
|
5989
|
+
if (minTime > 0 && state.lastStartAt > 0) {
|
|
5990
|
+
if (Date.now() - state.lastStartAt < minTime) {
|
|
5991
|
+
return false;
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
return true;
|
|
5995
|
+
}
|
|
5996
|
+
var MemoryRateLimitBackend = class {
|
|
5997
|
+
constructor() {
|
|
5998
|
+
this.queues = /* @__PURE__ */ new Map();
|
|
5999
|
+
this.leases = /* @__PURE__ */ new Map();
|
|
6000
|
+
}
|
|
6001
|
+
getState(queueKey) {
|
|
6002
|
+
let state = this.queues.get(queueKey);
|
|
6003
|
+
if (!state) {
|
|
6004
|
+
state = {
|
|
6005
|
+
running: 0,
|
|
6006
|
+
waiters: [],
|
|
6007
|
+
lastStartAt: 0,
|
|
6008
|
+
tokens: Number.POSITIVE_INFINITY,
|
|
6009
|
+
lastRefillAt: Date.now()
|
|
6010
|
+
};
|
|
6011
|
+
if (queueKey.includes(":")) {
|
|
6012
|
+
}
|
|
6013
|
+
this.queues.set(queueKey, state);
|
|
6014
|
+
}
|
|
6015
|
+
return state;
|
|
6016
|
+
}
|
|
6017
|
+
grant(state, queueKey, limits) {
|
|
6018
|
+
if (limits.reservoir !== void 0) {
|
|
6019
|
+
state.tokens -= 1;
|
|
6020
|
+
}
|
|
6021
|
+
state.running += 1;
|
|
6022
|
+
state.lastStartAt = Date.now();
|
|
6023
|
+
const leaseId = generateLeaseId();
|
|
6024
|
+
this.leases.set(leaseId, { queueKey, limits });
|
|
6025
|
+
return { leaseId, acquiredAt: Date.now(), queueKey };
|
|
6026
|
+
}
|
|
6027
|
+
drainWaiters(queueKey, state) {
|
|
6028
|
+
let progressed = true;
|
|
6029
|
+
while (progressed && state.waiters.length > 0) {
|
|
6030
|
+
progressed = false;
|
|
6031
|
+
for (let i = 0; i < state.waiters.length; i += 1) {
|
|
6032
|
+
const waiter = state.waiters[i];
|
|
6033
|
+
if (!waiter || !canAcquire(state, waiter.limits)) {
|
|
6034
|
+
continue;
|
|
6035
|
+
}
|
|
6036
|
+
state.waiters.splice(i, 1);
|
|
6037
|
+
if (waiter.timer) {
|
|
6038
|
+
clearTimeout(waiter.timer);
|
|
6039
|
+
}
|
|
6040
|
+
waiter.resolve(this.grant(state, queueKey, waiter.limits));
|
|
6041
|
+
progressed = true;
|
|
6042
|
+
break;
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
async acquire(queueKey, limits, timeoutMs = 12e4) {
|
|
6047
|
+
const state = this.getState(queueKey);
|
|
6048
|
+
if (limits.reservoir !== void 0 && state.tokens === Number.POSITIVE_INFINITY) {
|
|
6049
|
+
state.tokens = limits.reservoir;
|
|
6050
|
+
}
|
|
6051
|
+
if (canAcquire(state, limits)) {
|
|
6052
|
+
return this.grant(state, queueKey, limits);
|
|
6053
|
+
}
|
|
6054
|
+
return new Promise((resolve4, reject) => {
|
|
6055
|
+
const waiter = {
|
|
6056
|
+
resolve: resolve4,
|
|
6057
|
+
reject,
|
|
6058
|
+
timer: null,
|
|
6059
|
+
limits
|
|
6060
|
+
};
|
|
6061
|
+
if (timeoutMs > 0) {
|
|
6062
|
+
waiter.timer = setTimeout(() => {
|
|
6063
|
+
const idx = state.waiters.indexOf(waiter);
|
|
6064
|
+
if (idx >= 0) {
|
|
6065
|
+
state.waiters.splice(idx, 1);
|
|
6066
|
+
}
|
|
6067
|
+
reject(
|
|
6068
|
+
new Error(`Rate limit acquire timed out after ${timeoutMs}ms for ${queueKey}`)
|
|
6069
|
+
);
|
|
6070
|
+
}, timeoutMs);
|
|
6071
|
+
}
|
|
6072
|
+
state.waiters.push(waiter);
|
|
6073
|
+
const retry = () => {
|
|
6074
|
+
if (!state.waiters.includes(waiter)) {
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
6077
|
+
if (canAcquire(state, limits)) {
|
|
6078
|
+
const idx = state.waiters.indexOf(waiter);
|
|
6079
|
+
if (idx >= 0) {
|
|
6080
|
+
state.waiters.splice(idx, 1);
|
|
6081
|
+
}
|
|
6082
|
+
if (waiter.timer) {
|
|
6083
|
+
clearTimeout(waiter.timer);
|
|
6084
|
+
}
|
|
6085
|
+
resolve4(this.grant(state, queueKey, limits));
|
|
6086
|
+
return;
|
|
6087
|
+
}
|
|
6088
|
+
setTimeout(retry, Math.max(getMinTime(limits), 10));
|
|
6089
|
+
};
|
|
6090
|
+
setTimeout(retry, Math.max(getMinTime(limits), 10));
|
|
6091
|
+
});
|
|
6092
|
+
}
|
|
6093
|
+
async release(lease) {
|
|
6094
|
+
const tracked = this.leases.get(lease.leaseId);
|
|
6095
|
+
this.leases.delete(lease.leaseId);
|
|
6096
|
+
const queueKey = tracked?.queueKey ?? lease.queueKey;
|
|
6097
|
+
const state = this.queues.get(queueKey);
|
|
6098
|
+
if (!state) {
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
state.running = Math.max(0, state.running - 1);
|
|
6102
|
+
this.drainWaiters(queueKey, state);
|
|
6103
|
+
}
|
|
6104
|
+
};
|
|
6105
|
+
var memoryRateLimitBackend = new MemoryRateLimitBackend();
|
|
6106
|
+
|
|
6107
|
+
// src/ratelimit/backends/index.ts
|
|
6108
|
+
var cachedBackend = null;
|
|
6109
|
+
function shouldForceMemoryBackend() {
|
|
6110
|
+
const flag = process.env.SKEDYUL_RATE_LIMIT_MEMORY;
|
|
6111
|
+
return flag === "1" || flag === "true";
|
|
6112
|
+
}
|
|
6113
|
+
function getRateLimitBackend() {
|
|
6114
|
+
if (cachedBackend) {
|
|
6115
|
+
return cachedBackend;
|
|
6116
|
+
}
|
|
6117
|
+
if (shouldForceMemoryBackend()) {
|
|
6118
|
+
cachedBackend = memoryRateLimitBackend;
|
|
6119
|
+
return cachedBackend;
|
|
6120
|
+
}
|
|
6121
|
+
cachedBackend = createResilientBackend();
|
|
6122
|
+
return cachedBackend;
|
|
6123
|
+
}
|
|
6124
|
+
function createResilientBackend() {
|
|
6125
|
+
return {
|
|
6126
|
+
async acquire(queueKey, limits, timeoutMs) {
|
|
6127
|
+
try {
|
|
6128
|
+
return await platformRateLimitBackend.acquire(queueKey, limits, timeoutMs);
|
|
6129
|
+
} catch (error) {
|
|
6130
|
+
if (shouldFallbackToMemory(error)) {
|
|
6131
|
+
return memoryRateLimitBackend.acquire(queueKey, limits, timeoutMs);
|
|
6132
|
+
}
|
|
6133
|
+
throw error;
|
|
6134
|
+
}
|
|
6135
|
+
},
|
|
6136
|
+
async release(lease) {
|
|
6137
|
+
if (lease.leaseId.startsWith("mem_")) {
|
|
6138
|
+
return memoryRateLimitBackend.release(lease);
|
|
6139
|
+
}
|
|
6140
|
+
try {
|
|
6141
|
+
await platformRateLimitBackend.release(lease);
|
|
6142
|
+
} catch (error) {
|
|
6143
|
+
if (shouldFallbackToMemory(error)) {
|
|
6144
|
+
return memoryRateLimitBackend.release(lease);
|
|
6145
|
+
}
|
|
6146
|
+
throw error;
|
|
6147
|
+
}
|
|
6148
|
+
}
|
|
6149
|
+
};
|
|
6150
|
+
}
|
|
6151
|
+
function shouldFallbackToMemory(error) {
|
|
6152
|
+
if (shouldForceMemoryBackend()) {
|
|
6153
|
+
return true;
|
|
6154
|
+
}
|
|
6155
|
+
if (error instanceof RateLimitBackendError) {
|
|
6156
|
+
if (error.statusCode === 404 || error.statusCode === 503) {
|
|
6157
|
+
return true;
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
if (error instanceof TypeError) {
|
|
6161
|
+
return true;
|
|
6162
|
+
}
|
|
6163
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
6164
|
+
return nodeEnv === "development" || nodeEnv === "test";
|
|
6165
|
+
}
|
|
6166
|
+
|
|
6167
|
+
// src/ratelimit/should-retry.ts
|
|
6168
|
+
var DEFAULT_RETRY_STATUS_CODES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
6169
|
+
function getErrorStatusCode(error) {
|
|
6170
|
+
if (typeof error !== "object" || error === null) {
|
|
6171
|
+
return void 0;
|
|
6172
|
+
}
|
|
6173
|
+
const record2 = error;
|
|
6174
|
+
if (typeof record2.statusCode === "number") {
|
|
6175
|
+
return record2.statusCode;
|
|
6176
|
+
}
|
|
6177
|
+
if (typeof record2.status === "number") {
|
|
6178
|
+
return record2.status;
|
|
6179
|
+
}
|
|
6180
|
+
return void 0;
|
|
6181
|
+
}
|
|
6182
|
+
function getErrorCode(error) {
|
|
6183
|
+
if (typeof error !== "object" || error === null) {
|
|
6184
|
+
return void 0;
|
|
6185
|
+
}
|
|
6186
|
+
const record2 = error;
|
|
6187
|
+
return typeof record2.code === "string" ? record2.code : void 0;
|
|
6188
|
+
}
|
|
6189
|
+
function defaultShouldRetry(error, _attempt) {
|
|
6190
|
+
const code = getErrorCode(error);
|
|
6191
|
+
if (code === "RATE_LIMITED" || code === "RATE_LIMIT_BACKEND_ERROR") {
|
|
6192
|
+
return true;
|
|
6193
|
+
}
|
|
6194
|
+
const status = getErrorStatusCode(error);
|
|
6195
|
+
if (status !== void 0 && DEFAULT_RETRY_STATUS_CODES.has(status)) {
|
|
6196
|
+
return true;
|
|
6197
|
+
}
|
|
6198
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
6199
|
+
const lower = message.toLowerCase();
|
|
6200
|
+
if (lower.includes("rate limit") || lower.includes("too many requests") || lower.includes("econnreset") || lower.includes("etimedout")) {
|
|
6201
|
+
return true;
|
|
6202
|
+
}
|
|
6203
|
+
return false;
|
|
6204
|
+
}
|
|
6205
|
+
function sleep(ms) {
|
|
6206
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
6207
|
+
}
|
|
6208
|
+
|
|
6209
|
+
// src/ratelimit/queued-fetch.ts
|
|
6210
|
+
function normalizeQueueInput(input) {
|
|
6211
|
+
if (typeof input === "string") {
|
|
6212
|
+
return { name: input };
|
|
6213
|
+
}
|
|
6214
|
+
return { name: input.queue, subKey: input.key };
|
|
6215
|
+
}
|
|
6216
|
+
function resolveQueue(queueInput, ctxOverride) {
|
|
6217
|
+
const ctx = ctxOverride ?? getRateLimitExecutionContext();
|
|
6218
|
+
if (!ctx?.app?.versionId) {
|
|
6219
|
+
throw new QueueContextError(
|
|
6220
|
+
"Cannot resolve queue without app context. Ensure queuedFetch runs inside a tool/hook handler."
|
|
6221
|
+
);
|
|
6222
|
+
}
|
|
6223
|
+
const { name, subKey } = normalizeQueueInput(queueInput);
|
|
6224
|
+
const config = getQueueConfigWithRetry(name);
|
|
6225
|
+
if (!config) {
|
|
6226
|
+
throw new QueueNotFoundError(name);
|
|
6227
|
+
}
|
|
6228
|
+
const queueKey = resolveQueueKey(name, config, ctx, subKey);
|
|
6229
|
+
return {
|
|
6230
|
+
name,
|
|
6231
|
+
queueKey,
|
|
6232
|
+
config,
|
|
6233
|
+
limits: toQueueLimits(config)
|
|
6234
|
+
};
|
|
6235
|
+
}
|
|
6236
|
+
function createQueueHandle(queueInput) {
|
|
6237
|
+
return {
|
|
6238
|
+
run(fn) {
|
|
6239
|
+
return queuedFetch(queueInput, fn);
|
|
6240
|
+
}
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6243
|
+
async function queuedFetch(queueInput, fnOrPromise) {
|
|
6244
|
+
const fn = typeof fnOrPromise === "function" ? fnOrPromise : () => fnOrPromise;
|
|
6245
|
+
const resolved = resolveQueue(queueInput);
|
|
6246
|
+
const maxRetries = resolved.config.maxRetries ?? 0;
|
|
6247
|
+
const operation = {
|
|
6248
|
+
queueInput,
|
|
6249
|
+
fn,
|
|
6250
|
+
attempt: 0,
|
|
6251
|
+
resolved,
|
|
6252
|
+
lease: null
|
|
6253
|
+
};
|
|
6254
|
+
return runWithActiveQueuedOperation(
|
|
6255
|
+
operation,
|
|
6256
|
+
() => executeWithRetries(operation, maxRetries)
|
|
6257
|
+
);
|
|
6258
|
+
}
|
|
6259
|
+
async function executeWithRetries(operation, maxRetries) {
|
|
6260
|
+
const backend = getRateLimitBackend();
|
|
6261
|
+
const timeoutMs = operation.resolved.config.timeout ?? 12e4;
|
|
6262
|
+
const retryDelayMs = operation.resolved.config.retryDelayMs ?? 1e3;
|
|
6263
|
+
const shouldRetryFn = getQueueConfigWithRetry(operation.resolved.name)?.shouldRetry ?? defaultShouldRetry;
|
|
6264
|
+
const lease = await backend.acquire(
|
|
6265
|
+
operation.resolved.queueKey,
|
|
6266
|
+
operation.resolved.limits,
|
|
6267
|
+
timeoutMs
|
|
6268
|
+
);
|
|
6269
|
+
operation.lease = lease;
|
|
6270
|
+
setActiveQueuedOperationLease(lease);
|
|
6271
|
+
try {
|
|
6272
|
+
return await operation.fn();
|
|
6273
|
+
} catch (error) {
|
|
6274
|
+
await backend.release(lease);
|
|
6275
|
+
operation.lease = null;
|
|
6276
|
+
setActiveQueuedOperationLease(null);
|
|
6277
|
+
if (shouldRetryFn(error, operation.attempt) && operation.attempt < maxRetries) {
|
|
6278
|
+
await sleep(retryDelayMs);
|
|
6279
|
+
operation.attempt += 1;
|
|
6280
|
+
updateActiveQueuedOperationAttempt(operation.attempt);
|
|
6281
|
+
return executeWithRetries(operation, maxRetries);
|
|
6282
|
+
}
|
|
6283
|
+
if (shouldRetryFn(error, operation.attempt) && operation.attempt >= maxRetries) {
|
|
6284
|
+
throw new QueuedFetchExhaustedError(
|
|
6285
|
+
operation.attempt + 1,
|
|
6286
|
+
maxRetries,
|
|
6287
|
+
error
|
|
6288
|
+
);
|
|
6289
|
+
}
|
|
6290
|
+
throw error;
|
|
6291
|
+
} finally {
|
|
6292
|
+
if (operation.lease) {
|
|
6293
|
+
await backend.release(operation.lease);
|
|
6294
|
+
operation.lease = null;
|
|
6295
|
+
setActiveQueuedOperationLease(null);
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
async function requeue() {
|
|
6300
|
+
const operation = getActiveQueuedOperation();
|
|
6301
|
+
if (!operation) {
|
|
6302
|
+
throw new RequeueOutsideContextError();
|
|
6303
|
+
}
|
|
6304
|
+
const maxRetries = operation.resolved.config.maxRetries ?? 0;
|
|
6305
|
+
const nextAttempt = operation.attempt + 1;
|
|
6306
|
+
if (nextAttempt > maxRetries) {
|
|
6307
|
+
throw new QueuedFetchExhaustedError(nextAttempt, maxRetries, void 0);
|
|
6308
|
+
}
|
|
6309
|
+
if (operation.lease) {
|
|
6310
|
+
const backend = getRateLimitBackend();
|
|
6311
|
+
await backend.release(operation.lease);
|
|
6312
|
+
operation.lease = null;
|
|
6313
|
+
setActiveQueuedOperationLease(null);
|
|
6314
|
+
}
|
|
6315
|
+
operation.attempt = nextAttempt;
|
|
6316
|
+
updateActiveQueuedOperationAttempt(nextAttempt);
|
|
6317
|
+
const retryDelayMs = operation.resolved.config.retryDelayMs ?? 1e3;
|
|
6318
|
+
await sleep(retryDelayMs);
|
|
6319
|
+
return executeWithRetries(operation, maxRetries);
|
|
6320
|
+
}
|
|
6321
|
+
async function queuedFetchResponse(queueInput, url, init) {
|
|
6322
|
+
return queuedFetch(queueInput, () => fetch(url, init));
|
|
6323
|
+
}
|
|
6324
|
+
|
|
5663
6325
|
// src/triggers/types.ts
|
|
5664
6326
|
var import_v49 = require("zod/v4");
|
|
5665
6327
|
var CRMDataSchema = import_v49.z.object({
|
|
@@ -5764,16 +6426,16 @@ function evaluateTemplate(template, context) {
|
|
|
5764
6426
|
const templateRegex = /\{\{\s*([^}]+)\s*\}\}/g;
|
|
5765
6427
|
const singleMatch = template.match(/^\{\{\s*([^}]+)\s*\}\}$/);
|
|
5766
6428
|
if (singleMatch) {
|
|
5767
|
-
const
|
|
5768
|
-
return resolvePath(context,
|
|
6429
|
+
const path6 = singleMatch[1].trim();
|
|
6430
|
+
return resolvePath(context, path6);
|
|
5769
6431
|
}
|
|
5770
|
-
return template.replace(templateRegex, (_,
|
|
5771
|
-
const value = resolvePath(context,
|
|
6432
|
+
return template.replace(templateRegex, (_, path6) => {
|
|
6433
|
+
const value = resolvePath(context, path6.trim());
|
|
5772
6434
|
return value === void 0 || value === null ? "" : String(value);
|
|
5773
6435
|
});
|
|
5774
6436
|
}
|
|
5775
|
-
function resolvePath(obj,
|
|
5776
|
-
const parts =
|
|
6437
|
+
function resolvePath(obj, path6) {
|
|
6438
|
+
const parts = path6.split(".");
|
|
5777
6439
|
let current = obj;
|
|
5778
6440
|
for (const part of parts) {
|
|
5779
6441
|
if (current === null || current === void 0) {
|
|
@@ -5794,14 +6456,14 @@ function evaluateCondition(condition, context) {
|
|
|
5794
6456
|
const expression = match[1].trim();
|
|
5795
6457
|
const eqMatch = expression.match(/^(.+?)\s*==\s*['"](.+)['"]$/);
|
|
5796
6458
|
if (eqMatch) {
|
|
5797
|
-
const [,
|
|
5798
|
-
const actualValue = resolvePath(context,
|
|
6459
|
+
const [, path6, expectedValue] = eqMatch;
|
|
6460
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
5799
6461
|
return actualValue === expectedValue;
|
|
5800
6462
|
}
|
|
5801
6463
|
const neqMatch = expression.match(/^(.+?)\s*!=\s*['"](.+)['"]$/);
|
|
5802
6464
|
if (neqMatch) {
|
|
5803
|
-
const [,
|
|
5804
|
-
const actualValue = resolvePath(context,
|
|
6465
|
+
const [, path6, expectedValue] = neqMatch;
|
|
6466
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
5805
6467
|
return actualValue !== expectedValue;
|
|
5806
6468
|
}
|
|
5807
6469
|
const value = resolvePath(context, expression);
|
|
@@ -7166,10 +7828,15 @@ var index_default = { z: import_v413.z };
|
|
|
7166
7828
|
ParticipantEventPayloadSchema,
|
|
7167
7829
|
ParticipantKindSchema,
|
|
7168
7830
|
ProvisionConfigSchema,
|
|
7831
|
+
QueueContextError,
|
|
7832
|
+
QueueNotFoundError,
|
|
7833
|
+
QueuedFetchExhaustedError,
|
|
7834
|
+
RateLimitBackendError,
|
|
7169
7835
|
RelationshipCardinalitySchema,
|
|
7170
7836
|
RelationshipDefinitionSchema,
|
|
7171
7837
|
RelationshipExtensionSchema,
|
|
7172
7838
|
RelationshipLinkSchema,
|
|
7839
|
+
RequeueOutsideContextError,
|
|
7173
7840
|
ResolvedSkillSchema,
|
|
7174
7841
|
ResolvedTriggerSchema,
|
|
7175
7842
|
ResourceDependencySchema,
|
|
@@ -7247,6 +7914,7 @@ var index_default = { z: import_v413.z };
|
|
|
7247
7914
|
createListResponse,
|
|
7248
7915
|
createNotFoundError,
|
|
7249
7916
|
createPermissionError,
|
|
7917
|
+
createQueueHandle,
|
|
7250
7918
|
createRateLimitError,
|
|
7251
7919
|
createServerHookContext,
|
|
7252
7920
|
createSuccessResponse,
|
|
@@ -7256,6 +7924,7 @@ var index_default = { z: import_v413.z };
|
|
|
7256
7924
|
createWebhookContext,
|
|
7257
7925
|
createWorkflowStepContext,
|
|
7258
7926
|
cron,
|
|
7927
|
+
defaultShouldRetry,
|
|
7259
7928
|
defineAgent,
|
|
7260
7929
|
defineChannel,
|
|
7261
7930
|
defineConfig,
|
|
@@ -7278,6 +7947,7 @@ var index_default = { z: import_v413.z };
|
|
|
7278
7947
|
getConfig,
|
|
7279
7948
|
getContextByHandle,
|
|
7280
7949
|
getContextByModel,
|
|
7950
|
+
getRateLimitExecutionContext,
|
|
7281
7951
|
getRequiredInstallEnvKeys,
|
|
7282
7952
|
getRetryDelay,
|
|
7283
7953
|
instance,
|
|
@@ -7297,10 +7967,16 @@ var index_default = { z: import_v413.z };
|
|
|
7297
7967
|
loadConfig,
|
|
7298
7968
|
matchesTrigger,
|
|
7299
7969
|
parseCRMSchema,
|
|
7970
|
+
queuedFetch,
|
|
7971
|
+
queuedFetchResponse,
|
|
7972
|
+
registerQueueConfig,
|
|
7300
7973
|
report,
|
|
7974
|
+
requeue,
|
|
7301
7975
|
resolveInputMappings,
|
|
7976
|
+
resolveQueue,
|
|
7302
7977
|
resource,
|
|
7303
7978
|
runWithConfig,
|
|
7979
|
+
runWithRateLimitExecutionContext,
|
|
7304
7980
|
safeParseCRMSchema,
|
|
7305
7981
|
safeParseConfig,
|
|
7306
7982
|
server,
|