skedyul 1.2.51 → 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/esm/index.mjs
CHANGED
|
@@ -1852,6 +1852,68 @@ var CoreApiService = class {
|
|
|
1852
1852
|
};
|
|
1853
1853
|
var coreApiService = new CoreApiService();
|
|
1854
1854
|
|
|
1855
|
+
// src/ratelimit/config-loader.ts
|
|
1856
|
+
import * as fs from "fs";
|
|
1857
|
+
import * as path from "path";
|
|
1858
|
+
var cachedRuntimeQueues = null;
|
|
1859
|
+
function getRuntimeConfigPath() {
|
|
1860
|
+
if (process.env.LAMBDA_TASK_ROOT) {
|
|
1861
|
+
return path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json");
|
|
1862
|
+
}
|
|
1863
|
+
return path.join(process.cwd(), ".skedyul", "config.json");
|
|
1864
|
+
}
|
|
1865
|
+
function loadRuntimeConfigFile() {
|
|
1866
|
+
const configPath = getRuntimeConfigPath();
|
|
1867
|
+
if (!fs.existsSync(configPath)) {
|
|
1868
|
+
return null;
|
|
1869
|
+
}
|
|
1870
|
+
try {
|
|
1871
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
1872
|
+
return JSON.parse(raw);
|
|
1873
|
+
} catch {
|
|
1874
|
+
return null;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
var registeredServerConfig = null;
|
|
1878
|
+
function registerQueueConfig(config) {
|
|
1879
|
+
registeredServerConfig = config;
|
|
1880
|
+
cachedRuntimeQueues = config.queues ?? null;
|
|
1881
|
+
}
|
|
1882
|
+
function getQueueDefinitions() {
|
|
1883
|
+
if (cachedRuntimeQueues) {
|
|
1884
|
+
return cachedRuntimeQueues;
|
|
1885
|
+
}
|
|
1886
|
+
if (registeredServerConfig?.queues) {
|
|
1887
|
+
cachedRuntimeQueues = stripNonSerializableQueues(registeredServerConfig.queues);
|
|
1888
|
+
return cachedRuntimeQueues;
|
|
1889
|
+
}
|
|
1890
|
+
const fileConfig = loadRuntimeConfigFile();
|
|
1891
|
+
if (fileConfig?.queues) {
|
|
1892
|
+
cachedRuntimeQueues = fileConfig.queues;
|
|
1893
|
+
return cachedRuntimeQueues;
|
|
1894
|
+
}
|
|
1895
|
+
return {};
|
|
1896
|
+
}
|
|
1897
|
+
function stripNonSerializableQueues(queues) {
|
|
1898
|
+
const result = {};
|
|
1899
|
+
for (const [name, config] of Object.entries(queues)) {
|
|
1900
|
+
const { shouldRetry: _shouldRetry, ...serializable } = config;
|
|
1901
|
+
result[name] = serializable;
|
|
1902
|
+
}
|
|
1903
|
+
return result;
|
|
1904
|
+
}
|
|
1905
|
+
function getQueueConfig(queueName) {
|
|
1906
|
+
return getQueueDefinitions()[queueName];
|
|
1907
|
+
}
|
|
1908
|
+
function getQueueConfigWithRetry(queueName) {
|
|
1909
|
+
const fromServer = registeredServerConfig?.queues?.[queueName];
|
|
1910
|
+
if (fromServer) {
|
|
1911
|
+
return fromServer;
|
|
1912
|
+
}
|
|
1913
|
+
const serializable = getQueueConfig(queueName);
|
|
1914
|
+
return serializable;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1855
1917
|
// src/server/utils/schema.ts
|
|
1856
1918
|
import * as z8 from "zod";
|
|
1857
1919
|
function normalizeBilling(billing) {
|
|
@@ -3173,6 +3235,35 @@ var report = {
|
|
|
3173
3235
|
}
|
|
3174
3236
|
};
|
|
3175
3237
|
|
|
3238
|
+
// src/ratelimit/context.ts
|
|
3239
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
3240
|
+
var executionContextStorage = new AsyncLocalStorage2();
|
|
3241
|
+
var activeOperationStorage = new AsyncLocalStorage2();
|
|
3242
|
+
function runWithRateLimitExecutionContext(ctx, fn) {
|
|
3243
|
+
return executionContextStorage.run(ctx, fn);
|
|
3244
|
+
}
|
|
3245
|
+
function getRateLimitExecutionContext() {
|
|
3246
|
+
return executionContextStorage.getStore();
|
|
3247
|
+
}
|
|
3248
|
+
function runWithActiveQueuedOperation(operation, fn) {
|
|
3249
|
+
return activeOperationStorage.run(operation, fn);
|
|
3250
|
+
}
|
|
3251
|
+
function getActiveQueuedOperation() {
|
|
3252
|
+
return activeOperationStorage.getStore();
|
|
3253
|
+
}
|
|
3254
|
+
function setActiveQueuedOperationLease(lease) {
|
|
3255
|
+
const op = activeOperationStorage.getStore();
|
|
3256
|
+
if (op) {
|
|
3257
|
+
op.lease = lease;
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
function updateActiveQueuedOperationAttempt(attempt) {
|
|
3261
|
+
const op = activeOperationStorage.getStore();
|
|
3262
|
+
if (op) {
|
|
3263
|
+
op.attempt = attempt;
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3176
3267
|
// src/errors.ts
|
|
3177
3268
|
var InstallError = class extends Error {
|
|
3178
3269
|
// Optional: which field caused the error
|
|
@@ -3227,8 +3318,8 @@ var AppAuthInvalidError = class extends Error {
|
|
|
3227
3318
|
};
|
|
3228
3319
|
|
|
3229
3320
|
// src/server/context-logger.ts
|
|
3230
|
-
import { AsyncLocalStorage as
|
|
3231
|
-
var logContextStorage = new
|
|
3321
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
3322
|
+
var logContextStorage = new AsyncLocalStorage3();
|
|
3232
3323
|
function runWithLogContext(context, fn) {
|
|
3233
3324
|
return logContextStorage.run(context, fn);
|
|
3234
3325
|
}
|
|
@@ -3441,9 +3532,17 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
3441
3532
|
baseUrl: toolEnv.SKEDYUL_API_URL ?? "",
|
|
3442
3533
|
apiToken: toolEnv.SKEDYUL_API_TOKEN ?? ""
|
|
3443
3534
|
};
|
|
3535
|
+
const rateLimitContext = {
|
|
3536
|
+
app,
|
|
3537
|
+
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
3538
|
+
invocation,
|
|
3539
|
+
isProvisionContext: trigger === "provision"
|
|
3540
|
+
};
|
|
3444
3541
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
3445
|
-
return await
|
|
3446
|
-
return await
|
|
3542
|
+
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
3543
|
+
return await runWithLogContext({ invocation }, async () => {
|
|
3544
|
+
return await fn(inputs, executionContext);
|
|
3545
|
+
});
|
|
3447
3546
|
});
|
|
3448
3547
|
});
|
|
3449
3548
|
const billing = normalizeBilling(functionResult.billing);
|
|
@@ -3580,7 +3679,7 @@ function printStartupLog(config, tools, port) {
|
|
|
3580
3679
|
|
|
3581
3680
|
// src/server/route-handlers/adapters.ts
|
|
3582
3681
|
function fromLambdaEvent(event2) {
|
|
3583
|
-
const
|
|
3682
|
+
const path6 = event2.path || event2.rawPath || "/";
|
|
3584
3683
|
const method = event2.httpMethod || event2.requestContext?.http?.method || "POST";
|
|
3585
3684
|
const forwardedProto = event2.headers?.["x-forwarded-proto"] ?? event2.headers?.["X-Forwarded-Proto"];
|
|
3586
3685
|
const protocol = forwardedProto ?? "https";
|
|
@@ -3588,9 +3687,9 @@ function fromLambdaEvent(event2) {
|
|
|
3588
3687
|
const queryString = event2.queryStringParameters ? "?" + new URLSearchParams(
|
|
3589
3688
|
event2.queryStringParameters
|
|
3590
3689
|
).toString() : "";
|
|
3591
|
-
const url = `${protocol}://${host}${
|
|
3690
|
+
const url = `${protocol}://${host}${path6}${queryString}`;
|
|
3592
3691
|
return {
|
|
3593
|
-
path:
|
|
3692
|
+
path: path6,
|
|
3594
3693
|
method,
|
|
3595
3694
|
headers: event2.headers,
|
|
3596
3695
|
query: event2.queryStringParameters ?? {},
|
|
@@ -3672,8 +3771,8 @@ function parseBodyByContentType(req) {
|
|
|
3672
3771
|
}
|
|
3673
3772
|
|
|
3674
3773
|
// src/server/route-handlers/handlers.ts
|
|
3675
|
-
import * as
|
|
3676
|
-
import * as
|
|
3774
|
+
import * as fs2 from "fs";
|
|
3775
|
+
import * as path2 from "path";
|
|
3677
3776
|
|
|
3678
3777
|
// src/server/core-api-handler.ts
|
|
3679
3778
|
async function handleCoreMethod(method, params) {
|
|
@@ -3841,7 +3940,8 @@ function serializeConfig(config) {
|
|
|
3841
3940
|
type: w.type ?? "WEBHOOK"
|
|
3842
3941
|
})) : [],
|
|
3843
3942
|
provision: isProvisionConfig(config.provision) ? config.provision : void 0,
|
|
3844
|
-
agents: config.agents
|
|
3943
|
+
agents: config.agents,
|
|
3944
|
+
queues: config.queues
|
|
3845
3945
|
};
|
|
3846
3946
|
}
|
|
3847
3947
|
function isProvisionConfig(value) {
|
|
@@ -4137,7 +4237,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
|
|
|
4137
4237
|
}
|
|
4138
4238
|
|
|
4139
4239
|
// src/server/handlers/webhook-handler.ts
|
|
4140
|
-
function parseWebhookRequest(parsedBody, method, url,
|
|
4240
|
+
function parseWebhookRequest(parsedBody, method, url, path6, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
|
|
4141
4241
|
const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
|
|
4142
4242
|
if (isEnvelope) {
|
|
4143
4243
|
const envelope = parsedBody;
|
|
@@ -4191,7 +4291,7 @@ function parseWebhookRequest(parsedBody, method, url, path5, headers, query, raw
|
|
|
4191
4291
|
const webhookRequest = {
|
|
4192
4292
|
method,
|
|
4193
4293
|
url,
|
|
4194
|
-
path:
|
|
4294
|
+
path: path6,
|
|
4195
4295
|
headers,
|
|
4196
4296
|
query,
|
|
4197
4297
|
body: parsedBody,
|
|
@@ -4249,7 +4349,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
4249
4349
|
|
|
4250
4350
|
// src/server/route-handlers/handlers.ts
|
|
4251
4351
|
function getConfigFilePath() {
|
|
4252
|
-
return process.env.LAMBDA_TASK_ROOT ?
|
|
4352
|
+
return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
4253
4353
|
}
|
|
4254
4354
|
function findToolInRegistry(registry, toolName) {
|
|
4255
4355
|
for (const [key, t] of Object.entries(registry)) {
|
|
@@ -4288,8 +4388,8 @@ function handleHealthRoute(ctx) {
|
|
|
4288
4388
|
function handleConfigRoute(ctx) {
|
|
4289
4389
|
const configFilePath = getConfigFilePath();
|
|
4290
4390
|
try {
|
|
4291
|
-
if (
|
|
4292
|
-
const fileConfig = JSON.parse(
|
|
4391
|
+
if (fs2.existsSync(configFilePath)) {
|
|
4392
|
+
const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
|
|
4293
4393
|
return { status: 200, body: fileConfig };
|
|
4294
4394
|
}
|
|
4295
4395
|
} catch (err) {
|
|
@@ -4974,6 +5074,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
4974
5074
|
installContextLogger();
|
|
4975
5075
|
function createSkedyulServer(config) {
|
|
4976
5076
|
mergeRuntimeEnv();
|
|
5077
|
+
registerQueueConfig(config);
|
|
4977
5078
|
const registry = config.tools;
|
|
4978
5079
|
const webhookRegistry = config.webhooks;
|
|
4979
5080
|
if (config.coreApi?.service) {
|
|
@@ -5242,8 +5343,8 @@ function defineNavigation(navigation) {
|
|
|
5242
5343
|
}
|
|
5243
5344
|
|
|
5244
5345
|
// src/config/loader.ts
|
|
5245
|
-
import * as
|
|
5246
|
-
import * as
|
|
5346
|
+
import * as fs3 from "fs";
|
|
5347
|
+
import * as path3 from "path";
|
|
5247
5348
|
import * as os from "os";
|
|
5248
5349
|
var CONFIG_FILE_NAMES = [
|
|
5249
5350
|
"skedyul.config.ts",
|
|
@@ -5252,13 +5353,13 @@ var CONFIG_FILE_NAMES = [
|
|
|
5252
5353
|
"skedyul.config.cjs"
|
|
5253
5354
|
];
|
|
5254
5355
|
async function transpileTypeScript(filePath) {
|
|
5255
|
-
const content =
|
|
5256
|
-
const configDir =
|
|
5356
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5357
|
+
const configDir = path3.dirname(path3.resolve(filePath));
|
|
5257
5358
|
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*$/, "}");
|
|
5258
5359
|
transpiled = transpiled.replace(
|
|
5259
5360
|
/import\s+(\w+)\s+from\s+['"](\.[^'"]+)['"]\s*(?:with\s*\{[^}]*\})?/g,
|
|
5260
5361
|
(match, varName, relativePath) => {
|
|
5261
|
-
const absolutePath =
|
|
5362
|
+
const absolutePath = path3.resolve(configDir, relativePath);
|
|
5262
5363
|
return `const ${varName} = require('${absolutePath.replace(/\\/g, "/")}')`;
|
|
5263
5364
|
}
|
|
5264
5365
|
);
|
|
@@ -5266,8 +5367,8 @@ async function transpileTypeScript(filePath) {
|
|
|
5266
5367
|
return transpiled;
|
|
5267
5368
|
}
|
|
5268
5369
|
async function loadConfig(configPath) {
|
|
5269
|
-
const absolutePath =
|
|
5270
|
-
if (!
|
|
5370
|
+
const absolutePath = path3.resolve(configPath);
|
|
5371
|
+
if (!fs3.existsSync(absolutePath)) {
|
|
5271
5372
|
throw new Error(`Config file not found: ${absolutePath}`);
|
|
5272
5373
|
}
|
|
5273
5374
|
const isTypeScript = absolutePath.endsWith(".ts");
|
|
@@ -5276,8 +5377,8 @@ async function loadConfig(configPath) {
|
|
|
5276
5377
|
if (isTypeScript) {
|
|
5277
5378
|
const transpiled = await transpileTypeScript(absolutePath);
|
|
5278
5379
|
const tempDir = os.tmpdir();
|
|
5279
|
-
const tempFile =
|
|
5280
|
-
|
|
5380
|
+
const tempFile = path3.join(tempDir, `skedyul-config-${Date.now()}.cjs`);
|
|
5381
|
+
fs3.writeFileSync(tempFile, transpiled);
|
|
5281
5382
|
moduleToLoad = tempFile;
|
|
5282
5383
|
try {
|
|
5283
5384
|
const module2 = __require(moduleToLoad);
|
|
@@ -5291,7 +5392,7 @@ async function loadConfig(configPath) {
|
|
|
5291
5392
|
return config2;
|
|
5292
5393
|
} finally {
|
|
5293
5394
|
try {
|
|
5294
|
-
|
|
5395
|
+
fs3.unlinkSync(tempFile);
|
|
5295
5396
|
} catch {
|
|
5296
5397
|
}
|
|
5297
5398
|
}
|
|
@@ -5326,13 +5427,13 @@ function validateConfig(config) {
|
|
|
5326
5427
|
}
|
|
5327
5428
|
|
|
5328
5429
|
// src/config/schema-loader.ts
|
|
5329
|
-
import * as
|
|
5330
|
-
import * as
|
|
5430
|
+
import * as fs4 from "fs";
|
|
5431
|
+
import * as path4 from "path";
|
|
5331
5432
|
import * as os2 from "os";
|
|
5332
5433
|
|
|
5333
5434
|
// src/config/resolver.ts
|
|
5334
|
-
import * as
|
|
5335
|
-
import * as
|
|
5435
|
+
import * as fs5 from "fs";
|
|
5436
|
+
import * as path5 from "path";
|
|
5336
5437
|
|
|
5337
5438
|
// src/config/utils.ts
|
|
5338
5439
|
function getAllEnvKeys(config) {
|
|
@@ -5349,6 +5450,553 @@ function getRequiredInstallEnvKeys(config) {
|
|
|
5349
5450
|
return Object.entries(provision.env).filter(([, def]) => def.required).map(([key]) => key);
|
|
5350
5451
|
}
|
|
5351
5452
|
|
|
5453
|
+
// src/ratelimit/errors.ts
|
|
5454
|
+
var QueueContextError = class extends Error {
|
|
5455
|
+
constructor(message) {
|
|
5456
|
+
super(message);
|
|
5457
|
+
this.code = "QUEUE_CONTEXT_ERROR";
|
|
5458
|
+
this.name = "QueueContextError";
|
|
5459
|
+
}
|
|
5460
|
+
};
|
|
5461
|
+
var QueueNotFoundError = class extends Error {
|
|
5462
|
+
constructor(queueName) {
|
|
5463
|
+
super(`Queue "${queueName}" is not defined in skedyul.config queues`);
|
|
5464
|
+
this.code = "QUEUE_NOT_FOUND";
|
|
5465
|
+
this.name = "QueueNotFoundError";
|
|
5466
|
+
}
|
|
5467
|
+
};
|
|
5468
|
+
var QueuedFetchExhaustedError = class extends Error {
|
|
5469
|
+
constructor(attempts, maxRetries, causeError) {
|
|
5470
|
+
super(
|
|
5471
|
+
`queuedFetch exhausted retries after ${attempts} attempts (maxRetries=${maxRetries})`
|
|
5472
|
+
);
|
|
5473
|
+
this.code = "QUEUED_FETCH_EXHAUSTED";
|
|
5474
|
+
this.name = "QueuedFetchExhaustedError";
|
|
5475
|
+
this.attempts = attempts;
|
|
5476
|
+
this.maxRetries = maxRetries;
|
|
5477
|
+
this.causeError = causeError;
|
|
5478
|
+
}
|
|
5479
|
+
};
|
|
5480
|
+
var RequeueOutsideContextError = class extends Error {
|
|
5481
|
+
constructor() {
|
|
5482
|
+
super("requeue() can only be called inside an active queuedFetch operation");
|
|
5483
|
+
this.code = "REQUEUE_OUTSIDE_CONTEXT";
|
|
5484
|
+
this.name = "RequeueOutsideContextError";
|
|
5485
|
+
}
|
|
5486
|
+
};
|
|
5487
|
+
var RateLimitBackendError = class extends Error {
|
|
5488
|
+
constructor(message, statusCode) {
|
|
5489
|
+
super(message);
|
|
5490
|
+
this.code = "RATE_LIMIT_BACKEND_ERROR";
|
|
5491
|
+
this.name = "RateLimitBackendError";
|
|
5492
|
+
this.statusCode = statusCode;
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
|
|
5496
|
+
// src/ratelimit/resolve-queue-key.ts
|
|
5497
|
+
function resolveEndpointHandle(config, invocation) {
|
|
5498
|
+
if (config.endpoint) {
|
|
5499
|
+
return config.endpoint;
|
|
5500
|
+
}
|
|
5501
|
+
if (invocation?.toolHandle) {
|
|
5502
|
+
return invocation.toolHandle;
|
|
5503
|
+
}
|
|
5504
|
+
if (invocation?.serverHookHandle) {
|
|
5505
|
+
return invocation.serverHookHandle;
|
|
5506
|
+
}
|
|
5507
|
+
throw new QueueContextError(
|
|
5508
|
+
`Cannot resolve endpoint handle for queue scope "${config.scope}". Set endpoint in queue config or ensure invocation.toolHandle/serverHookHandle is available.`
|
|
5509
|
+
);
|
|
5510
|
+
}
|
|
5511
|
+
function appendSubKey(base, subKey) {
|
|
5512
|
+
return subKey ? `${base}:${subKey}` : base;
|
|
5513
|
+
}
|
|
5514
|
+
function resolveQueueKey(queueName, config, ctx, subKey) {
|
|
5515
|
+
const { app, appInstallationId, invocation, isProvisionContext: isProvisionContext2 } = ctx;
|
|
5516
|
+
switch (config.scope) {
|
|
5517
|
+
case "provision": {
|
|
5518
|
+
const base = `rl:pv:${app.versionId}:${queueName}`;
|
|
5519
|
+
return appendSubKey(base, subKey);
|
|
5520
|
+
}
|
|
5521
|
+
case "install": {
|
|
5522
|
+
if (!appInstallationId) {
|
|
5523
|
+
throw new QueueContextError(
|
|
5524
|
+
`Queue "${queueName}" with scope "install" requires appInstallationId in context`
|
|
5525
|
+
);
|
|
5526
|
+
}
|
|
5527
|
+
const base = `rl:in:${appInstallationId}:${queueName}`;
|
|
5528
|
+
return appendSubKey(base, subKey);
|
|
5529
|
+
}
|
|
5530
|
+
case "provision_endpoint": {
|
|
5531
|
+
if (!isProvisionContext2 && appInstallationId) {
|
|
5532
|
+
throw new QueueContextError(
|
|
5533
|
+
`Queue "${queueName}" with scope "provision_endpoint" requires provision context (no install)`
|
|
5534
|
+
);
|
|
5535
|
+
}
|
|
5536
|
+
const endpointHandle = resolveEndpointHandle(config, invocation);
|
|
5537
|
+
const base = `rl:pep:${app.versionId}:${endpointHandle}:${queueName}`;
|
|
5538
|
+
return appendSubKey(base, subKey);
|
|
5539
|
+
}
|
|
5540
|
+
case "install_endpoint": {
|
|
5541
|
+
if (!appInstallationId) {
|
|
5542
|
+
throw new QueueContextError(
|
|
5543
|
+
`Queue "${queueName}" with scope "install_endpoint" requires appInstallationId in context`
|
|
5544
|
+
);
|
|
5545
|
+
}
|
|
5546
|
+
const endpointHandle = resolveEndpointHandle(config, invocation);
|
|
5547
|
+
const base = `rl:iep:${appInstallationId}:${endpointHandle}:${queueName}`;
|
|
5548
|
+
return appendSubKey(base, subKey);
|
|
5549
|
+
}
|
|
5550
|
+
case "global": {
|
|
5551
|
+
const base = `rl:gl:${app.versionId}:${queueName}`;
|
|
5552
|
+
return appendSubKey(base, subKey);
|
|
5553
|
+
}
|
|
5554
|
+
default: {
|
|
5555
|
+
const _exhaustive = config.scope;
|
|
5556
|
+
throw new QueueContextError(`Unknown queue scope: ${String(_exhaustive)}`);
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
function toQueueLimits(config) {
|
|
5561
|
+
return {
|
|
5562
|
+
maxConcurrent: config.maxConcurrent,
|
|
5563
|
+
minTime: config.minTime,
|
|
5564
|
+
reservoir: config.reservoir,
|
|
5565
|
+
reservoirRefreshAmount: config.reservoirRefreshAmount,
|
|
5566
|
+
reservoirRefreshInterval: config.reservoirRefreshInterval
|
|
5567
|
+
};
|
|
5568
|
+
}
|
|
5569
|
+
|
|
5570
|
+
// src/ratelimit/backends/platform.ts
|
|
5571
|
+
function getApiBaseUrl() {
|
|
5572
|
+
const config = getConfig();
|
|
5573
|
+
if (config.baseUrl) {
|
|
5574
|
+
return config.baseUrl.replace(/\/+$/, "");
|
|
5575
|
+
}
|
|
5576
|
+
return (process.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_NODE_URL ?? "").replace(/\/+$/, "");
|
|
5577
|
+
}
|
|
5578
|
+
function getApiToken() {
|
|
5579
|
+
const config = getConfig();
|
|
5580
|
+
return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
|
|
5581
|
+
}
|
|
5582
|
+
async function callRateLimitApi(path6, body) {
|
|
5583
|
+
const baseUrl = getApiBaseUrl();
|
|
5584
|
+
const token2 = getApiToken();
|
|
5585
|
+
if (!baseUrl || !token2) {
|
|
5586
|
+
throw new RateLimitBackendError(
|
|
5587
|
+
"SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform rate limiting"
|
|
5588
|
+
);
|
|
5589
|
+
}
|
|
5590
|
+
const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path6}`, {
|
|
5591
|
+
method: "POST",
|
|
5592
|
+
headers: {
|
|
5593
|
+
Authorization: `Bearer ${token2}`,
|
|
5594
|
+
"Content-Type": "application/json"
|
|
5595
|
+
},
|
|
5596
|
+
body: JSON.stringify(body)
|
|
5597
|
+
});
|
|
5598
|
+
const payload = await response.json().catch(() => null);
|
|
5599
|
+
if (!response.ok || payload?.success === false) {
|
|
5600
|
+
const message = payload?.errors?.[0]?.message ?? `Rate limit API ${path6} failed with status ${response.status}`;
|
|
5601
|
+
throw new RateLimitBackendError(message, response.status);
|
|
5602
|
+
}
|
|
5603
|
+
if (!payload?.data) {
|
|
5604
|
+
throw new RateLimitBackendError(`Rate limit API ${path6} returned empty data`);
|
|
5605
|
+
}
|
|
5606
|
+
return payload.data;
|
|
5607
|
+
}
|
|
5608
|
+
var PlatformRateLimitBackend = class {
|
|
5609
|
+
async acquire(queueKey, limits, timeoutMs) {
|
|
5610
|
+
const data = await callRateLimitApi("acquire", {
|
|
5611
|
+
queueKey,
|
|
5612
|
+
limits,
|
|
5613
|
+
timeoutMs
|
|
5614
|
+
});
|
|
5615
|
+
return {
|
|
5616
|
+
leaseId: data.leaseId,
|
|
5617
|
+
acquiredAt: data.acquiredAt,
|
|
5618
|
+
queueKey: data.queueKey
|
|
5619
|
+
};
|
|
5620
|
+
}
|
|
5621
|
+
async release(lease) {
|
|
5622
|
+
await callRateLimitApi("release", {
|
|
5623
|
+
leaseId: lease.leaseId
|
|
5624
|
+
});
|
|
5625
|
+
}
|
|
5626
|
+
};
|
|
5627
|
+
var platformRateLimitBackend = new PlatformRateLimitBackend();
|
|
5628
|
+
|
|
5629
|
+
// src/ratelimit/backends/memory.ts
|
|
5630
|
+
var DEFAULT_MAX_CONCURRENT = 10;
|
|
5631
|
+
function getMaxConcurrent(limits) {
|
|
5632
|
+
return limits.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
|
|
5633
|
+
}
|
|
5634
|
+
function getMinTime(limits) {
|
|
5635
|
+
return limits.minTime ?? 0;
|
|
5636
|
+
}
|
|
5637
|
+
function generateLeaseId() {
|
|
5638
|
+
return `mem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5639
|
+
}
|
|
5640
|
+
function refillTokens(state, limits) {
|
|
5641
|
+
if (limits.reservoir === void 0) {
|
|
5642
|
+
return;
|
|
5643
|
+
}
|
|
5644
|
+
const capacity = limits.reservoir;
|
|
5645
|
+
const refreshAmount = limits.reservoirRefreshAmount ?? limits.reservoir;
|
|
5646
|
+
const refreshInterval = limits.reservoirRefreshInterval ?? 6e4;
|
|
5647
|
+
const now = Date.now();
|
|
5648
|
+
const elapsed = now - state.lastRefillAt;
|
|
5649
|
+
if (elapsed >= refreshInterval) {
|
|
5650
|
+
const intervals = Math.floor(elapsed / refreshInterval);
|
|
5651
|
+
state.tokens = Math.min(capacity, state.tokens + intervals * refreshAmount);
|
|
5652
|
+
state.lastRefillAt = now;
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5655
|
+
function canAcquire(state, limits) {
|
|
5656
|
+
if (state.running >= getMaxConcurrent(limits)) {
|
|
5657
|
+
return false;
|
|
5658
|
+
}
|
|
5659
|
+
refillTokens(state, limits);
|
|
5660
|
+
if (limits.reservoir !== void 0 && state.tokens <= 0) {
|
|
5661
|
+
return false;
|
|
5662
|
+
}
|
|
5663
|
+
const minTime = getMinTime(limits);
|
|
5664
|
+
if (minTime > 0 && state.lastStartAt > 0) {
|
|
5665
|
+
if (Date.now() - state.lastStartAt < minTime) {
|
|
5666
|
+
return false;
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
return true;
|
|
5670
|
+
}
|
|
5671
|
+
var MemoryRateLimitBackend = class {
|
|
5672
|
+
constructor() {
|
|
5673
|
+
this.queues = /* @__PURE__ */ new Map();
|
|
5674
|
+
this.leases = /* @__PURE__ */ new Map();
|
|
5675
|
+
}
|
|
5676
|
+
getState(queueKey) {
|
|
5677
|
+
let state = this.queues.get(queueKey);
|
|
5678
|
+
if (!state) {
|
|
5679
|
+
state = {
|
|
5680
|
+
running: 0,
|
|
5681
|
+
waiters: [],
|
|
5682
|
+
lastStartAt: 0,
|
|
5683
|
+
tokens: Number.POSITIVE_INFINITY,
|
|
5684
|
+
lastRefillAt: Date.now()
|
|
5685
|
+
};
|
|
5686
|
+
if (queueKey.includes(":")) {
|
|
5687
|
+
}
|
|
5688
|
+
this.queues.set(queueKey, state);
|
|
5689
|
+
}
|
|
5690
|
+
return state;
|
|
5691
|
+
}
|
|
5692
|
+
grant(state, queueKey, limits) {
|
|
5693
|
+
if (limits.reservoir !== void 0) {
|
|
5694
|
+
state.tokens -= 1;
|
|
5695
|
+
}
|
|
5696
|
+
state.running += 1;
|
|
5697
|
+
state.lastStartAt = Date.now();
|
|
5698
|
+
const leaseId = generateLeaseId();
|
|
5699
|
+
this.leases.set(leaseId, { queueKey, limits });
|
|
5700
|
+
return { leaseId, acquiredAt: Date.now(), queueKey };
|
|
5701
|
+
}
|
|
5702
|
+
drainWaiters(queueKey, state) {
|
|
5703
|
+
let progressed = true;
|
|
5704
|
+
while (progressed && state.waiters.length > 0) {
|
|
5705
|
+
progressed = false;
|
|
5706
|
+
for (let i = 0; i < state.waiters.length; i += 1) {
|
|
5707
|
+
const waiter = state.waiters[i];
|
|
5708
|
+
if (!waiter || !canAcquire(state, waiter.limits)) {
|
|
5709
|
+
continue;
|
|
5710
|
+
}
|
|
5711
|
+
state.waiters.splice(i, 1);
|
|
5712
|
+
if (waiter.timer) {
|
|
5713
|
+
clearTimeout(waiter.timer);
|
|
5714
|
+
}
|
|
5715
|
+
waiter.resolve(this.grant(state, queueKey, waiter.limits));
|
|
5716
|
+
progressed = true;
|
|
5717
|
+
break;
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
async acquire(queueKey, limits, timeoutMs = 12e4) {
|
|
5722
|
+
const state = this.getState(queueKey);
|
|
5723
|
+
if (limits.reservoir !== void 0 && state.tokens === Number.POSITIVE_INFINITY) {
|
|
5724
|
+
state.tokens = limits.reservoir;
|
|
5725
|
+
}
|
|
5726
|
+
if (canAcquire(state, limits)) {
|
|
5727
|
+
return this.grant(state, queueKey, limits);
|
|
5728
|
+
}
|
|
5729
|
+
return new Promise((resolve4, reject) => {
|
|
5730
|
+
const waiter = {
|
|
5731
|
+
resolve: resolve4,
|
|
5732
|
+
reject,
|
|
5733
|
+
timer: null,
|
|
5734
|
+
limits
|
|
5735
|
+
};
|
|
5736
|
+
if (timeoutMs > 0) {
|
|
5737
|
+
waiter.timer = setTimeout(() => {
|
|
5738
|
+
const idx = state.waiters.indexOf(waiter);
|
|
5739
|
+
if (idx >= 0) {
|
|
5740
|
+
state.waiters.splice(idx, 1);
|
|
5741
|
+
}
|
|
5742
|
+
reject(
|
|
5743
|
+
new Error(`Rate limit acquire timed out after ${timeoutMs}ms for ${queueKey}`)
|
|
5744
|
+
);
|
|
5745
|
+
}, timeoutMs);
|
|
5746
|
+
}
|
|
5747
|
+
state.waiters.push(waiter);
|
|
5748
|
+
const retry = () => {
|
|
5749
|
+
if (!state.waiters.includes(waiter)) {
|
|
5750
|
+
return;
|
|
5751
|
+
}
|
|
5752
|
+
if (canAcquire(state, limits)) {
|
|
5753
|
+
const idx = state.waiters.indexOf(waiter);
|
|
5754
|
+
if (idx >= 0) {
|
|
5755
|
+
state.waiters.splice(idx, 1);
|
|
5756
|
+
}
|
|
5757
|
+
if (waiter.timer) {
|
|
5758
|
+
clearTimeout(waiter.timer);
|
|
5759
|
+
}
|
|
5760
|
+
resolve4(this.grant(state, queueKey, limits));
|
|
5761
|
+
return;
|
|
5762
|
+
}
|
|
5763
|
+
setTimeout(retry, Math.max(getMinTime(limits), 10));
|
|
5764
|
+
};
|
|
5765
|
+
setTimeout(retry, Math.max(getMinTime(limits), 10));
|
|
5766
|
+
});
|
|
5767
|
+
}
|
|
5768
|
+
async release(lease) {
|
|
5769
|
+
const tracked = this.leases.get(lease.leaseId);
|
|
5770
|
+
this.leases.delete(lease.leaseId);
|
|
5771
|
+
const queueKey = tracked?.queueKey ?? lease.queueKey;
|
|
5772
|
+
const state = this.queues.get(queueKey);
|
|
5773
|
+
if (!state) {
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
state.running = Math.max(0, state.running - 1);
|
|
5777
|
+
this.drainWaiters(queueKey, state);
|
|
5778
|
+
}
|
|
5779
|
+
};
|
|
5780
|
+
var memoryRateLimitBackend = new MemoryRateLimitBackend();
|
|
5781
|
+
|
|
5782
|
+
// src/ratelimit/backends/index.ts
|
|
5783
|
+
var cachedBackend = null;
|
|
5784
|
+
function shouldForceMemoryBackend() {
|
|
5785
|
+
const flag = process.env.SKEDYUL_RATE_LIMIT_MEMORY;
|
|
5786
|
+
return flag === "1" || flag === "true";
|
|
5787
|
+
}
|
|
5788
|
+
function getRateLimitBackend() {
|
|
5789
|
+
if (cachedBackend) {
|
|
5790
|
+
return cachedBackend;
|
|
5791
|
+
}
|
|
5792
|
+
if (shouldForceMemoryBackend()) {
|
|
5793
|
+
cachedBackend = memoryRateLimitBackend;
|
|
5794
|
+
return cachedBackend;
|
|
5795
|
+
}
|
|
5796
|
+
cachedBackend = createResilientBackend();
|
|
5797
|
+
return cachedBackend;
|
|
5798
|
+
}
|
|
5799
|
+
function createResilientBackend() {
|
|
5800
|
+
return {
|
|
5801
|
+
async acquire(queueKey, limits, timeoutMs) {
|
|
5802
|
+
try {
|
|
5803
|
+
return await platformRateLimitBackend.acquire(queueKey, limits, timeoutMs);
|
|
5804
|
+
} catch (error) {
|
|
5805
|
+
if (shouldFallbackToMemory(error)) {
|
|
5806
|
+
return memoryRateLimitBackend.acquire(queueKey, limits, timeoutMs);
|
|
5807
|
+
}
|
|
5808
|
+
throw error;
|
|
5809
|
+
}
|
|
5810
|
+
},
|
|
5811
|
+
async release(lease) {
|
|
5812
|
+
if (lease.leaseId.startsWith("mem_")) {
|
|
5813
|
+
return memoryRateLimitBackend.release(lease);
|
|
5814
|
+
}
|
|
5815
|
+
try {
|
|
5816
|
+
await platformRateLimitBackend.release(lease);
|
|
5817
|
+
} catch (error) {
|
|
5818
|
+
if (shouldFallbackToMemory(error)) {
|
|
5819
|
+
return memoryRateLimitBackend.release(lease);
|
|
5820
|
+
}
|
|
5821
|
+
throw error;
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
};
|
|
5825
|
+
}
|
|
5826
|
+
function shouldFallbackToMemory(error) {
|
|
5827
|
+
if (shouldForceMemoryBackend()) {
|
|
5828
|
+
return true;
|
|
5829
|
+
}
|
|
5830
|
+
if (error instanceof RateLimitBackendError) {
|
|
5831
|
+
if (error.statusCode === 404 || error.statusCode === 503) {
|
|
5832
|
+
return true;
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
if (error instanceof TypeError) {
|
|
5836
|
+
return true;
|
|
5837
|
+
}
|
|
5838
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
5839
|
+
return nodeEnv === "development" || nodeEnv === "test";
|
|
5840
|
+
}
|
|
5841
|
+
|
|
5842
|
+
// src/ratelimit/should-retry.ts
|
|
5843
|
+
var DEFAULT_RETRY_STATUS_CODES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
5844
|
+
function getErrorStatusCode(error) {
|
|
5845
|
+
if (typeof error !== "object" || error === null) {
|
|
5846
|
+
return void 0;
|
|
5847
|
+
}
|
|
5848
|
+
const record2 = error;
|
|
5849
|
+
if (typeof record2.statusCode === "number") {
|
|
5850
|
+
return record2.statusCode;
|
|
5851
|
+
}
|
|
5852
|
+
if (typeof record2.status === "number") {
|
|
5853
|
+
return record2.status;
|
|
5854
|
+
}
|
|
5855
|
+
return void 0;
|
|
5856
|
+
}
|
|
5857
|
+
function getErrorCode(error) {
|
|
5858
|
+
if (typeof error !== "object" || error === null) {
|
|
5859
|
+
return void 0;
|
|
5860
|
+
}
|
|
5861
|
+
const record2 = error;
|
|
5862
|
+
return typeof record2.code === "string" ? record2.code : void 0;
|
|
5863
|
+
}
|
|
5864
|
+
function defaultShouldRetry(error, _attempt) {
|
|
5865
|
+
const code = getErrorCode(error);
|
|
5866
|
+
if (code === "RATE_LIMITED" || code === "RATE_LIMIT_BACKEND_ERROR") {
|
|
5867
|
+
return true;
|
|
5868
|
+
}
|
|
5869
|
+
const status = getErrorStatusCode(error);
|
|
5870
|
+
if (status !== void 0 && DEFAULT_RETRY_STATUS_CODES.has(status)) {
|
|
5871
|
+
return true;
|
|
5872
|
+
}
|
|
5873
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
5874
|
+
const lower = message.toLowerCase();
|
|
5875
|
+
if (lower.includes("rate limit") || lower.includes("too many requests") || lower.includes("econnreset") || lower.includes("etimedout")) {
|
|
5876
|
+
return true;
|
|
5877
|
+
}
|
|
5878
|
+
return false;
|
|
5879
|
+
}
|
|
5880
|
+
function sleep(ms) {
|
|
5881
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
// src/ratelimit/queued-fetch.ts
|
|
5885
|
+
function normalizeQueueInput(input) {
|
|
5886
|
+
if (typeof input === "string") {
|
|
5887
|
+
return { name: input };
|
|
5888
|
+
}
|
|
5889
|
+
return { name: input.queue, subKey: input.key };
|
|
5890
|
+
}
|
|
5891
|
+
function resolveQueue(queueInput, ctxOverride) {
|
|
5892
|
+
const ctx = ctxOverride ?? getRateLimitExecutionContext();
|
|
5893
|
+
if (!ctx?.app?.versionId) {
|
|
5894
|
+
throw new QueueContextError(
|
|
5895
|
+
"Cannot resolve queue without app context. Ensure queuedFetch runs inside a tool/hook handler."
|
|
5896
|
+
);
|
|
5897
|
+
}
|
|
5898
|
+
const { name, subKey } = normalizeQueueInput(queueInput);
|
|
5899
|
+
const config = getQueueConfigWithRetry(name);
|
|
5900
|
+
if (!config) {
|
|
5901
|
+
throw new QueueNotFoundError(name);
|
|
5902
|
+
}
|
|
5903
|
+
const queueKey = resolveQueueKey(name, config, ctx, subKey);
|
|
5904
|
+
return {
|
|
5905
|
+
name,
|
|
5906
|
+
queueKey,
|
|
5907
|
+
config,
|
|
5908
|
+
limits: toQueueLimits(config)
|
|
5909
|
+
};
|
|
5910
|
+
}
|
|
5911
|
+
function createQueueHandle(queueInput) {
|
|
5912
|
+
return {
|
|
5913
|
+
run(fn) {
|
|
5914
|
+
return queuedFetch(queueInput, fn);
|
|
5915
|
+
}
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
async function queuedFetch(queueInput, fnOrPromise) {
|
|
5919
|
+
const fn = typeof fnOrPromise === "function" ? fnOrPromise : () => fnOrPromise;
|
|
5920
|
+
const resolved = resolveQueue(queueInput);
|
|
5921
|
+
const maxRetries = resolved.config.maxRetries ?? 0;
|
|
5922
|
+
const operation = {
|
|
5923
|
+
queueInput,
|
|
5924
|
+
fn,
|
|
5925
|
+
attempt: 0,
|
|
5926
|
+
resolved,
|
|
5927
|
+
lease: null
|
|
5928
|
+
};
|
|
5929
|
+
return runWithActiveQueuedOperation(
|
|
5930
|
+
operation,
|
|
5931
|
+
() => executeWithRetries(operation, maxRetries)
|
|
5932
|
+
);
|
|
5933
|
+
}
|
|
5934
|
+
async function executeWithRetries(operation, maxRetries) {
|
|
5935
|
+
const backend = getRateLimitBackend();
|
|
5936
|
+
const timeoutMs = operation.resolved.config.timeout ?? 12e4;
|
|
5937
|
+
const retryDelayMs = operation.resolved.config.retryDelayMs ?? 1e3;
|
|
5938
|
+
const shouldRetryFn = getQueueConfigWithRetry(operation.resolved.name)?.shouldRetry ?? defaultShouldRetry;
|
|
5939
|
+
const lease = await backend.acquire(
|
|
5940
|
+
operation.resolved.queueKey,
|
|
5941
|
+
operation.resolved.limits,
|
|
5942
|
+
timeoutMs
|
|
5943
|
+
);
|
|
5944
|
+
operation.lease = lease;
|
|
5945
|
+
setActiveQueuedOperationLease(lease);
|
|
5946
|
+
try {
|
|
5947
|
+
return await operation.fn();
|
|
5948
|
+
} catch (error) {
|
|
5949
|
+
await backend.release(lease);
|
|
5950
|
+
operation.lease = null;
|
|
5951
|
+
setActiveQueuedOperationLease(null);
|
|
5952
|
+
if (shouldRetryFn(error, operation.attempt) && operation.attempt < maxRetries) {
|
|
5953
|
+
await sleep(retryDelayMs);
|
|
5954
|
+
operation.attempt += 1;
|
|
5955
|
+
updateActiveQueuedOperationAttempt(operation.attempt);
|
|
5956
|
+
return executeWithRetries(operation, maxRetries);
|
|
5957
|
+
}
|
|
5958
|
+
if (shouldRetryFn(error, operation.attempt) && operation.attempt >= maxRetries) {
|
|
5959
|
+
throw new QueuedFetchExhaustedError(
|
|
5960
|
+
operation.attempt + 1,
|
|
5961
|
+
maxRetries,
|
|
5962
|
+
error
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
throw error;
|
|
5966
|
+
} finally {
|
|
5967
|
+
if (operation.lease) {
|
|
5968
|
+
await backend.release(operation.lease);
|
|
5969
|
+
operation.lease = null;
|
|
5970
|
+
setActiveQueuedOperationLease(null);
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
async function requeue() {
|
|
5975
|
+
const operation = getActiveQueuedOperation();
|
|
5976
|
+
if (!operation) {
|
|
5977
|
+
throw new RequeueOutsideContextError();
|
|
5978
|
+
}
|
|
5979
|
+
const maxRetries = operation.resolved.config.maxRetries ?? 0;
|
|
5980
|
+
const nextAttempt = operation.attempt + 1;
|
|
5981
|
+
if (nextAttempt > maxRetries) {
|
|
5982
|
+
throw new QueuedFetchExhaustedError(nextAttempt, maxRetries, void 0);
|
|
5983
|
+
}
|
|
5984
|
+
if (operation.lease) {
|
|
5985
|
+
const backend = getRateLimitBackend();
|
|
5986
|
+
await backend.release(operation.lease);
|
|
5987
|
+
operation.lease = null;
|
|
5988
|
+
setActiveQueuedOperationLease(null);
|
|
5989
|
+
}
|
|
5990
|
+
operation.attempt = nextAttempt;
|
|
5991
|
+
updateActiveQueuedOperationAttempt(nextAttempt);
|
|
5992
|
+
const retryDelayMs = operation.resolved.config.retryDelayMs ?? 1e3;
|
|
5993
|
+
await sleep(retryDelayMs);
|
|
5994
|
+
return executeWithRetries(operation, maxRetries);
|
|
5995
|
+
}
|
|
5996
|
+
async function queuedFetchResponse(queueInput, url, init) {
|
|
5997
|
+
return queuedFetch(queueInput, () => fetch(url, init));
|
|
5998
|
+
}
|
|
5999
|
+
|
|
5352
6000
|
// src/triggers/types.ts
|
|
5353
6001
|
import { z as z11 } from "zod/v4";
|
|
5354
6002
|
var CRMDataSchema = z11.object({
|
|
@@ -5453,16 +6101,16 @@ function evaluateTemplate(template, context) {
|
|
|
5453
6101
|
const templateRegex = /\{\{\s*([^}]+)\s*\}\}/g;
|
|
5454
6102
|
const singleMatch = template.match(/^\{\{\s*([^}]+)\s*\}\}$/);
|
|
5455
6103
|
if (singleMatch) {
|
|
5456
|
-
const
|
|
5457
|
-
return resolvePath(context,
|
|
6104
|
+
const path6 = singleMatch[1].trim();
|
|
6105
|
+
return resolvePath(context, path6);
|
|
5458
6106
|
}
|
|
5459
|
-
return template.replace(templateRegex, (_,
|
|
5460
|
-
const value = resolvePath(context,
|
|
6107
|
+
return template.replace(templateRegex, (_, path6) => {
|
|
6108
|
+
const value = resolvePath(context, path6.trim());
|
|
5461
6109
|
return value === void 0 || value === null ? "" : String(value);
|
|
5462
6110
|
});
|
|
5463
6111
|
}
|
|
5464
|
-
function resolvePath(obj,
|
|
5465
|
-
const parts =
|
|
6112
|
+
function resolvePath(obj, path6) {
|
|
6113
|
+
const parts = path6.split(".");
|
|
5466
6114
|
let current = obj;
|
|
5467
6115
|
for (const part of parts) {
|
|
5468
6116
|
if (current === null || current === void 0) {
|
|
@@ -5483,14 +6131,14 @@ function evaluateCondition(condition, context) {
|
|
|
5483
6131
|
const expression = match[1].trim();
|
|
5484
6132
|
const eqMatch = expression.match(/^(.+?)\s*==\s*['"](.+)['"]$/);
|
|
5485
6133
|
if (eqMatch) {
|
|
5486
|
-
const [,
|
|
5487
|
-
const actualValue = resolvePath(context,
|
|
6134
|
+
const [, path6, expectedValue] = eqMatch;
|
|
6135
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
5488
6136
|
return actualValue === expectedValue;
|
|
5489
6137
|
}
|
|
5490
6138
|
const neqMatch = expression.match(/^(.+?)\s*!=\s*['"](.+)['"]$/);
|
|
5491
6139
|
if (neqMatch) {
|
|
5492
|
-
const [,
|
|
5493
|
-
const actualValue = resolvePath(context,
|
|
6140
|
+
const [, path6, expectedValue] = neqMatch;
|
|
6141
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
5494
6142
|
return actualValue !== expectedValue;
|
|
5495
6143
|
}
|
|
5496
6144
|
const value = resolvePath(context, expression);
|
|
@@ -6854,10 +7502,15 @@ export {
|
|
|
6854
7502
|
ParticipantEventPayloadSchema,
|
|
6855
7503
|
ParticipantKindSchema,
|
|
6856
7504
|
ProvisionConfigSchema,
|
|
7505
|
+
QueueContextError,
|
|
7506
|
+
QueueNotFoundError,
|
|
7507
|
+
QueuedFetchExhaustedError,
|
|
7508
|
+
RateLimitBackendError,
|
|
6857
7509
|
RelationshipCardinalitySchema,
|
|
6858
7510
|
RelationshipDefinitionSchema,
|
|
6859
7511
|
RelationshipExtensionSchema,
|
|
6860
7512
|
RelationshipLinkSchema,
|
|
7513
|
+
RequeueOutsideContextError,
|
|
6861
7514
|
ResolvedSkillSchema,
|
|
6862
7515
|
ResolvedTriggerSchema,
|
|
6863
7516
|
ResourceDependencySchema,
|
|
@@ -6935,6 +7588,7 @@ export {
|
|
|
6935
7588
|
createListResponse,
|
|
6936
7589
|
createNotFoundError,
|
|
6937
7590
|
createPermissionError,
|
|
7591
|
+
createQueueHandle,
|
|
6938
7592
|
createRateLimitError,
|
|
6939
7593
|
createServerHookContext,
|
|
6940
7594
|
createSuccessResponse,
|
|
@@ -6945,6 +7599,7 @@ export {
|
|
|
6945
7599
|
createWorkflowStepContext,
|
|
6946
7600
|
cron,
|
|
6947
7601
|
src_default as default,
|
|
7602
|
+
defaultShouldRetry,
|
|
6948
7603
|
defineAgent,
|
|
6949
7604
|
defineChannel,
|
|
6950
7605
|
defineConfig,
|
|
@@ -6967,6 +7622,7 @@ export {
|
|
|
6967
7622
|
getConfig,
|
|
6968
7623
|
getContextByHandle,
|
|
6969
7624
|
getContextByModel,
|
|
7625
|
+
getRateLimitExecutionContext,
|
|
6970
7626
|
getRequiredInstallEnvKeys,
|
|
6971
7627
|
getRetryDelay,
|
|
6972
7628
|
instance,
|
|
@@ -6986,10 +7642,16 @@ export {
|
|
|
6986
7642
|
loadConfig,
|
|
6987
7643
|
matchesTrigger,
|
|
6988
7644
|
parseCRMSchema,
|
|
7645
|
+
queuedFetch,
|
|
7646
|
+
queuedFetchResponse,
|
|
7647
|
+
registerQueueConfig,
|
|
6989
7648
|
report,
|
|
7649
|
+
requeue,
|
|
6990
7650
|
resolveInputMappings,
|
|
7651
|
+
resolveQueue,
|
|
6991
7652
|
resource,
|
|
6992
7653
|
runWithConfig,
|
|
7654
|
+
runWithRateLimitExecutionContext,
|
|
6993
7655
|
safeParseCRMSchema,
|
|
6994
7656
|
safeParseConfig,
|
|
6995
7657
|
server,
|