@decocms/runtime 1.0.0-alpha.5 → 1.0.1
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/config-schema.json +19 -8
- package/package.json +11 -17
- package/src/asset-server/index.test.ts +306 -0
- package/src/asset-server/index.ts +217 -34
- package/src/bindings/binder.ts +2 -5
- package/src/bindings/index.ts +0 -33
- package/src/bindings/language-model/utils.ts +0 -91
- package/src/bindings.ts +146 -139
- package/src/client.ts +1 -145
- package/src/cors.ts +140 -0
- package/src/events.ts +472 -0
- package/src/index.ts +206 -202
- package/src/mcp.ts +7 -166
- package/src/oauth.ts +495 -0
- package/src/proxy.ts +1 -208
- package/src/state.ts +3 -31
- package/src/tools.ts +645 -0
- package/src/wrangler.ts +6 -5
- package/tsconfig.json +1 -1
- package/src/admin.ts +0 -16
- package/src/auth.ts +0 -233
- package/src/bindings/deconfig/helpers.ts +0 -107
- package/src/bindings/deconfig/index.ts +0 -1
- package/src/bindings/deconfig/resources.ts +0 -689
- package/src/bindings/deconfig/types.ts +0 -106
- package/src/bindings/language-model/ai-sdk.ts +0 -90
- package/src/bindings/language-model/index.ts +0 -4
- package/src/bindings/resources/bindings.ts +0 -99
- package/src/bindings/resources/helpers.ts +0 -95
- package/src/bindings/resources/schemas.ts +0 -265
- package/src/bindings/views.ts +0 -14
- package/src/drizzle.ts +0 -201
- package/src/http-client-transport.ts +0 -1
- package/src/mastra.ts +0 -670
- package/src/mcp-client.ts +0 -139
- package/src/resources.ts +0 -168
- package/src/views.ts +0 -26
- package/src/well-known.ts +0 -20
package/src/events.ts
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CloudEvent,
|
|
3
|
+
EventResult,
|
|
4
|
+
OnEventsOutput,
|
|
5
|
+
} from "@decocms/bindings";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
import { isBinding } from "./bindings.ts";
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export interface EventSubscription {
|
|
14
|
+
eventType: string;
|
|
15
|
+
publisher: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per-event handler - handles events of a specific type
|
|
20
|
+
* Returns result for each event individually
|
|
21
|
+
*/
|
|
22
|
+
export type PerEventHandler<TEnv> = (
|
|
23
|
+
context: { events: CloudEvent[] },
|
|
24
|
+
env: TEnv,
|
|
25
|
+
) => EventResult | Promise<EventResult>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Batch handler function - handles multiple events at once
|
|
29
|
+
* Can return batch result or per-event results
|
|
30
|
+
*/
|
|
31
|
+
export type BatchHandlerFn<TEnv> = (
|
|
32
|
+
context: { events: CloudEvent[] },
|
|
33
|
+
env: TEnv,
|
|
34
|
+
) => OnEventsOutput | Promise<OnEventsOutput>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Batch handler with explicit event types for subscription
|
|
38
|
+
*/
|
|
39
|
+
export interface BatchHandler<TEnv> {
|
|
40
|
+
/** Handler function */
|
|
41
|
+
handler: BatchHandlerFn<TEnv>;
|
|
42
|
+
/** Event types to subscribe to */
|
|
43
|
+
events: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Binding-level handlers - either a batch handler with events or per-event handlers
|
|
48
|
+
*
|
|
49
|
+
* @example Per-event handlers (event types inferred from keys)
|
|
50
|
+
* ```ts
|
|
51
|
+
* { "order.created": handler, "order.updated": handler }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Batch handler with explicit events
|
|
55
|
+
* ```ts
|
|
56
|
+
* { handler: fn, events: ["order.created", "order.updated"] }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export type BindingHandlers<TEnv, Binding = unknown> =
|
|
60
|
+
| BatchHandler<TEnv>
|
|
61
|
+
| (Record<string, PerEventHandler<TEnv>> & CronHandlers<Binding, TEnv>);
|
|
62
|
+
|
|
63
|
+
export type CronHandlers<Binding, Env = unknown> = Binding extends {
|
|
64
|
+
__type: "@deco/event-bus";
|
|
65
|
+
value: string;
|
|
66
|
+
}
|
|
67
|
+
? {
|
|
68
|
+
[key in `cron/${string}`]: (env: Env) => Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
: {};
|
|
71
|
+
/**
|
|
72
|
+
* EventHandlers type supports three granularity levels:
|
|
73
|
+
*
|
|
74
|
+
* @example Global handler with explicit events
|
|
75
|
+
* ```ts
|
|
76
|
+
* { handler: (ctx, env) => result, events: ["order.created"] }
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example Per-binding batch handler
|
|
80
|
+
* ```ts
|
|
81
|
+
* { DATABASE: { handler: fn, events: ["order.created"] } }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example Per-event handlers (events inferred from keys)
|
|
85
|
+
* ```ts
|
|
86
|
+
* { DATABASE: { "order.created": (ctx, env) => result } }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export type EventHandlers<
|
|
90
|
+
Env = unknown,
|
|
91
|
+
TSchema extends z.ZodTypeAny = never,
|
|
92
|
+
> = [TSchema] extends [never]
|
|
93
|
+
? Record<string, never>
|
|
94
|
+
:
|
|
95
|
+
| BatchHandler<Env> // Global handler with events
|
|
96
|
+
| {
|
|
97
|
+
[K in keyof z.infer<TSchema> as z.infer<TSchema>[K] extends {
|
|
98
|
+
__type: string;
|
|
99
|
+
value: string;
|
|
100
|
+
}
|
|
101
|
+
? K
|
|
102
|
+
: never]?: BindingHandlers<Env, z.infer<TSchema>[K]>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Extract only the keys from T where the value is a Binding shape.
|
|
107
|
+
* Filters out non-binding properties at the type level.
|
|
108
|
+
*/
|
|
109
|
+
export type BindingKeysOf<T> = {
|
|
110
|
+
[K in keyof T]: T[K] extends { __type: string; value: string } ? K : never;
|
|
111
|
+
}[keyof T];
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Type Guards
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if handlers is a global batch handler (has handler + events at top level)
|
|
119
|
+
*/
|
|
120
|
+
const isGlobalHandler = <TEnv>(
|
|
121
|
+
handlers: EventHandlers<TEnv, z.ZodTypeAny>,
|
|
122
|
+
): handlers is BatchHandler<TEnv> => {
|
|
123
|
+
return (
|
|
124
|
+
typeof handlers === "object" &&
|
|
125
|
+
handlers !== null &&
|
|
126
|
+
"handler" in handlers &&
|
|
127
|
+
"events" in handlers &&
|
|
128
|
+
typeof handlers.handler === "function" &&
|
|
129
|
+
Array.isArray(handlers.events)
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if a binding handler is a batch handler (has handler + events) vs per-event handlers (object of functions)
|
|
135
|
+
*/
|
|
136
|
+
const isBatchHandler = <TEnv>(
|
|
137
|
+
handler: BindingHandlers<TEnv>,
|
|
138
|
+
): handler is BatchHandler<TEnv> => {
|
|
139
|
+
return (
|
|
140
|
+
typeof handler === "object" &&
|
|
141
|
+
handler !== null &&
|
|
142
|
+
"handler" in handler &&
|
|
143
|
+
"events" in handler &&
|
|
144
|
+
typeof handler.handler === "function" &&
|
|
145
|
+
Array.isArray(handler.events)
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Helper Functions
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get binding keys from event handlers object
|
|
155
|
+
*/
|
|
156
|
+
const getBindingKeys = <TEnv, TSchema extends z.ZodTypeAny>(
|
|
157
|
+
handlers: EventHandlers<TEnv, TSchema>,
|
|
158
|
+
): string[] => {
|
|
159
|
+
if (isGlobalHandler<TEnv>(handlers)) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
return Object.keys(handlers);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get event types for a binding from handlers
|
|
167
|
+
*/
|
|
168
|
+
const getEventTypesForBinding = <TEnv, TSchema extends z.ZodTypeAny>(
|
|
169
|
+
handlers: EventHandlers<TEnv, TSchema>,
|
|
170
|
+
binding: string,
|
|
171
|
+
): string[] => {
|
|
172
|
+
if (isGlobalHandler<TEnv>(handlers)) {
|
|
173
|
+
return handlers.events;
|
|
174
|
+
}
|
|
175
|
+
const bindingHandler = handlers[binding as keyof typeof handlers];
|
|
176
|
+
if (!bindingHandler) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
if (isBatchHandler(bindingHandler)) {
|
|
180
|
+
// Batch handler - return explicit events array
|
|
181
|
+
return bindingHandler.events;
|
|
182
|
+
}
|
|
183
|
+
// Per-event handlers - event types are the keys
|
|
184
|
+
return Object.keys(bindingHandler);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get subscriptions from event handlers and state
|
|
189
|
+
* Returns flat array of { eventType, publisher } for EVENT_SYNC_SUBSCRIPTIONS
|
|
190
|
+
*/
|
|
191
|
+
const eventsSubscriptions = <TEnv, TSchema extends z.ZodTypeAny = never>(
|
|
192
|
+
handlers: EventHandlers<TEnv, TSchema>,
|
|
193
|
+
state: z.infer<TSchema>,
|
|
194
|
+
): EventSubscription[] => {
|
|
195
|
+
if (isGlobalHandler<TEnv>(handlers)) {
|
|
196
|
+
// Global handler - subscribe to all bindings with the explicit events
|
|
197
|
+
const subscriptions: EventSubscription[] = [];
|
|
198
|
+
for (const [, value] of Object.entries(state)) {
|
|
199
|
+
if (isBinding(value)) {
|
|
200
|
+
for (const eventType of handlers.events) {
|
|
201
|
+
subscriptions.push({
|
|
202
|
+
eventType,
|
|
203
|
+
publisher: value.value,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return subscriptions;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const subscriptions: EventSubscription[] = [];
|
|
212
|
+
for (const binding of getBindingKeys(handlers)) {
|
|
213
|
+
const bindingValue = state[binding as keyof typeof state];
|
|
214
|
+
if (!isBinding(bindingValue)) continue;
|
|
215
|
+
|
|
216
|
+
const eventTypes = getEventTypesForBinding(handlers, binding);
|
|
217
|
+
for (const eventType of eventTypes) {
|
|
218
|
+
subscriptions.push({
|
|
219
|
+
eventType,
|
|
220
|
+
publisher: bindingValue.value,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return subscriptions;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Event Execution
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Group events by source (connection ID)
|
|
233
|
+
*/
|
|
234
|
+
const groupEventsBySource = (
|
|
235
|
+
events: CloudEvent[],
|
|
236
|
+
): Map<string, CloudEvent[]> => {
|
|
237
|
+
const grouped = new Map<string, CloudEvent[]>();
|
|
238
|
+
for (const event of events) {
|
|
239
|
+
const source = event.source;
|
|
240
|
+
const existing = grouped.get(source) || [];
|
|
241
|
+
existing.push(event);
|
|
242
|
+
grouped.set(source, existing);
|
|
243
|
+
}
|
|
244
|
+
return grouped;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Group events by type
|
|
249
|
+
*/
|
|
250
|
+
const groupEventsByType = (events: CloudEvent[]): Map<string, CloudEvent[]> => {
|
|
251
|
+
const grouped = new Map<string, CloudEvent[]>();
|
|
252
|
+
for (const event of events) {
|
|
253
|
+
const type = event.type;
|
|
254
|
+
const existing = grouped.get(type) || [];
|
|
255
|
+
existing.push(event);
|
|
256
|
+
grouped.set(type, existing);
|
|
257
|
+
}
|
|
258
|
+
return grouped;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Merge multiple OnEventsOutput results into a single result
|
|
263
|
+
*/
|
|
264
|
+
const mergeResults = (results: OnEventsOutput[]): OnEventsOutput => {
|
|
265
|
+
const merged: OnEventsOutput = {};
|
|
266
|
+
const allResults: Record<string, EventResult> = {};
|
|
267
|
+
|
|
268
|
+
let hasAnyFailure = false;
|
|
269
|
+
let totalProcessed = 0;
|
|
270
|
+
const errors: string[] = [];
|
|
271
|
+
|
|
272
|
+
for (const result of results) {
|
|
273
|
+
// Merge per-event results
|
|
274
|
+
if (result.results) {
|
|
275
|
+
Object.assign(allResults, result.results);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Track batch-level status
|
|
279
|
+
if (result.success === false) {
|
|
280
|
+
hasAnyFailure = true;
|
|
281
|
+
if (result.error) {
|
|
282
|
+
errors.push(result.error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (result.processedCount !== undefined) {
|
|
287
|
+
totalProcessed += result.processedCount;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Build merged result
|
|
292
|
+
if (Object.keys(allResults).length > 0) {
|
|
293
|
+
merged.results = allResults;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Set batch-level success based on all results
|
|
297
|
+
merged.success = !hasAnyFailure;
|
|
298
|
+
|
|
299
|
+
if (errors.length > 0) {
|
|
300
|
+
merged.error = errors.join("; ");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (totalProcessed > 0) {
|
|
304
|
+
merged.processedCount = totalProcessed;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return merged;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Execute event handlers and return merged result
|
|
312
|
+
*
|
|
313
|
+
* Supports three handler formats:
|
|
314
|
+
* 1. Global: `(context, env) => result` - handles all events
|
|
315
|
+
* 2. Per-binding: `{ BINDING: (context, env) => result }` - handles all events from binding
|
|
316
|
+
* 3. Per-event: `{ BINDING: { "event.type": (context, env) => result } }` - handles specific events
|
|
317
|
+
*/
|
|
318
|
+
const executeEventHandlers = async <TEnv, TSchema extends z.ZodTypeAny>(
|
|
319
|
+
handlers: EventHandlers<TEnv, TSchema>,
|
|
320
|
+
events: CloudEvent[],
|
|
321
|
+
env: z.infer<TSchema>,
|
|
322
|
+
state: z.infer<TSchema>,
|
|
323
|
+
): Promise<OnEventsOutput> => {
|
|
324
|
+
// Case 1: Global handler
|
|
325
|
+
if (isGlobalHandler<TEnv>(handlers)) {
|
|
326
|
+
try {
|
|
327
|
+
return await handlers.handler({ events }, env);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
error: error instanceof Error ? error.message : String(error),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Build a map from connectionId -> binding key
|
|
337
|
+
const connectionToBinding = new Map<string, string>();
|
|
338
|
+
for (const binding of getBindingKeys(handlers)) {
|
|
339
|
+
const bindingValue = state[binding as keyof typeof state];
|
|
340
|
+
if (isBinding(bindingValue)) {
|
|
341
|
+
connectionToBinding.set(bindingValue.value, binding);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Group events by source
|
|
346
|
+
const eventsBySource = groupEventsBySource(events);
|
|
347
|
+
|
|
348
|
+
// Process each binding's events in parallel
|
|
349
|
+
const promises: Promise<OnEventsOutput>[] = [];
|
|
350
|
+
|
|
351
|
+
for (const [source, sourceEvents] of eventsBySource) {
|
|
352
|
+
const binding = connectionToBinding.get(source);
|
|
353
|
+
if (!binding) {
|
|
354
|
+
// No handler for this source - mark as success (ignore)
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const bindingHandler = handlers[binding as keyof typeof handlers];
|
|
359
|
+
if (!bindingHandler) continue;
|
|
360
|
+
|
|
361
|
+
// Case 2: Per-binding batch handler
|
|
362
|
+
if (isBatchHandler(bindingHandler)) {
|
|
363
|
+
promises.push(
|
|
364
|
+
(async () => {
|
|
365
|
+
try {
|
|
366
|
+
return await bindingHandler.handler({ events: sourceEvents }, env);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
// Mark all events from this binding as failed
|
|
369
|
+
const results: Record<string, EventResult> = {};
|
|
370
|
+
for (const event of sourceEvents) {
|
|
371
|
+
results[event.id] = {
|
|
372
|
+
success: false,
|
|
373
|
+
error: error instanceof Error ? error.message : String(error),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
return { results };
|
|
377
|
+
}
|
|
378
|
+
})(),
|
|
379
|
+
);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Case 3: Per-event handlers
|
|
384
|
+
const perEventHandlers = bindingHandler as Record<
|
|
385
|
+
string,
|
|
386
|
+
PerEventHandler<z.infer<TSchema>>
|
|
387
|
+
>;
|
|
388
|
+
const eventsByType = groupEventsByType(sourceEvents);
|
|
389
|
+
|
|
390
|
+
for (const [eventType, typedEvents] of eventsByType) {
|
|
391
|
+
const eventHandler = perEventHandlers[eventType];
|
|
392
|
+
if (!eventHandler) {
|
|
393
|
+
// No handler for this event type - mark as success (ignore)
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Case 3a: Cron handlers (event type starts with "cron/")
|
|
398
|
+
// - Handler signature: (env) => Promise<void>
|
|
399
|
+
// - Fire and forget (don't await)
|
|
400
|
+
// - Always return success immediately
|
|
401
|
+
if (eventType.startsWith("cron/")) {
|
|
402
|
+
const cronHandler = eventHandler as unknown as (
|
|
403
|
+
env: z.infer<TSchema>,
|
|
404
|
+
) => Promise<void>;
|
|
405
|
+
|
|
406
|
+
// Fire and forget - don't await, just log errors
|
|
407
|
+
cronHandler(env).catch((error) => {
|
|
408
|
+
console.error(
|
|
409
|
+
`[Event] Cron handler error for ${eventType}:`,
|
|
410
|
+
error instanceof Error ? error.message : String(error),
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Immediately return success for all cron events
|
|
415
|
+
const results: Record<string, EventResult> = {};
|
|
416
|
+
for (const event of typedEvents) {
|
|
417
|
+
results[event.id] = { success: true };
|
|
418
|
+
}
|
|
419
|
+
promises.push(Promise.resolve({ results }));
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Case 3b: Regular per-event handlers
|
|
424
|
+
// Call handler for each event type (handler receives all events of that type)
|
|
425
|
+
promises.push(
|
|
426
|
+
(async () => {
|
|
427
|
+
try {
|
|
428
|
+
const result = await eventHandler({ events: typedEvents }, env);
|
|
429
|
+
// Convert per-event result to output with results for each event
|
|
430
|
+
const results: Record<string, EventResult> = {};
|
|
431
|
+
for (const event of typedEvents) {
|
|
432
|
+
results[event.id] = result;
|
|
433
|
+
}
|
|
434
|
+
return { results };
|
|
435
|
+
} catch (error) {
|
|
436
|
+
const results: Record<string, EventResult> = {};
|
|
437
|
+
for (const event of typedEvents) {
|
|
438
|
+
results[event.id] = {
|
|
439
|
+
success: false,
|
|
440
|
+
error: error instanceof Error ? error.message : String(error),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
return { results };
|
|
444
|
+
}
|
|
445
|
+
})(),
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Wait for all handlers to complete
|
|
451
|
+
const results = await Promise.all(promises);
|
|
452
|
+
|
|
453
|
+
// If no handlers were called, return success
|
|
454
|
+
if (results.length === 0) {
|
|
455
|
+
return { success: true };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Merge all results
|
|
459
|
+
return mergeResults(results);
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// Exports
|
|
464
|
+
// ============================================================================
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Event utilities for subscriptions and execution
|
|
468
|
+
*/
|
|
469
|
+
export const Event = {
|
|
470
|
+
subscriptions: eventsSubscriptions,
|
|
471
|
+
execute: executeEventHandlers,
|
|
472
|
+
};
|