melony 0.3.5 → 0.3.6

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/index.d.ts CHANGED
@@ -17,20 +17,24 @@ declare class Runtime<TState = any, TEvent = any> {
17
17
  * Process an incoming event through the runtime.
18
18
  * All event processing is handled by user-defined event handlers.
19
19
  */
20
- run(event: TEvent, options?: {
21
- state?: TState;
22
- runId?: string;
23
- }): AsyncGenerator<TEvent>;
20
+ run(event: TEvent, options?: RunOptions<TState>): AsyncGenerator<TEvent>;
24
21
  /**
25
22
  * Run all event handlers that match the given event type.
26
23
  */
27
24
  private runEventHandlers;
28
25
  /**
29
- * Internal helper to yield an event with metadata.
26
+ * Internal helper to yield an event with metadata and trigger hooks.
30
27
  */
31
28
  private emit;
32
29
  }
33
30
 
31
+ /**
32
+ * Options for executing a Melony run.
33
+ */
34
+ interface RunOptions<TState = any> {
35
+ state?: TState;
36
+ runId?: string;
37
+ }
34
38
  interface RuntimeContext<TState = any, TEvent = any> {
35
39
  state: TState;
36
40
  runId: string;
@@ -51,6 +55,7 @@ type EventInterceptor<TState = any, TEvent = any> = (event: TEvent, context: Run
51
55
  * Can return events to emit or undefined to continue processing.
52
56
  */
53
57
  type EventHandler<TState = any, TEvent = any> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void;
58
+ type LifecycleHookResult<TEvent = any> = AsyncGenerator<TEvent, void, unknown> | void;
54
59
  /**
55
60
  * Configuration for the Melony runtime.
56
61
  */
@@ -62,10 +67,30 @@ interface Config<TState = any, TEvent = any> {
62
67
  * Defaults to "type".
63
68
  */
64
69
  eventKey?: string;
70
+ /**
71
+ * The initial state for the runtime.
72
+ * Can be an object or a factory function (sync or async).
73
+ */
74
+ initialState?: TState | (() => TState | Promise<TState>);
75
+ /**
76
+ * Hook called before the runtime starts processing events.
77
+ */
78
+ onStart?: (context: RuntimeContext<TState, TEvent>, initialEvent: TEvent) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>;
79
+ /**
80
+ * Hook called after the runtime has finished processing events (even on failure).
81
+ */
82
+ onEnd?: (context: RuntimeContext<TState, TEvent>) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>;
83
+ /**
84
+ * Hook called for every event emitted by the runtime.
85
+ */
86
+ onEvent?: (context: RuntimeContext<TState, TEvent>, event: TEvent) => Promise<void> | void;
65
87
  }
66
88
 
89
+ /**
90
+ * The Melony Runtime class.
91
+ * This is the core class that powers the Melony framework.
92
+ */
67
93
  declare const MelonyRuntime: typeof Runtime;
68
-
69
94
  /**
70
95
  * A Melony plugin is a function that receives the builder and extends it.
71
96
  * This allows for modularizing common handlers.
@@ -83,6 +108,23 @@ declare class MelonyBuilder<TState = any, TEvent = any> {
83
108
  * Defaults to "type".
84
109
  */
85
110
  eventKey(key: string): this;
111
+ /**
112
+ * Configure the initial state for the runtime.
113
+ * Supports a static object or a factory function.
114
+ */
115
+ initialState(state: TState | (() => TState | Promise<TState>)): this;
116
+ /**
117
+ * Register a hook called before the runtime starts processing events.
118
+ */
119
+ onStart(hook: (context: RuntimeContext<TState, TEvent>, initialEvent: TEvent) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>): this;
120
+ /**
121
+ * Register a hook called after the runtime has finished processing events.
122
+ */
123
+ onEnd(hook: (context: RuntimeContext<TState, TEvent>) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>): this;
124
+ /**
125
+ * Register a hook called for every event emitted by the runtime.
126
+ */
127
+ onEvent(hook: (context: RuntimeContext<TState, TEvent>, event: TEvent) => Promise<void> | void): this;
86
128
  /**
87
129
  * Add an event handler for a specific event type. Supports method chaining.
88
130
  * The handler receives the narrowed event type based on the eventType string.
@@ -104,6 +146,10 @@ declare class MelonyBuilder<TState = any, TEvent = any> {
104
146
  * This is the final method in the fluent chain.
105
147
  */
106
148
  build(): Runtime<TState, TEvent>;
149
+ /**
150
+ * Execute the runtime for a specific event.
151
+ */
152
+ run(event: TEvent, options?: RunOptions<TState>): Promise<AsyncGenerator<TEvent>>;
107
153
  /**
108
154
  * Execute and stream the response for an event.
109
155
  * This is a convenience method that builds the runtime and calls run().
@@ -116,9 +162,13 @@ declare class MelonyBuilder<TState = any, TEvent = any> {
116
162
  * Execute the agent and return the data from the last event of a specific type as a JSON response.
117
163
  * Ideal for initialization or non-streaming requests where you only need the final UI state.
118
164
  */
119
- jsonResponse(event: TEvent, options?: {
120
- state?: TState;
121
- runId?: string;
165
+ jsonResponse(event: TEvent, options?: RunOptions<TState>): Promise<Response>;
166
+ /**
167
+ * A unified Web-Standard Request Handler.
168
+ * Automatically parses a Request and returns a streaming Response.
169
+ */
170
+ handle(request: Request, options?: {
171
+ state?: (req: Request) => Promise<TState> | TState;
122
172
  }): Promise<Response>;
123
173
  /**
124
174
  * Get the current configuration (useful for debugging or serialization).
@@ -131,6 +181,15 @@ declare class MelonyBuilder<TState = any, TEvent = any> {
131
181
  */
132
182
  declare function melony<TState = any, TEvent = any>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent>;
133
183
 
184
+ interface CreateJsonResponseDeps<TState, TEvent> {
185
+ run: (event: TEvent, options?: RunOptions<TState>) => AsyncIterable<TEvent>;
186
+ }
187
+ /**
188
+ * Run the agent to completion and return all emitted events as a JSON Response.
189
+ * Useful for initialization or non-streaming requests.
190
+ */
191
+ declare function createJsonResponse<TState, TEvent>(event: TEvent, options: RunOptions<TState> | undefined, deps: CreateJsonResponseDeps<TState, TEvent>): Promise<Response>;
192
+
134
193
  /**
135
194
  * Convert an async generator of events to an HTTP streaming response
136
195
  * Exported for backward compatibility and standalone usage
@@ -139,4 +198,20 @@ declare function createStreamResponse<TEvent = any>(generator: AsyncGenerator<TE
139
198
 
140
199
  declare const generateId: () => string;
141
200
 
142
- export { type Config, type EventHandler, type EventInterceptor, MelonyBuilder, type MelonyPlugin, MelonyRuntime, Runtime, type RuntimeContext, createStreamResponse, generateId, melony };
201
+ interface HandleMelonyRequestOptions<TState> {
202
+ state?: (req: Request) => Promise<TState> | TState;
203
+ }
204
+ interface HandleMelonyRequestDeps<TState, TEvent> {
205
+ eventKey: string;
206
+ initialState?: TState | (() => TState | Promise<TState>);
207
+ streamResponse: (event: TEvent, options?: {
208
+ state?: TState;
209
+ runId?: string;
210
+ }) => Promise<Response>;
211
+ }
212
+ /**
213
+ * Unified Web-Standard Request handler: parses Request and returns a streaming Response.
214
+ */
215
+ declare function handleMelonyRequest<TState, TEvent>(request: Request, options: HandleMelonyRequestOptions<TState> | undefined, deps: HandleMelonyRequestDeps<TState, TEvent>): Promise<Response>;
216
+
217
+ export { type Config, type CreateJsonResponseDeps, type EventHandler, type EventInterceptor, type HandleMelonyRequestDeps, type HandleMelonyRequestOptions, type LifecycleHookResult, MelonyBuilder, type MelonyPlugin, MelonyRuntime, type RunOptions, Runtime, type RuntimeContext, createJsonResponse, createStreamResponse, generateId, handleMelonyRequest, melony };
package/dist/index.js CHANGED
@@ -29,8 +29,18 @@ var Runtime = class {
29
29
  async *run(event, options) {
30
30
  const runId = options?.runId ?? generateId();
31
31
  const eventKey = this.config.eventKey || "type";
32
+ let state;
33
+ if (options?.state) {
34
+ state = options.state;
35
+ } else if (typeof this.config.initialState === "function") {
36
+ state = await this.config.initialState();
37
+ } else if (this.config.initialState) {
38
+ state = typeof globalThis.structuredClone === "function" ? globalThis.structuredClone(this.config.initialState) : { ...this.config.initialState };
39
+ } else {
40
+ state = {};
41
+ }
32
42
  const context = {
33
- state: options?.state ?? {},
43
+ state,
34
44
  runtime: this,
35
45
  runId,
36
46
  suspend: (event2) => {
@@ -38,6 +48,14 @@ var Runtime = class {
38
48
  }
39
49
  };
40
50
  try {
51
+ if (this.config.onStart) {
52
+ const startEvents = await this.config.onStart(context, event);
53
+ if (startEvents) {
54
+ for await (const lifecycleEvent of startEvents) {
55
+ yield* this.runEventHandlers(lifecycleEvent, context);
56
+ }
57
+ }
58
+ }
41
59
  yield* this.runEventHandlers(event, context);
42
60
  } catch (error) {
43
61
  let eventToEmit;
@@ -53,7 +71,16 @@ var Runtime = class {
53
71
  };
54
72
  }
55
73
  if (eventToEmit) {
56
- yield* this.emit(eventToEmit);
74
+ yield* this.runEventHandlers(eventToEmit, context);
75
+ }
76
+ } finally {
77
+ if (this.config.onEnd) {
78
+ const endEvents = await this.config.onEnd(context);
79
+ if (endEvents) {
80
+ for await (const lifecycleEvent of endEvents) {
81
+ yield* this.runEventHandlers(lifecycleEvent, context);
82
+ }
83
+ }
57
84
  }
58
85
  }
59
86
  }
@@ -70,7 +97,7 @@ var Runtime = class {
70
97
  currentEvent = result;
71
98
  }
72
99
  }
73
- const eventType = this.getEventType(currentEvent);
100
+ let eventType = this.getEventType(currentEvent);
74
101
  if (eventType !== "*") {
75
102
  const specificInterceptors = this.config.interceptors.get(eventType) || [];
76
103
  for (const interceptor of specificInterceptors) {
@@ -80,11 +107,11 @@ var Runtime = class {
80
107
  }
81
108
  }
82
109
  }
83
- const handlers = [
84
- ...this.config.handlers.get("*") || [],
85
- ...this.config.handlers.get(this.getEventType(currentEvent)) || []
86
- ];
87
- yield* this.emit(currentEvent);
110
+ eventType = this.getEventType(currentEvent);
111
+ const wildcardHandlers = this.config.handlers.get("*") || [];
112
+ const specificHandlers = this.config.handlers.get(eventType) || [];
113
+ const handlers = eventType === "*" ? [...wildcardHandlers] : [...wildcardHandlers, ...specificHandlers];
114
+ yield* this.emit(currentEvent, context);
88
115
  for (const handler of handlers) {
89
116
  const result = handler(currentEvent, context);
90
117
  if (result) {
@@ -95,15 +122,26 @@ var Runtime = class {
95
122
  }
96
123
  }
97
124
  /**
98
- * Internal helper to yield an event with metadata.
125
+ * Internal helper to yield an event with metadata and trigger hooks.
99
126
  */
100
- async *emit(event) {
127
+ async *emit(event, context) {
128
+ if (this.config.onEvent) {
129
+ await this.config.onEvent(context, event);
130
+ }
101
131
  yield event;
102
132
  }
103
133
  };
104
134
 
105
- // src/melony.ts
106
- var MelonyRuntime = Runtime;
135
+ // src/utils/create-json-response.ts
136
+ async function createJsonResponse(event, options, deps) {
137
+ const events = [];
138
+ for await (const e of deps.run(event, options)) {
139
+ events.push(e);
140
+ }
141
+ return new Response(JSON.stringify({ events }), {
142
+ headers: { "Content-Type": "application/json" }
143
+ });
144
+ }
107
145
 
108
146
  // src/utils/create-stream-response.ts
109
147
  function createStreamResponse(generator) {
@@ -132,13 +170,87 @@ function createStreamResponse(generator) {
132
170
  });
133
171
  }
134
172
 
135
- // src/builder.ts
173
+ // src/utils/handle-request.ts
174
+ async function handleMelonyRequest(request, options, deps) {
175
+ if (request.method === "OPTIONS") {
176
+ const requested = request.headers.get("access-control-request-headers");
177
+ const defaultAllowHeaders = "Content-Type, x-melony-thread-id, x-melony-run-id, x-melony-session-id, Authorization, Last-Event-ID";
178
+ const origin2 = request.headers.get("origin");
179
+ const allowOrigin = origin2 ?? "*";
180
+ const headers = {
181
+ "Access-Control-Allow-Origin": allowOrigin,
182
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
183
+ "Access-Control-Allow-Headers": requested ?? defaultAllowHeaders
184
+ };
185
+ if (origin2) {
186
+ headers.Vary = "Origin";
187
+ headers["Access-Control-Allow-Credentials"] = "true";
188
+ }
189
+ const pnr2 = request.headers.get("access-control-request-private-network");
190
+ if (pnr2?.toLowerCase() === "true") {
191
+ headers["Access-Control-Allow-Private-Network"] = "true";
192
+ }
193
+ return new Response(null, { status: 204, headers });
194
+ }
195
+ let event;
196
+ try {
197
+ if (request.method === "POST") {
198
+ event = await request.json();
199
+ } else {
200
+ const url = new URL(request.url);
201
+ event = {
202
+ [deps.eventKey]: url.searchParams.get("type") || "http-get",
203
+ data: Object.fromEntries(url.searchParams.entries())
204
+ };
205
+ }
206
+ } catch {
207
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
208
+ status: 400,
209
+ headers: {
210
+ "Content-Type": "application/json",
211
+ "Access-Control-Allow-Origin": "*"
212
+ }
213
+ });
214
+ }
215
+ const threadId = request.headers.get("x-melony-thread-id") || event.threadId || generateId();
216
+ const runId = request.headers.get("x-melony-run-id") || generateId();
217
+ const sessionId = request.headers.get("x-melony-session-id") || event.sessionId || generateId();
218
+ let state;
219
+ if (options?.state) {
220
+ state = await options.state(request);
221
+ } else if (typeof deps.initialState === "function") {
222
+ state = await deps.initialState();
223
+ } else if (deps.initialState) {
224
+ state = typeof globalThis.structuredClone === "function" ? globalThis.structuredClone(deps.initialState) : { ...deps.initialState };
225
+ } else {
226
+ state = { threadId, runId, sessionId };
227
+ }
228
+ const response = await deps.streamResponse(event, { state, runId });
229
+ const origin = request.headers.get("origin");
230
+ response.headers.set("Access-Control-Allow-Origin", origin ?? "*");
231
+ if (origin) {
232
+ response.headers.set("Vary", "Origin");
233
+ response.headers.set("Access-Control-Allow-Credentials", "true");
234
+ }
235
+ const pnr = request.headers.get("access-control-request-private-network");
236
+ if (pnr?.toLowerCase() === "true") {
237
+ response.headers.set("Access-Control-Allow-Private-Network", "true");
238
+ }
239
+ return response;
240
+ }
241
+
242
+ // src/melony.ts
243
+ var MelonyRuntime = Runtime;
136
244
  var MelonyBuilder = class {
137
245
  constructor(initialConfig) {
138
246
  this.config = {
139
247
  handlers: initialConfig?.handlers ?? /* @__PURE__ */ new Map(),
140
248
  interceptors: initialConfig?.interceptors ?? /* @__PURE__ */ new Map(),
141
- eventKey: initialConfig?.eventKey ?? "type"
249
+ eventKey: initialConfig?.eventKey ?? "type",
250
+ initialState: initialConfig?.initialState,
251
+ onStart: initialConfig?.onStart,
252
+ onEnd: initialConfig?.onEnd,
253
+ onEvent: initialConfig?.onEvent
142
254
  };
143
255
  }
144
256
  /**
@@ -149,6 +261,35 @@ var MelonyBuilder = class {
149
261
  this.config.eventKey = key;
150
262
  return this;
151
263
  }
264
+ /**
265
+ * Configure the initial state for the runtime.
266
+ * Supports a static object or a factory function.
267
+ */
268
+ initialState(state) {
269
+ this.config.initialState = state;
270
+ return this;
271
+ }
272
+ /**
273
+ * Register a hook called before the runtime starts processing events.
274
+ */
275
+ onStart(hook) {
276
+ this.config.onStart = hook;
277
+ return this;
278
+ }
279
+ /**
280
+ * Register a hook called after the runtime has finished processing events.
281
+ */
282
+ onEnd(hook) {
283
+ this.config.onEnd = hook;
284
+ return this;
285
+ }
286
+ /**
287
+ * Register a hook called for every event emitted by the runtime.
288
+ */
289
+ onEvent(hook) {
290
+ this.config.onEvent = hook;
291
+ return this;
292
+ }
152
293
  /**
153
294
  * Add an event handler for a specific event type. Supports method chaining.
154
295
  * The handler receives the narrowed event type based on the eventType string.
@@ -192,6 +333,13 @@ var MelonyBuilder = class {
192
333
  build() {
193
334
  return new Runtime(this.config);
194
335
  }
336
+ /**
337
+ * Execute the runtime for a specific event.
338
+ */
339
+ async run(event, options) {
340
+ const runtime = this.build();
341
+ return runtime.run(event, options);
342
+ }
195
343
  /**
196
344
  * Execute and stream the response for an event.
197
345
  * This is a convenience method that builds the runtime and calls run().
@@ -206,13 +354,19 @@ var MelonyBuilder = class {
206
354
  * Ideal for initialization or non-streaming requests where you only need the final UI state.
207
355
  */
208
356
  async jsonResponse(event, options) {
209
- const events = [];
210
- const runtime = this.build();
211
- for await (const e of runtime.run(event, options)) {
212
- events.push(e);
213
- }
214
- return new Response(JSON.stringify({ events }), {
215
- headers: { "Content-Type": "application/json" }
357
+ return createJsonResponse(event, options, {
358
+ run: (e, opts) => this.build().run(e, opts)
359
+ });
360
+ }
361
+ /**
362
+ * A unified Web-Standard Request Handler.
363
+ * Automatically parses a Request and returns a streaming Response.
364
+ */
365
+ async handle(request, options) {
366
+ return handleMelonyRequest(request, options, {
367
+ eventKey: this.config.eventKey || "type",
368
+ initialState: this.config.initialState,
369
+ streamResponse: (event, opts) => this.streamResponse(event, opts)
216
370
  });
217
371
  }
218
372
  /**
@@ -226,6 +380,6 @@ function melony(initialConfig) {
226
380
  return new MelonyBuilder(initialConfig);
227
381
  }
228
382
 
229
- export { MelonyBuilder, MelonyRuntime, Runtime, createStreamResponse, generateId, melony };
383
+ export { MelonyBuilder, MelonyRuntime, Runtime, createJsonResponse, createStreamResponse, generateId, handleMelonyRequest, melony };
230
384
  //# sourceMappingURL=index.js.map
231
385
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/generate-id.ts","../src/runtime.ts","../src/melony.ts","../src/utils/create-stream-response.ts","../src/builder.ts"],"names":["event"],"mappings":"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","file":"index.js","sourcesContent":["export const generateId = () => {\n return typeof crypto !== \"undefined\" && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).substring(7);\n};\n","import {\n RuntimeContext,\n Config,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\n/**\n * The Melony Runtime.\n * Fully unopinionated - processes events through handlers.\n */\nexport class Runtime<TState = any, TEvent = any> {\n public readonly config: Config<TState, TEvent>;\n\n constructor(config: Config<TState, TEvent>) {\n this.config = config;\n }\n\n /**\n * Helper to get the event type from an event object based on configuration.\n */\n private getEventType(event: TEvent): string {\n const key = this.config.eventKey || \"type\";\n return (event as any)[key] || \"*\";\n }\n\n /**\n * Helper to check if a value is a Melony Event.\n */\n private isEvent(val: any): val is TEvent {\n const key = this.config.eventKey || \"type\";\n return val && typeof val === \"object\" && typeof (val as any)[key] === \"string\";\n }\n\n /**\n * Process an incoming event through the runtime.\n * All event processing is handled by user-defined event handlers.\n */\n public async *run(\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ): AsyncGenerator<TEvent> {\n const runId = options?.runId ?? generateId();\n const eventKey = this.config.eventKey || \"type\";\n\n const context: RuntimeContext<TState, TEvent> = {\n state: (options?.state ?? {}) as TState,\n runtime: this,\n runId,\n suspend: (event?: TEvent) => {\n throw event || { [eventKey]: \"run-suspended\", data: {} };\n },\n };\n\n try {\n // Process the incoming event through handlers\n yield* this.runEventHandlers(event, context);\n } catch (error) {\n let eventToEmit: TEvent | undefined;\n\n if (this.isEvent(error)) {\n eventToEmit = error as TEvent;\n } else {\n eventToEmit = {\n [eventKey]: \"error\",\n data: {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n },\n } as unknown as TEvent;\n }\n\n if (eventToEmit) {\n yield* this.emit(eventToEmit);\n }\n }\n }\n\n\n /**\n * Run all event handlers that match the given event type.\n */\n private async *runEventHandlers(\n event: TEvent,\n context: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n let currentEvent = event;\n const eventKey = this.config.eventKey || \"type\";\n\n // 1. Run global interceptors first\n const globalInterceptors = this.config.interceptors.get(\"*\") || [];\n for (const interceptor of globalInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && eventKey in (result as any)) {\n currentEvent = result as TEvent;\n }\n }\n\n // 2. Run specific interceptors for the (possibly new) event type\n // If currentEvent type is \"*\", it's already been handled by the global interceptors\n const eventType = this.getEventType(currentEvent);\n if (eventType !== \"*\") {\n const specificInterceptors = this.config.interceptors.get(eventType) || [];\n for (const interceptor of specificInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && eventKey in (result as any)) {\n currentEvent = result as TEvent;\n }\n }\n }\n\n const handlers = [\n ...(this.config.handlers.get(\"*\") || []),\n ...(this.config.handlers.get(this.getEventType(currentEvent)) || []),\n ];\n\n // First emit the event itself\n yield* this.emit(currentEvent);\n\n for (const handler of handlers) {\n const result = handler(currentEvent, context);\n if (result) {\n for await (const yieldedEvent of result) {\n // Recursively process yielded events through their handlers\n yield* this.runEventHandlers(yieldedEvent, context);\n }\n }\n }\n }\n\n /**\n * Internal helper to yield an event with metadata.\n */\n private async *emit(\n event: TEvent\n ): AsyncGenerator<TEvent> {\n yield event;\n }\n}\n","import { Runtime } from \"./runtime\";\n\nexport const MelonyRuntime = Runtime;","/**\n * Convert an async generator of events to an HTTP streaming response\n * Exported for backward compatibility and standalone usage\n */\nexport function createStreamResponse<TEvent = any>(\n generator: AsyncGenerator<TEvent>,\n): Response {\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n try {\n for await (const message of generator) {\n // Format as SSE: data: {...}\\n\\n\n const chunk = `data: ${JSON.stringify(message)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n controller.close();\n } catch (error) {\n controller.error(error);\n }\n },\n });\n\n return new Response(stream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n","import {\n EventHandler,\n Config,\n RuntimeContext,\n EventInterceptor,\n} from \"./types\";\nimport { Runtime } from \"./runtime\";\nimport { createStreamResponse } from \"./utils/create-stream-response\";\n\n/**\n * A Melony plugin is a function that receives the builder and extends it.\n * This allows for modularizing common handlers.\n */\nexport type MelonyPlugin<TState = any, TEvent = any> = (\n builder: MelonyBuilder<TState, TEvent>\n) => void;\n\n/**\n * Fluent builder for creating Melony agents with excellent developer experience.\n * Provides method chaining for handlers and plugins with full TypeScript support.\n */\nexport class MelonyBuilder<\n TState = any,\n TEvent = any\n> {\n private config: Config<TState, TEvent>;\n\n constructor(initialConfig?: Partial<Config<TState, TEvent>>) {\n this.config = {\n handlers: initialConfig?.handlers ?? new Map(),\n interceptors: initialConfig?.interceptors ?? new Map(),\n eventKey: initialConfig?.eventKey ?? \"type\",\n };\n }\n\n /**\n * Configure the key in the event object that defines its type.\n * Defaults to \"type\".\n */\n eventKey(key: string): this {\n this.config.eventKey = key;\n return this;\n }\n\n /**\n * Add an event handler for a specific event type. Supports method chaining.\n * The handler receives the narrowed event type based on the eventType string.\n */\n on(\n eventType: string | \"*\",\n handler: (\n event: TEvent,\n context: RuntimeContext<TState, TEvent>\n ) => AsyncGenerator<TEvent, void, unknown> | void\n ): this {\n if (!this.config.handlers.has(eventType)) {\n this.config.handlers.set(eventType, []);\n }\n // Cast is safe because runtime only calls this handler for matching event types\n this.config.handlers.get(eventType)!.push(handler as EventHandler<TState, TEvent>);\n return this;\n }\n\n /**\n * Register an interceptor that runs before any handlers.\n * Useful for logging, validation, or suspending for approval.\n */\n intercept(interceptor: EventInterceptor<TState, TEvent>): this;\n intercept(\n eventType: string,\n interceptor: (\n event: TEvent,\n context: RuntimeContext<TState, TEvent>\n ) => Promise<TEvent | void> | TEvent | void\n ): this;\n intercept(\n arg1: string | EventInterceptor<TState, TEvent>,\n arg2?: any\n ): this {\n if (typeof arg1 === \"string\") {\n const type = arg1;\n const interceptor = arg2!;\n if (!this.config.interceptors.has(type)) {\n this.config.interceptors.set(type, []);\n }\n this.config.interceptors.get(type)!.push(interceptor as EventInterceptor<TState, TEvent>);\n } else {\n const interceptor = arg1;\n if (!this.config.interceptors.has(\"*\")) {\n this.config.interceptors.set(\"*\", []);\n }\n this.config.interceptors.get(\"*\")!.push(interceptor);\n }\n return this;\n }\n\n /**\n * Use a plugin to extend the builder.\n * This is ideal for modularizing common handlers.\n */\n use(plugin: MelonyPlugin<TState, TEvent>): this {\n plugin(this);\n return this;\n }\n\n /**\n * Build and return the Melony runtime instance.\n * This is the final method in the fluent chain.\n */\n build(): Runtime<TState, TEvent> {\n return new Runtime(this.config);\n }\n\n /**\n * Execute and stream the response for an event.\n * This is a convenience method that builds the runtime and calls run().\n */\n async streamResponse(\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ): Promise<Response> {\n const runtime = this.build();\n const generator = runtime.run(event, options);\n return createStreamResponse(generator);\n }\n\n /**\n * Execute the agent and return the data from the last event of a specific type as a JSON response.\n * Ideal for initialization or non-streaming requests where you only need the final UI state.\n */\n async jsonResponse(\n event: TEvent,\n options?: {\n state?: TState;\n runId?: string;\n }\n ): Promise<Response> {\n const events = [];\n const runtime = this.build();\n\n for await (const e of runtime.run(event, options)) {\n events.push(e);\n }\n\n return new Response(JSON.stringify({ events }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n /**\n * Get the current configuration (useful for debugging or serialization).\n */\n getConfig(): Config<TState, TEvent> {\n return { ...this.config };\n }\n}\n\n/**\n * Factory function to create a new Melony builder instance.\n * This is the entry point for the fluent API.\n */\nexport function melony<\n TState = any,\n TEvent = any\n>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent> {\n return new MelonyBuilder<TState, TEvent>(initialConfig);\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/generate-id.ts","../src/runtime.ts","../src/utils/create-json-response.ts","../src/utils/create-stream-response.ts","../src/utils/handle-request.ts","../src/melony.ts"],"names":["event","origin","pnr"],"mappings":"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","file":"index.js","sourcesContent":["export const generateId = () => {\n return typeof crypto !== \"undefined\" && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).substring(7);\n};\n","import {\n RuntimeContext,\n Config,\n RunOptions,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\n/**\n * The Melony Runtime.\n * Fully unopinionated - processes events through handlers.\n */\nexport class Runtime<TState = any, TEvent = any> {\n public readonly config: Config<TState, TEvent>;\n\n constructor(config: Config<TState, TEvent>) {\n this.config = config;\n }\n\n /**\n * Helper to get the event type from an event object based on configuration.\n */\n private getEventType(event: TEvent): string {\n const key = this.config.eventKey || \"type\";\n return (event as any)[key] || \"*\";\n }\n\n /**\n * Helper to check if a value is a Melony Event.\n */\n private isEvent(val: any): val is TEvent {\n const key = this.config.eventKey || \"type\";\n return val && typeof val === \"object\" && typeof (val as any)[key] === \"string\";\n }\n\n /**\n * Process an incoming event through the runtime.\n * All event processing is handled by user-defined event handlers.\n */\n public async *run(\n event: TEvent,\n options?: RunOptions<TState>\n ): AsyncGenerator<TEvent> {\n const runId = options?.runId ?? generateId();\n const eventKey = this.config.eventKey || \"type\";\n\n // Resolve initial state: options.state > config.initialState > {}\n let state: TState;\n if (options?.state) {\n state = options.state;\n } else if (typeof this.config.initialState === \"function\") {\n state = await (this.config.initialState as any)();\n } else if (this.config.initialState) {\n // Use structuredClone if available, otherwise fallback to simple spread for safety\n state = typeof (globalThis as any).structuredClone === \"function\"\n ? (globalThis as any).structuredClone(this.config.initialState)\n : { ...this.config.initialState };\n } else {\n state = {} as TState;\n }\n\n const context: RuntimeContext<TState, TEvent> = {\n state,\n runtime: this,\n runId,\n suspend: (event?: TEvent) => {\n throw event || { [eventKey]: \"run-suspended\", data: {} };\n },\n };\n\n try {\n // 1. Call onStart hook\n if (this.config.onStart) {\n const startEvents = await this.config.onStart(context, event);\n if (startEvents) {\n for await (const lifecycleEvent of startEvents) {\n yield* this.runEventHandlers(lifecycleEvent, context);\n }\n }\n }\n\n // 2. Process the incoming event through handlers\n yield* this.runEventHandlers(event, context);\n } catch (error) {\n let eventToEmit: TEvent | undefined;\n\n if (this.isEvent(error)) {\n eventToEmit = error as TEvent;\n } else {\n eventToEmit = {\n [eventKey]: \"error\",\n data: {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n },\n } as unknown as TEvent;\n }\n\n if (eventToEmit) {\n yield* this.runEventHandlers(eventToEmit, context);\n }\n } finally {\n // 3. Call onEnd hook\n if (this.config.onEnd) {\n const endEvents = await this.config.onEnd(context);\n if (endEvents) {\n for await (const lifecycleEvent of endEvents) {\n yield* this.runEventHandlers(lifecycleEvent, context);\n }\n }\n }\n }\n }\n\n\n /**\n * Run all event handlers that match the given event type.\n */\n private async *runEventHandlers(\n event: TEvent,\n context: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n let currentEvent = event;\n const eventKey = this.config.eventKey || \"type\";\n\n // 1. Run global interceptors first\n const globalInterceptors = this.config.interceptors.get(\"*\") || [];\n for (const interceptor of globalInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && eventKey in (result as any)) {\n currentEvent = result as TEvent;\n }\n }\n\n // 2. Run specific interceptors for the (possibly new) event type\n // If currentEvent type is \"*\", it's already been handled by the global interceptors\n let eventType = this.getEventType(currentEvent);\n if (eventType !== \"*\") {\n const specificInterceptors = this.config.interceptors.get(eventType) || [];\n for (const interceptor of specificInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && eventKey in (result as any)) {\n currentEvent = result as TEvent;\n }\n }\n }\n\n eventType = this.getEventType(currentEvent);\n const wildcardHandlers = this.config.handlers.get(\"*\") || [];\n const specificHandlers = this.config.handlers.get(eventType) || [];\n // When type is missing, getEventType is \"*\"; do not register wildcard handlers twice.\n const handlers =\n eventType === \"*\"\n ? [...wildcardHandlers]\n : [...wildcardHandlers, ...specificHandlers];\n\n // First emit the event itself\n yield* this.emit(currentEvent, context);\n\n for (const handler of handlers) {\n const result = handler(currentEvent, context);\n if (result) {\n for await (const yieldedEvent of result) {\n // Recursively process yielded events through their handlers\n yield* this.runEventHandlers(yieldedEvent, context);\n }\n }\n }\n }\n\n /**\n * Internal helper to yield an event with metadata and trigger hooks.\n */\n private async *emit(\n event: TEvent,\n context: RuntimeContext<TState, TEvent>\n ): AsyncGenerator<TEvent> {\n if (this.config.onEvent) {\n await this.config.onEvent(context, event);\n }\n yield event;\n }\n}\n","import type { RunOptions } from \"../types\";\n\nexport interface CreateJsonResponseDeps<TState, TEvent> {\n run: (\n event: TEvent,\n options?: RunOptions<TState>\n ) => AsyncIterable<TEvent>;\n}\n\n/**\n * Run the agent to completion and return all emitted events as a JSON Response.\n * Useful for initialization or non-streaming requests.\n */\nexport async function createJsonResponse<TState, TEvent>(\n event: TEvent,\n options: RunOptions<TState> | undefined,\n deps: CreateJsonResponseDeps<TState, TEvent>\n): Promise<Response> {\n const events: TEvent[] = [];\n for await (const e of deps.run(event, options)) {\n events.push(e);\n }\n\n return new Response(JSON.stringify({ events }), {\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n","/**\n * Convert an async generator of events to an HTTP streaming response\n * Exported for backward compatibility and standalone usage\n */\nexport function createStreamResponse<TEvent = any>(\n generator: AsyncGenerator<TEvent>,\n): Response {\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n try {\n for await (const message of generator) {\n // Format as SSE: data: {...}\\n\\n\n const chunk = `data: ${JSON.stringify(message)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n controller.close();\n } catch (error) {\n controller.error(error);\n }\n },\n });\n\n return new Response(stream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n","import { generateId } from \"./generate-id\";\n\nexport interface HandleMelonyRequestOptions<TState> {\n state?: (req: Request) => Promise<TState> | TState;\n}\n\nexport interface HandleMelonyRequestDeps<TState, TEvent> {\n eventKey: string;\n initialState?: TState | (() => TState | Promise<TState>);\n streamResponse: (\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ) => Promise<Response>;\n}\n\n/**\n * Unified Web-Standard Request handler: parses Request and returns a streaming Response.\n */\nexport async function handleMelonyRequest<TState, TEvent>(\n request: Request,\n options: HandleMelonyRequestOptions<TState> | undefined,\n deps: HandleMelonyRequestDeps<TState, TEvent>\n): Promise<Response> {\n if (request.method === \"OPTIONS\") {\n const requested = request.headers.get(\"access-control-request-headers\");\n const defaultAllowHeaders =\n \"Content-Type, x-melony-thread-id, x-melony-run-id, x-melony-session-id, Authorization, Last-Event-ID\";\n const origin = request.headers.get(\"origin\");\n const allowOrigin = origin ?? \"*\";\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Origin\": allowOrigin,\n \"Access-Control-Allow-Methods\": \"POST, GET, OPTIONS\",\n \"Access-Control-Allow-Headers\": requested ?? defaultAllowHeaders,\n };\n if (origin) {\n headers.Vary = \"Origin\";\n headers[\"Access-Control-Allow-Credentials\"] = \"true\";\n }\n const pnr = request.headers.get(\"access-control-request-private-network\");\n if (pnr?.toLowerCase() === \"true\") {\n headers[\"Access-Control-Allow-Private-Network\"] = \"true\";\n }\n return new Response(null, { status: 204, headers });\n }\n\n let event: TEvent;\n try {\n if (request.method === \"POST\") {\n event = await request.json();\n } else {\n const url = new URL(request.url);\n // Avoid defaulting GET to \"run\": browsers, probes, and EventSource use GET; reconnects\n // would re-trigger full runs. Use ?type=run when you intend orchestration via GET.\n event = {\n [deps.eventKey]: url.searchParams.get(\"type\") || \"http-get\",\n data: Object.fromEntries(url.searchParams.entries()),\n } as unknown as TEvent;\n }\n } catch {\n return new Response(JSON.stringify({ error: \"Invalid JSON body\" }), {\n status: 400,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Access-Control-Allow-Origin\": \"*\",\n },\n });\n }\n\n const threadId =\n request.headers.get(\"x-melony-thread-id\") ||\n (event as { threadId?: string }).threadId ||\n generateId();\n const runId = request.headers.get(\"x-melony-run-id\") || generateId();\n const sessionId =\n request.headers.get(\"x-melony-session-id\") ||\n (event as { sessionId?: string }).sessionId ||\n generateId();\n\n let state: TState;\n if (options?.state) {\n state = await options.state(request);\n } else if (typeof deps.initialState === \"function\") {\n state = await (deps.initialState as () => TState | Promise<TState>)();\n } else if (deps.initialState) {\n state =\n typeof (globalThis as any).structuredClone === \"function\"\n ? (globalThis as any).structuredClone(deps.initialState)\n : { ...(deps.initialState as object) };\n } else {\n state = { threadId, runId, sessionId } as unknown as TState;\n }\n\n const response = await deps.streamResponse(event, { state, runId });\n const origin = request.headers.get(\"origin\");\n response.headers.set(\"Access-Control-Allow-Origin\", origin ?? \"*\");\n if (origin) {\n response.headers.set(\"Vary\", \"Origin\");\n response.headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n }\n const pnr = request.headers.get(\"access-control-request-private-network\");\n if (pnr?.toLowerCase() === \"true\") {\n response.headers.set(\"Access-Control-Allow-Private-Network\", \"true\");\n }\n return response;\n}\n","import { Runtime } from \"./runtime\";\nimport {\n EventHandler,\n Config,\n RuntimeContext,\n EventInterceptor,\n RunOptions,\n LifecycleHookResult,\n} from \"./types\";\nimport { createJsonResponse } from \"./utils/create-json-response\";\nimport { createStreamResponse } from \"./utils/create-stream-response\";\nimport { handleMelonyRequest } from \"./utils/handle-request\";\n\n/**\n * The Melony Runtime class.\n * This is the core class that powers the Melony framework.\n */\nexport const MelonyRuntime = Runtime;\n\n/**\n * A Melony plugin is a function that receives the builder and extends it.\n * This allows for modularizing common handlers.\n */\nexport type MelonyPlugin<TState = any, TEvent = any> = (\n builder: MelonyBuilder<TState, TEvent>\n) => void;\n\n/**\n * Fluent builder for creating Melony agents with excellent developer experience.\n * Provides method chaining for handlers and plugins with full TypeScript support.\n */\nexport class MelonyBuilder<\n TState = any,\n TEvent = any\n> {\n private config: Config<TState, TEvent>;\n\n constructor(initialConfig?: Partial<Config<TState, TEvent>>) {\n this.config = {\n handlers: initialConfig?.handlers ?? new Map(),\n interceptors: initialConfig?.interceptors ?? new Map(),\n eventKey: initialConfig?.eventKey ?? \"type\",\n initialState: initialConfig?.initialState,\n onStart: initialConfig?.onStart,\n onEnd: initialConfig?.onEnd,\n onEvent: initialConfig?.onEvent,\n };\n }\n\n /**\n * Configure the key in the event object that defines its type.\n * Defaults to \"type\".\n */\n eventKey(key: string): this {\n this.config.eventKey = key;\n return this;\n }\n\n /**\n * Configure the initial state for the runtime.\n * Supports a static object or a factory function.\n */\n initialState(state: TState | (() => TState | Promise<TState>)): this {\n this.config.initialState = state;\n return this;\n }\n\n /**\n * Register a hook called before the runtime starts processing events.\n */\n onStart(\n hook: (\n context: RuntimeContext<TState, TEvent>,\n initialEvent: TEvent\n ) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>\n ): this {\n this.config.onStart = hook;\n return this;\n }\n\n /**\n * Register a hook called after the runtime has finished processing events.\n */\n onEnd(\n hook: (\n context: RuntimeContext<TState, TEvent>\n ) => Promise<LifecycleHookResult<TEvent>> | LifecycleHookResult<TEvent>\n ): this {\n this.config.onEnd = hook;\n return this;\n }\n\n /**\n * Register a hook called for every event emitted by the runtime.\n */\n onEvent(\n hook: (\n context: RuntimeContext<TState, TEvent>,\n event: TEvent\n ) => Promise<void> | void\n ): this {\n this.config.onEvent = hook;\n return this;\n }\n\n /**\n * Add an event handler for a specific event type. Supports method chaining.\n * The handler receives the narrowed event type based on the eventType string.\n */\n on(\n eventType: string | \"*\",\n handler: (\n event: TEvent,\n context: RuntimeContext<TState, TEvent>\n ) => AsyncGenerator<TEvent, void, unknown> | void\n ): this {\n if (!this.config.handlers.has(eventType)) {\n this.config.handlers.set(eventType, []);\n }\n // Cast is safe because runtime only calls this handler for matching event types\n this.config.handlers.get(eventType)!.push(handler as EventHandler<TState, TEvent>);\n return this;\n }\n\n /**\n * Register an interceptor that runs before any handlers.\n * Useful for logging, validation, or suspending for approval.\n */\n intercept(interceptor: EventInterceptor<TState, TEvent>): this;\n intercept(\n eventType: string,\n interceptor: (\n event: TEvent,\n context: RuntimeContext<TState, TEvent>\n ) => Promise<TEvent | void> | TEvent | void\n ): this;\n intercept(\n arg1: string | EventInterceptor<TState, TEvent>,\n arg2?: any\n ): this {\n if (typeof arg1 === \"string\") {\n const type = arg1;\n const interceptor = arg2!;\n if (!this.config.interceptors.has(type)) {\n this.config.interceptors.set(type, []);\n }\n this.config.interceptors.get(type)!.push(interceptor as EventInterceptor<TState, TEvent>);\n } else {\n const interceptor = arg1;\n if (!this.config.interceptors.has(\"*\")) {\n this.config.interceptors.set(\"*\", []);\n }\n this.config.interceptors.get(\"*\")!.push(interceptor);\n }\n return this;\n }\n\n /**\n * Use a plugin to extend the builder.\n * This is ideal for modularizing common handlers.\n */\n use(plugin: MelonyPlugin<TState, TEvent>): this {\n plugin(this);\n return this;\n }\n\n /**\n * Build and return the Melony runtime instance.\n * This is the final method in the fluent chain.\n */\n build(): Runtime<TState, TEvent> {\n return new Runtime(this.config);\n }\n\n /**\n * Execute the runtime for a specific event.\n */\n async run(\n event: TEvent,\n options?: RunOptions<TState>\n ): Promise<AsyncGenerator<TEvent>> {\n const runtime = this.build();\n return runtime.run(event, options);\n }\n\n /**\n * Execute and stream the response for an event.\n * This is a convenience method that builds the runtime and calls run().\n */\n async streamResponse(\n event: TEvent,\n options?: { state?: TState; runId?: string }\n ): Promise<Response> {\n const runtime = this.build();\n const generator = runtime.run(event, options);\n return createStreamResponse(generator);\n }\n\n /**\n * Execute the agent and return the data from the last event of a specific type as a JSON response.\n * Ideal for initialization or non-streaming requests where you only need the final UI state.\n */\n async jsonResponse(\n event: TEvent,\n options?: RunOptions<TState>\n ): Promise<Response> {\n return createJsonResponse(event, options, {\n run: (e, opts) => this.build().run(e, opts),\n });\n }\n\n /**\n * A unified Web-Standard Request Handler.\n * Automatically parses a Request and returns a streaming Response.\n */\n async handle(\n request: Request,\n options?: { state?: (req: Request) => Promise<TState> | TState }\n ): Promise<Response> {\n return handleMelonyRequest(request, options, {\n eventKey: this.config.eventKey || \"type\",\n initialState: this.config.initialState,\n streamResponse: (event: TEvent, opts) => this.streamResponse(event, opts),\n });\n }\n\n /**\n * Get the current configuration (useful for debugging or serialization).\n */\n getConfig(): Config<TState, TEvent> {\n return { ...this.config };\n }\n}\n\n/**\n * Factory function to create a new Melony builder instance.\n * This is the entry point for the fluent API.\n */\nexport function melony<\n TState = any,\n TEvent = any\n>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent> {\n return new MelonyBuilder<TState, TEvent>(initialConfig);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "melony",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,