skedyul 1.5.13 → 1.6.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.
@@ -9,7 +9,9 @@ import type { CoreApiConfig } from '../core/types';
9
9
  import type { EnvSchema, ComputeLayer, ModelDefinition, RelationshipDefinition, ChannelDefinition, WorkflowDefinition, PageDefinition, NavigationConfig, AgentDefinition, SignalDefinition } from './types';
10
10
  import type { AppEventDefinition } from './types/app-event';
11
11
  import type { QueueRegistry, SerializableQueueConfig } from './queue-config';
12
+ import type { SequencerRegistry, SerializableSequencerConfig } from './sequencer-config';
12
13
  export type { QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './queue-config';
14
+ export type { SequencerScope, SerializableSequencerConfig, SequencerRegistry, } from './sequencer-config';
13
15
  /**
14
16
  * Install configuration - defines per-install env vars and SHARED models.
15
17
  * This is configured by users during app installation.
@@ -109,6 +111,8 @@ export interface SkedyulConfig {
109
111
  build?: BuildConfig;
110
112
  /** Rate-limit queue definitions for queuedFetch */
111
113
  queues?: QueueRegistry;
114
+ /** Sequencer definitions for timestamp-aware ordering and short-lived locks */
115
+ sequencers?: SequencerRegistry;
112
116
  }
113
117
  /**
114
118
  * Serializable config (for database storage).
@@ -131,6 +135,8 @@ export interface SerializableSkedyulConfig {
131
135
  events?: AppEventDefinition[];
132
136
  /** Rate-limit queue definitions */
133
137
  queues?: Record<string, SerializableQueueConfig>;
138
+ /** Sequencer definitions */
139
+ sequencers?: Record<string, SerializableSequencerConfig>;
134
140
  }
135
141
  /**
136
142
  * Define a Skedyul app configuration with full type safety.
@@ -9,7 +9,7 @@
9
9
  * - All definition types extend BaseDefinition
10
10
  */
11
11
  export * from './types';
12
- export type { InstallConfig, ProvisionConfig, BuildConfig, CorsOptions, SkedyulConfig, SerializableSkedyulConfig, QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './app-config';
12
+ export type { InstallConfig, ProvisionConfig, BuildConfig, CorsOptions, SkedyulConfig, SerializableSkedyulConfig, QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, SequencerScope, SerializableSequencerConfig, SequencerRegistry, } from './app-config';
13
13
  export { defineConfig } from './app-config';
14
14
  export { defineModel, defineChannel, definePage, defineWorkflow, defineAgent, defineEnv, defineNavigation, } from './define';
15
15
  export { SCHEMA_FILE_EXTENSIONS, loadSchema, saveSchema, serializeSchemaToJson, serializeSchemaToTypeScript, transformToBackendSchema, transformFromBackendSchema, type LoadSchemaOptions, type LoadSchemaResult, type SerializeSchemaOptions, type BackendModelDefinition, type BackendFieldDefinition, type BackendRelationshipDefinition, type BackendDesiredSchema, } from './schema-loader';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Sequencer configuration for skedyul.config sequencers.
3
+ */
4
+ import type { QueueScope } from './queue-config';
5
+ export type SequencerScope = QueueScope;
6
+ /**
7
+ * Serializable sequencer config (stored in DB / .skedyul/config.json).
8
+ */
9
+ export interface SerializableSequencerConfig {
10
+ scope: SequencerScope;
11
+ /** Override endpoint handle for provision_endpoint / install_endpoint scopes */
12
+ endpoint?: string;
13
+ /** Opt-in per named sequencer (default false) */
14
+ enabled?: boolean;
15
+ /** Lock TTL for acquire() — default 60_000 ms */
16
+ lockTtlMs?: number;
17
+ /** Watermark key TTL in Redis — default 7 days */
18
+ watermarkTtlMs?: number;
19
+ }
20
+ export type SequencerRegistry = Record<string, SerializableSequencerConfig>;
@@ -160,6 +160,8 @@ export interface ModelDefinition extends BaseDefinition {
160
160
  addNavigation?: boolean;
161
161
  /** Root path for developer resource UI (e.g., '/access_requests'). Links internal model to a provision.pages entry. */
162
162
  page?: string;
163
+ /** Encrypted env vars scoped to each instance of this internal model */
164
+ env?: import('./env').EnvSchema;
163
165
  }
164
166
  /**
165
167
  * One side of a relationship link.
@@ -255,9 +255,27 @@ export interface InstanceData {
255
255
  id: string;
256
256
  /** The app installation this instance belongs to (if any) */
257
257
  appInstallationId?: string | null;
258
+ /** Encrypted env vars for this instance (only when requested via includeEnv) */
259
+ env?: Record<string, string>;
258
260
  _meta: InstanceMeta;
259
261
  [fieldHandle: string]: unknown;
260
262
  }
263
+ export type InstanceEnv = Record<string, string>;
264
+ export interface InstanceWriteOptions {
265
+ env?: InstanceEnv;
266
+ }
267
+ export interface InstanceGetOptions {
268
+ includeEnv?: boolean;
269
+ }
270
+ export type InstanceCreateManyItem = Record<string, unknown> | {
271
+ data: Record<string, unknown>;
272
+ env?: InstanceEnv;
273
+ };
274
+ export type InstanceUpdateManyItem = {
275
+ id: string;
276
+ data: Record<string, unknown>;
277
+ env?: InstanceEnv;
278
+ };
261
279
  /**
262
280
  * Result from instance.list().
263
281
  */
@@ -282,9 +300,9 @@ export interface InstanceListArgs {
282
300
  */
283
301
  export interface InstanceClient {
284
302
  list(modelHandle: string, args?: InstanceListArgs): Promise<InstanceListResult>;
285
- get(modelHandle: string, id: string): Promise<InstanceData | null>;
286
- create(modelHandle: string, data: Record<string, unknown>): Promise<InstanceData>;
287
- update(modelHandle: string, id: string, data: Record<string, unknown>): Promise<InstanceData>;
303
+ get(modelHandle: string, id: string, options?: InstanceGetOptions): Promise<InstanceData | null>;
304
+ create(modelHandle: string, data: Record<string, unknown>, options?: InstanceWriteOptions): Promise<InstanceData>;
305
+ update(modelHandle: string, id: string, data: Record<string, unknown>, options?: InstanceWriteOptions): Promise<InstanceData>;
288
306
  delete(modelHandle: string, id: string): Promise<{
289
307
  deleted: boolean;
290
308
  }>;
@@ -299,17 +317,14 @@ export interface InstanceClient {
299
317
  error: string;
300
318
  }>;
301
319
  }>;
302
- createMany(modelHandle: string, items: Record<string, unknown>[]): Promise<{
320
+ createMany(modelHandle: string, items: InstanceCreateManyItem[]): Promise<{
303
321
  created: InstanceData[];
304
322
  errors: Array<{
305
323
  index: number;
306
324
  error: string;
307
325
  }>;
308
326
  }>;
309
- updateMany(modelHandle: string, items: Array<{
310
- id: string;
311
- data: Record<string, unknown>;
312
- }>): Promise<{
327
+ updateMany(modelHandle: string, items: InstanceUpdateManyItem[]): Promise<{
313
328
  updated: InstanceData[];
314
329
  errors: Array<{
315
330
  index: number;
@@ -97,6 +97,16 @@ function registerQueueConfig(config) {
97
97
  cachedRuntimeQueues = config.queues ?? null;
98
98
  }
99
99
 
100
+ // src/sequencer/config-loader.ts
101
+ var fs2 = __toESM(require("fs"));
102
+ var path2 = __toESM(require("path"));
103
+ var cachedRuntimeSequencers = null;
104
+ var registeredServerConfig2 = null;
105
+ function registerSequencerConfig(config) {
106
+ registeredServerConfig2 = config;
107
+ cachedRuntimeSequencers = config.sequencers ?? null;
108
+ }
109
+
100
110
  // src/server/utils/schema.ts
101
111
  var z = __toESM(require("zod"));
102
112
  function normalizeBilling(billing) {
@@ -733,7 +743,7 @@ function printStartupLog(config, tools, port) {
733
743
 
734
744
  // src/server/route-handlers/adapters.ts
735
745
  function fromLambdaEvent(event) {
736
- const path3 = event.path || event.rawPath || "/";
746
+ const path4 = event.path || event.rawPath || "/";
737
747
  const method = event.httpMethod || event.requestContext?.http?.method || "POST";
738
748
  const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
739
749
  const protocol = forwardedProto ?? "https";
@@ -741,9 +751,9 @@ function fromLambdaEvent(event) {
741
751
  const queryString = event.queryStringParameters ? "?" + new URLSearchParams(
742
752
  event.queryStringParameters
743
753
  ).toString() : "";
744
- const url = `${protocol}://${host}${path3}${queryString}`;
754
+ const url = `${protocol}://${host}${path4}${queryString}`;
745
755
  return {
746
- path: path3,
756
+ path: path4,
747
757
  method,
748
758
  headers: event.headers,
749
759
  query: event.queryStringParameters ?? {},
@@ -825,8 +835,8 @@ function parseBodyByContentType(req) {
825
835
  }
826
836
 
827
837
  // src/server/route-handlers/handlers.ts
828
- var fs2 = __toESM(require("fs"));
829
- var path2 = __toESM(require("path"));
838
+ var fs3 = __toESM(require("fs"));
839
+ var path3 = __toESM(require("path"));
830
840
 
831
841
  // src/server/core-api-handler.ts
832
842
  async function handleCoreMethod(method, params) {
@@ -1293,8 +1303,13 @@ async function handleOAuthCallback(parsedBody, hooks) {
1293
1303
  }
1294
1304
  }
1295
1305
 
1306
+ // src/types/webhook.ts
1307
+ function isRuntimeWebhookContext(ctx) {
1308
+ return "appInstallationId" in ctx && ctx.appInstallationId !== void 0;
1309
+ }
1310
+
1296
1311
  // src/server/handlers/webhook-handler.ts
1297
- function parseWebhookRequest(parsedBody, method, url, path3, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1312
+ function parseWebhookRequest(parsedBody, method, url, path4, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1298
1313
  const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
1299
1314
  if (isEnvelope) {
1300
1315
  const envelope = parsedBody;
@@ -1348,7 +1363,7 @@ function parseWebhookRequest(parsedBody, method, url, path3, headers, query, raw
1348
1363
  const webhookRequest = {
1349
1364
  method,
1350
1365
  url,
1351
- path: path3,
1366
+ path: path4,
1352
1367
  headers,
1353
1368
  query,
1354
1369
  body: parsedBody,
@@ -1377,9 +1392,17 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
1377
1392
  };
1378
1393
  let webhookResponse;
1379
1394
  try {
1395
+ const rateLimitContext = {
1396
+ app: data.webhookContext.app,
1397
+ appInstallationId: isRuntimeWebhookContext(data.webhookContext) ? data.webhookContext.appInstallationId : void 0,
1398
+ invocation: data.invocation,
1399
+ isProvisionContext: !isRuntimeWebhookContext(data.webhookContext)
1400
+ };
1380
1401
  webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
1381
1402
  return await runWithConfig(requestConfig, async () => {
1382
- return await webhookDef.handler(data.webhookRequest, data.webhookContext);
1403
+ return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
1404
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
1405
+ });
1383
1406
  });
1384
1407
  });
1385
1408
  } catch (err) {
@@ -1406,7 +1429,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
1406
1429
 
1407
1430
  // src/server/route-handlers/handlers.ts
1408
1431
  function getConfigFilePath() {
1409
- return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1432
+ return process.env.LAMBDA_TASK_ROOT ? path3.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1410
1433
  }
1411
1434
  function findToolInRegistry(registry, toolName) {
1412
1435
  for (const [key, t] of Object.entries(registry)) {
@@ -1445,8 +1468,8 @@ function handleHealthRoute(ctx) {
1445
1468
  function handleConfigRoute(ctx) {
1446
1469
  const configFilePath = getConfigFilePath();
1447
1470
  try {
1448
- if (fs2.existsSync(configFilePath)) {
1449
- const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
1471
+ if (fs3.existsSync(configFilePath)) {
1472
+ const fileConfig = JSON.parse(fs3.readFileSync(configFilePath, "utf-8"));
1450
1473
  return { status: 200, body: fileConfig };
1451
1474
  }
1452
1475
  } catch (err) {
@@ -2134,6 +2157,7 @@ installContextLogger();
2134
2157
  function createSkedyulServer(config) {
2135
2158
  mergeRuntimeEnv();
2136
2159
  registerQueueConfig(config);
2160
+ registerSequencerConfig(config);
2137
2161
  const registry = config.tools;
2138
2162
  const webhookRegistry = config.webhooks;
2139
2163
  if (config.coreApi?.service) {