melony 0.3.4 → 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/README.md CHANGED
@@ -33,7 +33,7 @@ import { Runtime } from "melony";
33
33
 
34
34
  // 1. Create the runtime
35
35
  const agent = new Runtime({
36
- eventHandlers: new Map([
36
+ handlers: new Map([
37
37
  ["user:text", [async function* (event, { runtime }) {
38
38
  if (event.data.content.includes("weather")) {
39
39
  yield { type: "assistant:text", data: { content: "Weather in London is sunny!" } };
package/dist/cli.js CHANGED
@@ -1,65 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { MelonyClient } from './chunk-2LVJG7WL.js';
3
- import './chunk-WAI5H335.js';
4
2
  import 'dotenv/config';
5
- import * as readline from 'readline/promises';
6
- import * as path from 'path';
7
- import { fileURLToPath } from 'url';
8
3
  import { Command } from 'commander';
9
4
 
10
- path.dirname(fileURLToPath(import.meta.url));
11
5
  var program = new Command();
12
- program.name("melony").description("Melony CLI - Fast, minimalist AI agent framework").version("0.1.0");
13
- program.command("chat").description("Start an interactive chat session with a Melony agent").option("-u, --url <url>", "Server URL", process.env.MELONY_SERVER_URL ?? "http://localhost:4001/api/chat").action(async (options) => {
14
- const client = new MelonyClient({
15
- url: options.url
16
- });
17
- const rl = readline.createInterface({
18
- input: process.stdin,
19
- output: process.stdout
20
- });
21
- console.log("------------------------------------------");
22
- console.log("\u{1F34E} Melony Interactive CLI");
23
- console.log(`Connected to: ${options.url}`);
24
- console.log("Type your message and press Enter. Type 'exit' to quit.");
25
- console.log("------------------------------------------");
26
- while (true) {
27
- const input = await rl.question("\nYou: ");
28
- if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
29
- break;
30
- }
31
- if (!input.trim()) continue;
32
- process.stdout.write("Agent: ");
33
- try {
34
- const stream = client.send({
35
- type: "user:text",
36
- data: { content: input }
37
- });
38
- let fullResponse = "";
39
- for await (const event of stream) {
40
- if (event.type === "llm:text:delta" || event.type === "assistant:text:delta" || event.type === "assistant:text-delta") {
41
- const data = event.data;
42
- const delta = data.text ?? data.delta ?? "";
43
- process.stdout.write(delta);
44
- fullResponse += delta;
45
- } else if (event.type === "llm:text" || event.type === "assistant:text") {
46
- if (!fullResponse) {
47
- const data = event.data;
48
- process.stdout.write(data.text ?? data.content ?? "");
49
- }
50
- } else if (event.type.startsWith("action:")) {
51
- console.log(`
52
- [Action: ${JSON.stringify(event.data, null, 2)}]`);
53
- }
54
- }
55
- process.stdout.write("\n");
56
- } catch (error) {
57
- console.error("\nError:", error instanceof Error ? error.message : String(error));
58
- }
59
- }
60
- console.log("\nGoodbye!");
61
- rl.close();
62
- });
6
+ program.name("melony").description("Melony CLI - Fast, minimalist AI agent framework").version("0.0.0");
63
7
  if (!process.argv.slice(2).length) {
64
8
  process.argv.push("chat");
65
9
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":"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","file":"cli.js","sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport * as readline from \"node:readline/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { MelonyClient } from \"./client\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst program = new Command();\n\nprogram\n .name(\"melony\")\n .description(\"Melony CLI - Fast, minimalist AI agent framework\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"chat\")\n .description(\"Start an interactive chat session with a Melony agent\")\n .option(\"-u, --url <url>\", \"Server URL\", process.env.MELONY_SERVER_URL ?? \"http://localhost:4001/api/chat\")\n .action(async (options) => {\n const client = new MelonyClient({\n url: options.url,\n });\n\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n console.log(\"------------------------------------------\");\n console.log(\"🍎 Melony Interactive CLI\");\n console.log(`Connected to: ${options.url}`);\n console.log(\"Type your message and press Enter. Type 'exit' to quit.\");\n console.log(\"------------------------------------------\");\n\n while (true) {\n const input = await rl.question(\"\\nYou: \");\n\n if (input.toLowerCase() === \"exit\" || input.toLowerCase() === \"quit\") {\n break;\n }\n\n if (!input.trim()) continue;\n\n process.stdout.write(\"Agent: \");\n\n try {\n const stream = client.send({\n type: \"user:text\",\n data: { content: input },\n } as any);\n\n let fullResponse = \"\";\n\n for await (const event of stream) {\n if (\n event.type === \"llm:text:delta\" ||\n event.type === \"assistant:text:delta\" ||\n event.type === \"assistant:text-delta\"\n ) {\n const data = event.data as any;\n const delta = data.text ?? data.delta ?? \"\";\n process.stdout.write(delta);\n fullResponse += delta;\n } else if (event.type === \"llm:text\" || event.type === \"assistant:text\") {\n if (!fullResponse) {\n const data = event.data as any;\n process.stdout.write(data.text ?? data.content ?? \"\");\n }\n } else if (event.type.startsWith(\"action:\")) {\n console.log(`\\n[Action: ${JSON.stringify(event.data, null, 2)}]`);\n }\n }\n process.stdout.write(\"\\n\");\n } catch (error) {\n console.error(\"\\nError:\", error instanceof Error ? error.message : String(error));\n }\n }\n\n console.log(\"\\nGoodbye!\");\n rl.close();\n });\n\n// Default to chat if no command is provided\nif (!process.argv.slice(2).length) {\n process.argv.push(\"chat\");\n}\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":";;;;AAIA,IAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAE5B,OAAA,CACG,KAAK,QAAQ,CAAA,CACb,YAAY,kDAAkD,CAAA,CAC9D,QAAQ,OAAO,CAAA;AAGlB,IAAI,CAAC,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,CAAC,EAAE,MAAA,EAAQ;AACjC,EAAA,OAAA,CAAQ,IAAA,CAAK,KAAK,MAAM,CAAA;AAC1B;AAEA,OAAA,CAAQ,KAAA,EAAM","file":"cli.js","sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { Command } from \"commander\";\n\nconst program = new Command();\n\nprogram\n .name(\"melony\")\n .description(\"Melony CLI - Fast, minimalist AI agent framework\")\n .version(\"0.0.0\");\n\n// Default to chat if no command is provided\nif (!process.argv.slice(2).length) {\n process.argv.push(\"chat\");\n}\n\nprogram.parse();\n"]}
package/dist/index.d.ts CHANGED
@@ -1,35 +1,141 @@
1
- import { R as Runtime, E as Event, C as Config, a as RuntimeContext, I as Interceptor } from './generate-id-DOokX68f.js';
2
- export { b as EventHandler, g as generateId } from './generate-id-DOokX68f.js';
1
+ /**
2
+ * The Melony Runtime.
3
+ * Fully unopinionated - processes events through handlers.
4
+ */
5
+ declare class Runtime<TState = any, TEvent = any> {
6
+ readonly config: Config<TState, TEvent>;
7
+ constructor(config: Config<TState, TEvent>);
8
+ /**
9
+ * Helper to get the event type from an event object based on configuration.
10
+ */
11
+ private getEventType;
12
+ /**
13
+ * Helper to check if a value is a Melony Event.
14
+ */
15
+ private isEvent;
16
+ /**
17
+ * Process an incoming event through the runtime.
18
+ * All event processing is handled by user-defined event handlers.
19
+ */
20
+ run(event: TEvent, options?: RunOptions<TState>): AsyncGenerator<TEvent>;
21
+ /**
22
+ * Run all event handlers that match the given event type.
23
+ */
24
+ private runEventHandlers;
25
+ /**
26
+ * Internal helper to yield an event with metadata and trigger hooks.
27
+ */
28
+ private emit;
29
+ }
3
30
 
4
- declare const MelonyRuntime: typeof Runtime;
31
+ /**
32
+ * Options for executing a Melony run.
33
+ */
34
+ interface RunOptions<TState = any> {
35
+ state?: TState;
36
+ runId?: string;
37
+ }
38
+ interface RuntimeContext<TState = any, TEvent = any> {
39
+ state: TState;
40
+ runId: string;
41
+ runtime: Runtime<TState, TEvent>;
42
+ /**
43
+ * Immediately interrupts the runtime execution.
44
+ * If an event is provided, it will be emitted before the runtime stops.
45
+ */
46
+ suspend: (event?: TEvent) => never;
47
+ }
48
+ /**
49
+ * Interceptors run before any event handlers.
50
+ * They can modify the event or call context.suspend() to stop execution.
51
+ */
52
+ type EventInterceptor<TState = any, TEvent = any> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => Promise<TEvent | void> | TEvent | void;
53
+ /**
54
+ * Event handler function for processing events.
55
+ * Can return events to emit or undefined to continue processing.
56
+ */
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;
59
+ /**
60
+ * Configuration for the Melony runtime.
61
+ */
62
+ interface Config<TState = any, TEvent = any> {
63
+ handlers: Map<string, EventHandler<TState, TEvent>[]>;
64
+ interceptors: Map<string, EventInterceptor<TState, TEvent>[]>;
65
+ /**
66
+ * The key in the event object that defines its type.
67
+ * Defaults to "type".
68
+ */
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;
87
+ }
5
88
 
89
+ /**
90
+ * The Melony Runtime class.
91
+ * This is the core class that powers the Melony framework.
92
+ */
93
+ declare const MelonyRuntime: typeof Runtime;
6
94
  /**
7
95
  * A Melony plugin is a function that receives the builder and extends it.
8
96
  * This allows for modularizing common handlers.
9
97
  */
10
- type MelonyPlugin<TState = any, TEvent extends Event = Event> = (builder: MelonyBuilder<TState, TEvent>) => void;
98
+ type MelonyPlugin<TState = any, TEvent = any> = (builder: MelonyBuilder<TState, TEvent>) => void;
11
99
  /**
12
100
  * Fluent builder for creating Melony agents with excellent developer experience.
13
101
  * Provides method chaining for handlers and plugins with full TypeScript support.
14
102
  */
15
- declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
103
+ declare class MelonyBuilder<TState = any, TEvent = any> {
16
104
  private config;
17
105
  constructor(initialConfig?: Partial<Config<TState, TEvent>>);
106
+ /**
107
+ * Configure the key in the event object that defines its type.
108
+ * Defaults to "type".
109
+ */
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;
18
128
  /**
19
129
  * Add an event handler for a specific event type. Supports method chaining.
20
130
  * The handler receives the narrowed event type based on the eventType string.
21
131
  */
22
- on<K extends TEvent["type"]>(eventType: K | "*", handler: (event: K extends "*" ? TEvent : Extract<TEvent, {
23
- type: K;
24
- }>, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void): this;
132
+ on(eventType: string | "*", handler: (event: TEvent, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void): this;
25
133
  /**
26
134
  * Register an interceptor that runs before any handlers.
27
135
  * Useful for logging, validation, or suspending for approval.
28
136
  */
29
- intercept(interceptor: Interceptor<TState, TEvent>): this;
30
- intercept<K extends TEvent["type"]>(eventType: K, interceptor: (event: Extract<TEvent, {
31
- type: K;
32
- }>, context: RuntimeContext<TState, TEvent>) => Promise<TEvent | void> | TEvent | void): this;
137
+ intercept(interceptor: EventInterceptor<TState, TEvent>): this;
138
+ intercept(eventType: string, interceptor: (event: TEvent, context: RuntimeContext<TState, TEvent>) => Promise<TEvent | void> | TEvent | void): this;
33
139
  /**
34
140
  * Use a plugin to extend the builder.
35
141
  * This is ideal for modularizing common handlers.
@@ -40,6 +146,10 @@ declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
40
146
  * This is the final method in the fluent chain.
41
147
  */
42
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>>;
43
153
  /**
44
154
  * Execute and stream the response for an event.
45
155
  * This is a convenience method that builds the runtime and calls run().
@@ -52,9 +162,13 @@ declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
52
162
  * Execute the agent and return the data from the last event of a specific type as a JSON response.
53
163
  * Ideal for initialization or non-streaming requests where you only need the final UI state.
54
164
  */
55
- jsonResponse(event: TEvent, options?: {
56
- state?: TState;
57
- 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;
58
172
  }): Promise<Response>;
59
173
  /**
60
174
  * Get the current configuration (useful for debugging or serialization).
@@ -65,12 +179,39 @@ declare class MelonyBuilder<TState = any, TEvent extends Event = Event> {
65
179
  * Factory function to create a new Melony builder instance.
66
180
  * This is the entry point for the fluent API.
67
181
  */
68
- declare function melony<TState = any, TEvent extends Event = Event>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent>;
182
+ declare function melony<TState = any, TEvent = any>(initialConfig?: Partial<Config<TState, TEvent>>): MelonyBuilder<TState, TEvent>;
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>;
69
192
 
70
193
  /**
71
194
  * Convert an async generator of events to an HTTP streaming response
72
195
  * Exported for backward compatibility and standalone usage
73
196
  */
74
- declare function createStreamResponse(generator: AsyncGenerator<Event>): Response;
197
+ declare function createStreamResponse<TEvent = any>(generator: AsyncGenerator<TEvent>): Response;
198
+
199
+ declare const generateId: () => string;
200
+
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>;
75
216
 
76
- export { Config, Event, Interceptor, MelonyBuilder, type MelonyPlugin, MelonyRuntime, Runtime, RuntimeContext, createStreamResponse, melony };
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
@@ -1,37 +1,69 @@
1
- import { generateId } from './chunk-WAI5H335.js';
2
- export { generateId } from './chunk-WAI5H335.js';
1
+ // src/utils/generate-id.ts
2
+ var generateId = () => {
3
+ return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(7);
4
+ };
3
5
 
4
6
  // src/runtime.ts
5
- function isEvent(val) {
6
- return val && typeof val === "object" && typeof val.type === "string";
7
- }
8
7
  var Runtime = class {
9
8
  constructor(config) {
10
9
  this.config = config;
11
10
  }
11
+ /**
12
+ * Helper to get the event type from an event object based on configuration.
13
+ */
14
+ getEventType(event) {
15
+ const key = this.config.eventKey || "type";
16
+ return event[key] || "*";
17
+ }
18
+ /**
19
+ * Helper to check if a value is a Melony Event.
20
+ */
21
+ isEvent(val) {
22
+ const key = this.config.eventKey || "type";
23
+ return val && typeof val === "object" && typeof val[key] === "string";
24
+ }
12
25
  /**
13
26
  * Process an incoming event through the runtime.
14
27
  * All event processing is handled by user-defined event handlers.
15
28
  */
16
29
  async *run(event, options) {
17
30
  const runId = options?.runId ?? generateId();
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
+ }
18
42
  const context = {
19
- state: options?.state ?? {},
43
+ state,
20
44
  runtime: this,
21
45
  runId,
22
46
  suspend: (event2) => {
23
- throw event2 || { type: "run-suspended", data: {} };
47
+ throw event2 || { [eventKey]: "run-suspended", data: {} };
24
48
  }
25
49
  };
26
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
+ }
27
59
  yield* this.runEventHandlers(event, context);
28
60
  } catch (error) {
29
61
  let eventToEmit;
30
- if (isEvent(error)) {
62
+ if (this.isEvent(error)) {
31
63
  eventToEmit = error;
32
64
  } else {
33
65
  eventToEmit = {
34
- type: "error",
66
+ [eventKey]: "error",
35
67
  data: {
36
68
  message: error instanceof Error ? error.message : String(error),
37
69
  stack: error instanceof Error ? error.stack : void 0
@@ -39,7 +71,16 @@ var Runtime = class {
39
71
  };
40
72
  }
41
73
  if (eventToEmit) {
42
- yield* this.emit(eventToEmit, context);
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
+ }
43
84
  }
44
85
  }
45
86
  }
@@ -48,26 +89,28 @@ var Runtime = class {
48
89
  */
49
90
  async *runEventHandlers(event, context) {
50
91
  let currentEvent = event;
92
+ const eventKey = this.config.eventKey || "type";
51
93
  const globalInterceptors = this.config.interceptors.get("*") || [];
52
94
  for (const interceptor of globalInterceptors) {
53
95
  const result = await interceptor(currentEvent, context);
54
- if (result && typeof result === "object" && "type" in result) {
96
+ if (result && typeof result === "object" && eventKey in result) {
55
97
  currentEvent = result;
56
98
  }
57
99
  }
58
- if (currentEvent.type !== "*") {
59
- const specificInterceptors = this.config.interceptors.get(currentEvent.type) || [];
100
+ let eventType = this.getEventType(currentEvent);
101
+ if (eventType !== "*") {
102
+ const specificInterceptors = this.config.interceptors.get(eventType) || [];
60
103
  for (const interceptor of specificInterceptors) {
61
104
  const result = await interceptor(currentEvent, context);
62
- if (result && typeof result === "object" && "type" in result) {
105
+ if (result && typeof result === "object" && eventKey in result) {
63
106
  currentEvent = result;
64
107
  }
65
108
  }
66
109
  }
67
- const handlers = [
68
- ...this.config.eventHandlers.get("*") || [],
69
- ...this.config.eventHandlers.get(currentEvent.type) || []
70
- ];
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];
71
114
  yield* this.emit(currentEvent, context);
72
115
  for (const handler of handlers) {
73
116
  const result = handler(currentEvent, context);
@@ -79,15 +122,26 @@ var Runtime = class {
79
122
  }
80
123
  }
81
124
  /**
82
- * Internal helper to yield an event with metadata.
125
+ * Internal helper to yield an event with metadata and trigger hooks.
83
126
  */
84
127
  async *emit(event, context) {
128
+ if (this.config.onEvent) {
129
+ await this.config.onEvent(context, event);
130
+ }
85
131
  yield event;
86
132
  }
87
133
  };
88
134
 
89
- // src/melony.ts
90
- 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
+ }
91
145
 
92
146
  // src/utils/create-stream-response.ts
93
147
  function createStreamResponse(generator) {
@@ -116,23 +170,135 @@ function createStreamResponse(generator) {
116
170
  });
117
171
  }
118
172
 
119
- // 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;
120
244
  var MelonyBuilder = class {
121
245
  constructor(initialConfig) {
122
246
  this.config = {
123
- eventHandlers: initialConfig?.eventHandlers ?? /* @__PURE__ */ new Map(),
124
- interceptors: initialConfig?.interceptors ?? /* @__PURE__ */ new Map()
247
+ handlers: initialConfig?.handlers ?? /* @__PURE__ */ new Map(),
248
+ interceptors: initialConfig?.interceptors ?? /* @__PURE__ */ new Map(),
249
+ eventKey: initialConfig?.eventKey ?? "type",
250
+ initialState: initialConfig?.initialState,
251
+ onStart: initialConfig?.onStart,
252
+ onEnd: initialConfig?.onEnd,
253
+ onEvent: initialConfig?.onEvent
125
254
  };
126
255
  }
256
+ /**
257
+ * Configure the key in the event object that defines its type.
258
+ * Defaults to "type".
259
+ */
260
+ eventKey(key) {
261
+ this.config.eventKey = key;
262
+ return this;
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
+ }
127
293
  /**
128
294
  * Add an event handler for a specific event type. Supports method chaining.
129
295
  * The handler receives the narrowed event type based on the eventType string.
130
296
  */
131
297
  on(eventType, handler) {
132
- if (!this.config.eventHandlers.has(eventType)) {
133
- this.config.eventHandlers.set(eventType, []);
298
+ if (!this.config.handlers.has(eventType)) {
299
+ this.config.handlers.set(eventType, []);
134
300
  }
135
- this.config.eventHandlers.get(eventType).push(handler);
301
+ this.config.handlers.get(eventType).push(handler);
136
302
  return this;
137
303
  }
138
304
  intercept(arg1, arg2) {
@@ -167,6 +333,13 @@ var MelonyBuilder = class {
167
333
  build() {
168
334
  return new Runtime(this.config);
169
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
+ }
170
343
  /**
171
344
  * Execute and stream the response for an event.
172
345
  * This is a convenience method that builds the runtime and calls run().
@@ -181,13 +354,19 @@ var MelonyBuilder = class {
181
354
  * Ideal for initialization or non-streaming requests where you only need the final UI state.
182
355
  */
183
356
  async jsonResponse(event, options) {
184
- const events = [];
185
- const runtime = this.build();
186
- for await (const e of runtime.run(event, options)) {
187
- events.push(e);
188
- }
189
- return new Response(JSON.stringify({ events }), {
190
- 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)
191
370
  });
192
371
  }
193
372
  /**
@@ -201,6 +380,6 @@ function melony(initialConfig) {
201
380
  return new MelonyBuilder(initialConfig);
202
381
  }
203
382
 
204
- export { MelonyBuilder, MelonyRuntime, Runtime, createStreamResponse, melony };
383
+ export { MelonyBuilder, MelonyRuntime, Runtime, createJsonResponse, createStreamResponse, generateId, handleMelonyRequest, melony };
205
384
  //# sourceMappingURL=index.js.map
206
385
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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":["import {\n Event,\n RuntimeContext,\n Config,\n} from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\n/**\n * Helper to check if a value is a Melony Event.\n */\nfunction isEvent(val: any): val is Event {\n return val && typeof val === \"object\" && typeof val.type === \"string\";\n}\n\n/**\n * The Melony Runtime.\n * Fully unopinionated - processes events through handlers.\n */\nexport class Runtime<TState = any, TEvent extends Event = Event> {\n public readonly config: Config<TState, TEvent>;\n\n constructor(config: Config<TState, TEvent>) {\n this.config = config;\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 \n const context: RuntimeContext<TState, TEvent> = {\n state: (options?.state ?? {}) as TState,\n runtime: this,\n runId,\n suspend: (event?: TEvent) => {\n throw event || { type: \"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 (isEvent(error)) {\n eventToEmit = error as TEvent;\n } else {\n eventToEmit = {\n type: \"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, context);\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\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\" && \"type\" in result) {\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 if (currentEvent.type !== \"*\") {\n const specificInterceptors = this.config.interceptors.get(currentEvent.type) || [];\n for (const interceptor of specificInterceptors) {\n const result = await interceptor(currentEvent, context);\n if (result && typeof result === \"object\" && \"type\" in result) {\n currentEvent = result as TEvent;\n }\n }\n }\n\n const handlers = [\n ...(this.config.eventHandlers.get(\"*\") || []),\n ...(this.config.eventHandlers.get(currentEvent.type) || []),\n ];\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.\n */\n private async *emit(\n event: TEvent,\n context?: RuntimeContext<TState, TEvent>,\n ): AsyncGenerator<TEvent> {\n yield event;\n }\n}\n","import { Runtime } from \"./runtime\";\n\nexport const MelonyRuntime = Runtime;","import { Event } from \"../types\";\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(\n generator: AsyncGenerator<Event>,\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 Event,\n EventHandler,\n Config,\n RuntimeContext,\n Interceptor,\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 extends Event = Event> = (\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 extends Event = Event\n> {\n private config: Config<TState, TEvent>;\n\n constructor(initialConfig?: Partial<Config<TState, TEvent>>) {\n this.config = {\n eventHandlers: initialConfig?.eventHandlers ?? new Map(),\n interceptors: initialConfig?.interceptors ?? new Map(),\n };\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<K extends TEvent[\"type\"]>(\n eventType: K | \"*\",\n handler: (\n event: K extends \"*\" ? TEvent : Extract<TEvent, { type: K }>,\n context: RuntimeContext<TState, TEvent>\n ) => AsyncGenerator<TEvent, void, unknown> | void\n ): this {\n if (!this.config.eventHandlers.has(eventType)) {\n this.config.eventHandlers.set(eventType, []);\n }\n // Cast is safe because runtime only calls this handler for matching event types\n this.config.eventHandlers.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: Interceptor<TState, TEvent>): this;\n intercept<K extends TEvent[\"type\"]>(\n eventType: K,\n interceptor: (\n event: Extract<TEvent, { type: K }>,\n context: RuntimeContext<TState, TEvent>\n ) => Promise<TEvent | void> | TEvent | void\n ): this;\n intercept(\n arg1: string | Interceptor<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 Interceptor<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 extends Event = Event\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.4",
3
+ "version": "0.3.6",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -26,11 +26,6 @@
26
26
  "import": "./dist/index.js",
27
27
  "default": "./dist/index.js"
28
28
  },
29
- "./client": {
30
- "types": "./dist/client.d.ts",
31
- "import": "./dist/client.js",
32
- "default": "./dist/client.js"
33
- },
34
29
  "./package.json": "./package.json"
35
30
  },
36
31
  "module": "dist/index.js",
@@ -1,171 +0,0 @@
1
- import { generateId } from './chunk-WAI5H335.js';
2
-
3
- // src/client.ts
4
- var MelonyClient = class {
5
- constructor(options) {
6
- this.abortController = null;
7
- this.stateListeners = /* @__PURE__ */ new Set();
8
- this.url = options.url;
9
- this.headers = options.headers;
10
- this.state = {
11
- events: options.initialEvents ?? [],
12
- streaming: false,
13
- error: null,
14
- context: options.initialContext ?? {}
15
- };
16
- }
17
- subscribe(listener) {
18
- this.stateListeners.add(listener);
19
- return () => {
20
- this.stateListeners.delete(listener);
21
- };
22
- }
23
- getState() {
24
- return { ...this.state };
25
- }
26
- async getRequestHeaders() {
27
- const headers = {
28
- "Content-Type": "application/json"
29
- };
30
- if (this.headers) {
31
- const extraHeaders = typeof this.headers === "function" ? await this.headers() : this.headers;
32
- Object.assign(headers, extraHeaders);
33
- }
34
- return headers;
35
- }
36
- setState(updates) {
37
- this.state = { ...this.state, ...updates };
38
- this.stateListeners.forEach((l) => l(this.getState()));
39
- }
40
- async *send(event, additionalBody) {
41
- if (this.abortController) this.abortController.abort();
42
- this.abortController = new AbortController();
43
- const optimisticEvent = {
44
- id: generateId(),
45
- ...event
46
- };
47
- this.setState({
48
- streaming: true,
49
- error: null,
50
- events: [...this.state.events, optimisticEvent]
51
- });
52
- try {
53
- const headers = await this.getRequestHeaders();
54
- const runResponse = await fetch(`${this.url}/runs`, {
55
- method: "POST",
56
- headers,
57
- body: JSON.stringify({
58
- event: optimisticEvent,
59
- ...additionalBody
60
- }),
61
- signal: this.abortController.signal
62
- });
63
- if (!runResponse.ok)
64
- throw new Error(`HTTP error! status: ${runResponse.status}`);
65
- const { runId, threadId } = await runResponse.json();
66
- yield* this.stream({ runId, threadId });
67
- } catch (err) {
68
- if (err instanceof Error && err.name === "AbortError") {
69
- this.setState({ streaming: false });
70
- return;
71
- }
72
- const error = err instanceof Error ? err : new Error(String(err));
73
- this.setState({ error, streaming: false });
74
- throw error;
75
- }
76
- }
77
- /**
78
- * Stream events from a specific run or thread.
79
- */
80
- async *stream(filter) {
81
- if (this.abortController) this.abortController.abort();
82
- this.abortController = new AbortController();
83
- this.setState({
84
- streaming: true,
85
- error: null
86
- });
87
- try {
88
- const headers = await this.getRequestHeaders();
89
- const streamUrl = new URL(`${this.url}/stream`);
90
- if (filter.runId) streamUrl.searchParams.set("runId", filter.runId);
91
- if (filter.threadId) streamUrl.searchParams.set("threadId", filter.threadId);
92
- const response = await fetch(streamUrl.toString(), {
93
- headers: {
94
- ...headers,
95
- "Accept": "text/event-stream"
96
- },
97
- signal: this.abortController.signal
98
- });
99
- if (!response.ok)
100
- throw new Error(`HTTP error! status: ${response.status}`);
101
- if (!response.body) throw new Error("No response body");
102
- const reader = response.body.getReader();
103
- const decoder = new TextDecoder();
104
- let buffer = "";
105
- while (true) {
106
- const { done, value } = await reader.read();
107
- if (done) break;
108
- buffer += decoder.decode(value, { stream: true });
109
- const lines = buffer.split("\n\n");
110
- buffer = lines.pop() || "";
111
- for (const line of lines) {
112
- if (!line.startsWith("data: ")) continue;
113
- try {
114
- const incomingEvent = JSON.parse(line.slice(6));
115
- const handled = this.handleIncomingEvent(incomingEvent);
116
- if (!handled) continue;
117
- yield incomingEvent;
118
- if (incomingEvent.type === "run:status") {
119
- const data = incomingEvent.data;
120
- if (data?.status === "completed" || data?.status === "failed") {
121
- this.setState({ streaming: false });
122
- return;
123
- }
124
- }
125
- } catch (e) {
126
- console.error("Failed to parse event", e);
127
- }
128
- }
129
- }
130
- this.setState({ streaming: false });
131
- } catch (err) {
132
- if (err instanceof Error && err.name === "AbortError") {
133
- this.setState({ streaming: false });
134
- return;
135
- }
136
- const error = err instanceof Error ? err : new Error(String(err));
137
- this.setState({ error, streaming: false });
138
- throw error;
139
- }
140
- }
141
- handleIncomingEvent(event) {
142
- const events = [...this.state.events];
143
- const index = event.id ? events.findIndex((e) => e.id === event.id) : -1;
144
- if (index !== -1) {
145
- events[index] = event;
146
- } else {
147
- events.push(event);
148
- }
149
- this.setState({ events });
150
- return true;
151
- }
152
- reset(events = []) {
153
- this.stop();
154
- this.setState({
155
- events,
156
- error: null,
157
- streaming: false
158
- });
159
- }
160
- stop() {
161
- if (this.abortController) {
162
- this.abortController.abort();
163
- this.abortController = null;
164
- this.setState({ streaming: false });
165
- }
166
- }
167
- };
168
-
169
- export { MelonyClient };
170
- //# sourceMappingURL=chunk-2LVJG7WL.js.map
171
- //# sourceMappingURL=chunk-2LVJG7WL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"chunk-2LVJG7WL.js","sourcesContent":["import { Event } from \"./types\";\nimport { generateId } from \"./utils/generate-id\";\n\nexport type { Event };\nexport { generateId };\n\nexport interface ClientState<TEvent extends Event = Event> {\n events: TEvent[];\n streaming: boolean;\n error: Error | null;\n context: Record<string, any>;\n}\n\nexport interface MelonyClientOptions<TEvent extends Event = Event> {\n url: string;\n initialEvents?: TEvent[];\n initialContext?: Record<string, any>;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n}\n\nexport class MelonyClient<TEvent extends Event = Event> {\n private state: ClientState<TEvent>;\n public readonly url: string;\n private headers?: MelonyClientOptions<TEvent>[\"headers\"];\n private abortController: AbortController | null = null;\n private stateListeners: Set<(state: ClientState<TEvent>) => void> = new Set();\n\n constructor(options: MelonyClientOptions<TEvent>) {\n this.url = options.url;\n this.headers = options.headers;\n this.state = {\n events: options.initialEvents ?? [],\n streaming: false,\n error: null,\n context: options.initialContext ?? {},\n };\n }\n\n subscribe(listener: (state: ClientState<TEvent>) => void) {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n getState() {\n return { ...this.state };\n }\n\n private async getRequestHeaders() {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.headers) {\n const extraHeaders =\n typeof this.headers === \"function\"\n ? await this.headers()\n : this.headers;\n Object.assign(headers, extraHeaders);\n }\n return headers;\n }\n\n private setState(updates: Partial<ClientState<TEvent>>) {\n this.state = { ...this.state, ...updates };\n this.stateListeners.forEach((l) => l(this.getState()));\n }\n\n async *send(\n event: TEvent,\n additionalBody?: Record<string, any>\n ): AsyncGenerator<TEvent> {\n if (this.abortController) this.abortController.abort();\n this.abortController = new AbortController();\n\n const optimisticEvent: TEvent = {\n id: generateId(),\n ...event\n } as TEvent;\n\n this.setState({\n streaming: true,\n error: null,\n events: [...this.state.events, optimisticEvent],\n });\n\n try {\n const headers = await this.getRequestHeaders();\n \n // 1. Create a Run\n const runResponse = await fetch(`${this.url}/runs`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n event: optimisticEvent,\n ...additionalBody,\n }),\n signal: this.abortController.signal,\n });\n\n if (!runResponse.ok)\n throw new Error(`HTTP error! status: ${runResponse.status}`);\n \n const { runId, threadId } = await runResponse.json();\n yield* this.stream({ runId, threadId });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n this.setState({ streaming: false });\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n this.setState({ error, streaming: false });\n throw error;\n }\n }\n\n /**\n * Stream events from a specific run or thread.\n */\n async *stream(filter: { runId?: string; threadId?: string }): AsyncGenerator<TEvent> {\n if (this.abortController) this.abortController.abort();\n this.abortController = new AbortController();\n\n this.setState({\n streaming: true,\n error: null,\n });\n\n try {\n const headers = await this.getRequestHeaders();\n const streamUrl = new URL(`${this.url}/stream`);\n if (filter.runId) streamUrl.searchParams.set(\"runId\", filter.runId);\n if (filter.threadId) streamUrl.searchParams.set(\"threadId\", filter.threadId);\n\n const response = await fetch(streamUrl.toString(), {\n headers: {\n ...headers,\n \"Accept\": \"text/event-stream\",\n },\n signal: this.abortController.signal,\n });\n\n if (!response.ok)\n throw new Error(`HTTP error! status: ${response.status}`);\n if (!response.body) throw new Error(\"No response body\");\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n try {\n const incomingEvent: TEvent = JSON.parse(line.slice(6));\n const handled = this.handleIncomingEvent(incomingEvent);\n if (!handled) continue;\n yield incomingEvent;\n\n // If we receive a run:status event with completed/failed, we can stop\n if (incomingEvent.type === \"run:status\") {\n const data = incomingEvent.data as { status?: string };\n if (data?.status === \"completed\" || data?.status === \"failed\") {\n this.setState({ streaming: false });\n return;\n }\n }\n } catch (e) {\n console.error(\"Failed to parse event\", e);\n }\n }\n }\n this.setState({ streaming: false });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n this.setState({ streaming: false });\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n this.setState({ error, streaming: false });\n throw error;\n }\n }\n\n private handleIncomingEvent(event: TEvent) {\n const events = [...this.state.events];\n\n // Replace optimistic event if IDs match, otherwise push\n const index = event.id ? events.findIndex((e) => e.id === event.id) : -1;\n if (index !== -1) {\n events[index] = event;\n } else {\n events.push(event);\n }\n\n this.setState({ events });\n return true;\n }\n\n reset(events: TEvent[] = []) {\n this.stop();\n this.setState({\n events,\n error: null,\n streaming: false,\n });\n }\n\n stop() {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n this.setState({ streaming: false });\n }\n }\n}\n"]}
@@ -1,8 +0,0 @@
1
- // src/utils/generate-id.ts
2
- var generateId = () => {
3
- return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(7);
4
- };
5
-
6
- export { generateId };
7
- //# sourceMappingURL=chunk-WAI5H335.js.map
8
- //# sourceMappingURL=chunk-WAI5H335.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/generate-id.ts"],"names":[],"mappings":";AAAO,IAAM,aAAa,MAAM;AAC9B,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,aAC3C,MAAA,CAAO,UAAA,EAAW,GAClB,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,UAAU,CAAC,CAAA;AAC5C","file":"chunk-WAI5H335.js","sourcesContent":["export const generateId = () => {\n return typeof crypto !== \"undefined\" && crypto.randomUUID\n ? crypto.randomUUID()\n : Math.random().toString(36).substring(7);\n};\n"]}
package/dist/client.d.ts DELETED
@@ -1,45 +0,0 @@
1
- import { E as Event } from './generate-id-DOokX68f.js';
2
- export { g as generateId } from './generate-id-DOokX68f.js';
3
-
4
- interface ClientState<TEvent extends Event = Event> {
5
- events: TEvent[];
6
- streaming: boolean;
7
- error: Error | null;
8
- context: Record<string, any>;
9
- }
10
- interface MelonyClientOptions<TEvent extends Event = Event> {
11
- url: string;
12
- initialEvents?: TEvent[];
13
- initialContext?: Record<string, any>;
14
- headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
15
- }
16
- declare class MelonyClient<TEvent extends Event = Event> {
17
- private state;
18
- readonly url: string;
19
- private headers?;
20
- private abortController;
21
- private stateListeners;
22
- constructor(options: MelonyClientOptions<TEvent>);
23
- subscribe(listener: (state: ClientState<TEvent>) => void): () => void;
24
- getState(): {
25
- events: TEvent[];
26
- streaming: boolean;
27
- error: Error | null;
28
- context: Record<string, any>;
29
- };
30
- private getRequestHeaders;
31
- private setState;
32
- send(event: TEvent, additionalBody?: Record<string, any>): AsyncGenerator<TEvent>;
33
- /**
34
- * Stream events from a specific run or thread.
35
- */
36
- stream(filter: {
37
- runId?: string;
38
- threadId?: string;
39
- }): AsyncGenerator<TEvent>;
40
- private handleIncomingEvent;
41
- reset(events?: TEvent[]): void;
42
- stop(): void;
43
- }
44
-
45
- export { type ClientState, Event, MelonyClient, type MelonyClientOptions };
package/dist/client.js DELETED
@@ -1,4 +0,0 @@
1
- export { MelonyClient } from './chunk-2LVJG7WL.js';
2
- export { generateId } from './chunk-WAI5H335.js';
3
- //# sourceMappingURL=client.js.map
4
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"client.js"}
@@ -1,63 +0,0 @@
1
- /**
2
- * The Melony Runtime.
3
- * Fully unopinionated - processes events through handlers.
4
- */
5
- declare class Runtime<TState = any, TEvent extends Event = Event> {
6
- readonly config: Config<TState, TEvent>;
7
- constructor(config: Config<TState, TEvent>);
8
- /**
9
- * Process an incoming event through the runtime.
10
- * All event processing is handled by user-defined event handlers.
11
- */
12
- run(event: TEvent, options?: {
13
- state?: TState;
14
- runId?: string;
15
- }): AsyncGenerator<TEvent>;
16
- /**
17
- * Run all event handlers that match the given event type.
18
- */
19
- private runEventHandlers;
20
- /**
21
- * Internal helper to yield an event with metadata.
22
- */
23
- private emit;
24
- }
25
-
26
- /**
27
- * The core Event structure.
28
- * Truly unopinionated - only 'type' is strictly required for dispatching.
29
- */
30
- type Event = {
31
- /** The type of the event (required for runtime dispatching) */
32
- type: string;
33
- /** Catch-all for any other custom fields */
34
- [key: string]: any;
35
- };
36
- interface RuntimeContext<TState = any, TEvent extends Event = Event> {
37
- state: TState;
38
- runId: string;
39
- runtime: Runtime<TState, TEvent>;
40
- /**
41
- * Immediately interrupts the runtime execution.
42
- * If an event is provided, it will be emitted before the runtime stops.
43
- */
44
- suspend: (event?: TEvent) => never;
45
- }
46
- /**
47
- * Interceptors run before any event handlers.
48
- * They can modify the event or call context.suspend() to stop execution.
49
- */
50
- type Interceptor<TState = any, TEvent extends Event = Event> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => Promise<TEvent | void> | TEvent | void;
51
- /**
52
- * Event handler function for processing events.
53
- * Can return events to emit or undefined to continue processing.
54
- */
55
- type EventHandler<TState = any, TEvent extends Event = Event> = (event: TEvent, context: RuntimeContext<TState, TEvent>) => AsyncGenerator<TEvent, void, unknown> | void;
56
- interface Config<TState = any, TEvent extends Event = Event> {
57
- eventHandlers: Map<string, EventHandler<TState, TEvent>[]>;
58
- interceptors: Map<string, Interceptor<TState, TEvent>[]>;
59
- }
60
-
61
- declare const generateId: () => string;
62
-
63
- export { type Config as C, type Event as E, type Interceptor as I, Runtime as R, type RuntimeContext as a, type EventHandler as b, generateId as g };