@decocms/runtime 1.0.0-alpha.36 → 1.0.0-alpha.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/runtime",
3
- "version": "1.0.0-alpha.36",
3
+ "version": "1.0.0-alpha.37",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit"
package/src/events.ts CHANGED
@@ -60,10 +60,18 @@ export interface BatchHandler<TEnv> {
60
60
  * { handler: fn, events: ["order.created", "order.updated"] }
61
61
  * ```
62
62
  */
63
- export type BindingHandlers<TEnv> =
63
+ export type BindingHandlers<TEnv, Binding = unknown> =
64
64
  | BatchHandler<TEnv>
65
- | Record<string, PerEventHandler<TEnv>>;
65
+ | (Record<string, PerEventHandler<TEnv>> & CronHandlers<Binding, TEnv>);
66
66
 
67
+ export type CronHandlers<Binding, Env = unknown> = Binding extends {
68
+ __type: "@deco/event-bus";
69
+ value: string;
70
+ }
71
+ ? {
72
+ [key in `cron/${string}`]: (env: Env) => Promise<void>;
73
+ }
74
+ : {};
67
75
  /**
68
76
  * EventHandlers type supports three granularity levels:
69
77
  *
@@ -82,9 +90,10 @@ export type BindingHandlers<TEnv> =
82
90
  * { DATABASE: { "order.created": (ctx, env) => result } }
83
91
  * ```
84
92
  */
85
- export type EventHandlers<TSchema extends z.ZodTypeAny = never> = [
86
- TSchema,
87
- ] extends [never]
93
+ export type EventHandlers<
94
+ Env = unknown,
95
+ TSchema extends z.ZodTypeAny = never,
96
+ > = [TSchema] extends [never]
88
97
  ? Record<string, never>
89
98
  :
90
99
  | BatchHandler<z.infer<TSchema>> // Global handler with events
@@ -94,7 +103,7 @@ export type EventHandlers<TSchema extends z.ZodTypeAny = never> = [
94
103
  value: string;
95
104
  }
96
105
  ? K
97
- : never]?: BindingHandlers<z.infer<TSchema>>;
106
+ : never]?: BindingHandlers<Env, z.infer<TSchema>[K]>;
98
107
  };
99
108
 
100
109
  /**
@@ -124,7 +133,7 @@ const isBinding = (v: unknown): v is Binding => {
124
133
  * Check if handlers is a global batch handler (has handler + events at top level)
125
134
  */
126
135
  const isGlobalHandler = <TEnv>(
127
- handlers: EventHandlers<z.ZodTypeAny>,
136
+ handlers: EventHandlers<TEnv, z.ZodTypeAny>,
128
137
  ): handlers is BatchHandler<TEnv> => {
129
138
  return (
130
139
  typeof handlers === "object" &&
@@ -159,10 +168,10 @@ const isBatchHandler = <TEnv>(
159
168
  /**
160
169
  * Get binding keys from event handlers object
161
170
  */
162
- const getBindingKeys = <TSchema extends z.ZodTypeAny>(
163
- handlers: EventHandlers<TSchema>,
171
+ const getBindingKeys = <TEnv, TSchema extends z.ZodTypeAny>(
172
+ handlers: EventHandlers<TEnv, TSchema>,
164
173
  ): string[] => {
165
- if (isGlobalHandler(handlers)) {
174
+ if (isGlobalHandler<TEnv>(handlers)) {
166
175
  return [];
167
176
  }
168
177
  return Object.keys(handlers);
@@ -171,11 +180,11 @@ const getBindingKeys = <TSchema extends z.ZodTypeAny>(
171
180
  /**
172
181
  * Get event types for a binding from handlers
173
182
  */
174
- const getEventTypesForBinding = <TSchema extends z.ZodTypeAny>(
175
- handlers: EventHandlers<TSchema>,
183
+ const getEventTypesForBinding = <TEnv, TSchema extends z.ZodTypeAny>(
184
+ handlers: EventHandlers<TEnv, TSchema>,
176
185
  binding: string,
177
186
  ): string[] => {
178
- if (isGlobalHandler(handlers)) {
187
+ if (isGlobalHandler<TEnv>(handlers)) {
179
188
  return handlers.events;
180
189
  }
181
190
  const bindingHandler = handlers[binding as keyof typeof handlers];
@@ -193,10 +202,10 @@ const getEventTypesForBinding = <TSchema extends z.ZodTypeAny>(
193
202
  /**
194
203
  * Get scopes from event handlers for subscription
195
204
  */
196
- const scopesFromEvents = <TSchema extends z.ZodTypeAny = never>(
197
- handlers: EventHandlers<TSchema>,
205
+ const scopesFromEvents = <TEnv, TSchema extends z.ZodTypeAny = never>(
206
+ handlers: EventHandlers<TEnv, TSchema>,
198
207
  ): string[] => {
199
- if (isGlobalHandler(handlers)) {
208
+ if (isGlobalHandler<TEnv>(handlers)) {
200
209
  // Global handler - scopes are based on explicit events array
201
210
  // Note: "*" binding means all bindings
202
211
  return handlers.events.map((event) => `*::event@${event}`);
@@ -216,11 +225,11 @@ const scopesFromEvents = <TSchema extends z.ZodTypeAny = never>(
216
225
  * Get subscriptions from event handlers and state
217
226
  * Returns flat array of { eventType, publisher } for EVENT_SYNC_SUBSCRIPTIONS
218
227
  */
219
- const eventsSubscriptions = <TSchema extends z.ZodTypeAny = never>(
220
- handlers: EventHandlers<TSchema>,
228
+ const eventsSubscriptions = <TEnv, TSchema extends z.ZodTypeAny = never>(
229
+ handlers: EventHandlers<TEnv, TSchema>,
221
230
  state: z.infer<TSchema>,
222
231
  ): EventSubscription[] => {
223
- if (isGlobalHandler(handlers)) {
232
+ if (isGlobalHandler<TEnv>(handlers)) {
224
233
  // Global handler - subscribe to all bindings with the explicit events
225
234
  const subscriptions: EventSubscription[] = [];
226
235
  for (const [, value] of Object.entries(state)) {
@@ -343,14 +352,14 @@ const mergeResults = (results: OnEventsOutput[]): OnEventsOutput => {
343
352
  * 2. Per-binding: `{ BINDING: (context, env) => result }` - handles all events from binding
344
353
  * 3. Per-event: `{ BINDING: { "event.type": (context, env) => result } }` - handles specific events
345
354
  */
346
- const executeEventHandlers = async <TSchema extends z.ZodTypeAny>(
347
- handlers: EventHandlers<TSchema>,
355
+ const executeEventHandlers = async <TEnv, TSchema extends z.ZodTypeAny>(
356
+ handlers: EventHandlers<TEnv, TSchema>,
348
357
  events: CloudEvent[],
349
358
  env: z.infer<TSchema>,
350
359
  state: z.infer<TSchema>,
351
360
  ): Promise<OnEventsOutput> => {
352
361
  // Case 1: Global handler
353
- if (isGlobalHandler(handlers)) {
362
+ if (isGlobalHandler<TEnv>(handlers)) {
354
363
  try {
355
364
  return await handlers.handler({ events }, env);
356
365
  } catch (error) {
@@ -422,6 +431,33 @@ const executeEventHandlers = async <TSchema extends z.ZodTypeAny>(
422
431
  continue;
423
432
  }
424
433
 
434
+ // Case 3a: Cron handlers (event type starts with "cron/")
435
+ // - Handler signature: (env) => Promise<void>
436
+ // - Fire and forget (don't await)
437
+ // - Always return success immediately
438
+ if (eventType.startsWith("cron/")) {
439
+ const cronHandler = eventHandler as unknown as (
440
+ env: z.infer<TSchema>,
441
+ ) => Promise<void>;
442
+
443
+ // Fire and forget - don't await, just log errors
444
+ cronHandler(env).catch((error) => {
445
+ console.error(
446
+ `[Event] Cron handler error for ${eventType}:`,
447
+ error instanceof Error ? error.message : String(error),
448
+ );
449
+ });
450
+
451
+ // Immediately return success for all cron events
452
+ const results: Record<string, EventResult> = {};
453
+ for (const event of typedEvents) {
454
+ results[event.id] = { success: true };
455
+ }
456
+ promises.push(Promise.resolve({ results }));
457
+ continue;
458
+ }
459
+
460
+ // Case 3b: Regular per-event handlers
425
461
  // Call handler for each event type (handler receives all events of that type)
426
462
  promises.push(
427
463
  (async () => {
package/src/tools.ts CHANGED
@@ -237,7 +237,7 @@ export interface CreateMCPServerOptions<
237
237
  oauth?: OAuthConfig;
238
238
  events?: {
239
239
  bus?: keyof PickByType<Env & DefaultEnv<TSchema>, EventBusBindingClient>;
240
- handlers?: EventHandlers<TSchema>;
240
+ handlers?: EventHandlers<Env & DefaultEnv<TSchema>, TSchema>;
241
241
  };
242
242
  configuration?: {
243
243
  onChange?: (