@kubuild/ai 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-27AY2KBI.js +500 -0
  3. package/dist/chunk-27AY2KBI.js.map +1 -0
  4. package/dist/chunk-JY4NHD3Q.js +227 -0
  5. package/dist/chunk-JY4NHD3Q.js.map +1 -0
  6. package/dist/chunk-UH24LHRY.js +443 -0
  7. package/dist/chunk-UH24LHRY.js.map +1 -0
  8. package/dist/client/ai-client.d.ts +59 -0
  9. package/dist/client/ai-client.d.ts.map +1 -0
  10. package/dist/client/index.cjs +254 -0
  11. package/dist/client/index.cjs.map +1 -0
  12. package/dist/client/index.d.ts +2 -0
  13. package/dist/client/index.d.ts.map +1 -0
  14. package/dist/client/index.js +9 -0
  15. package/dist/client/index.js.map +1 -0
  16. package/dist/core/index.d.ts +3 -0
  17. package/dist/core/index.d.ts.map +1 -0
  18. package/dist/core/normalizer.d.ts +9 -0
  19. package/dist/core/normalizer.d.ts.map +1 -0
  20. package/dist/core/prompt-compiler.d.ts +10 -0
  21. package/dist/core/prompt-compiler.d.ts.map +1 -0
  22. package/dist/index.cjs +468 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +23 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/react/index.cjs +584 -0
  29. package/dist/react/index.cjs.map +1 -0
  30. package/dist/react/index.d.ts +3 -0
  31. package/dist/react/index.d.ts.map +1 -0
  32. package/dist/react/index.js +336 -0
  33. package/dist/react/index.js.map +1 -0
  34. package/dist/react/use-ai-chat.d.ts +63 -0
  35. package/dist/react/use-ai-chat.d.ts.map +1 -0
  36. package/dist/react/use-ai-generator.d.ts +19 -0
  37. package/dist/react/use-ai-generator.d.ts.map +1 -0
  38. package/dist/server/adapters/anthropic.d.ts +30 -0
  39. package/dist/server/adapters/anthropic.d.ts.map +1 -0
  40. package/dist/server/adapters/base.d.ts +2 -0
  41. package/dist/server/adapters/base.d.ts.map +1 -0
  42. package/dist/server/adapters/custom.d.ts +18 -0
  43. package/dist/server/adapters/custom.d.ts.map +1 -0
  44. package/dist/server/adapters/gemini.d.ts +22 -0
  45. package/dist/server/adapters/gemini.d.ts.map +1 -0
  46. package/dist/server/adapters/index.cjs +529 -0
  47. package/dist/server/adapters/index.cjs.map +1 -0
  48. package/dist/server/adapters/index.d.ts +6 -0
  49. package/dist/server/adapters/index.d.ts.map +1 -0
  50. package/dist/server/adapters/index.js +13 -0
  51. package/dist/server/adapters/index.js.map +1 -0
  52. package/dist/server/adapters/openai.d.ts +26 -0
  53. package/dist/server/adapters/openai.d.ts.map +1 -0
  54. package/dist/server/adapters/stream-utils.d.ts +12 -0
  55. package/dist/server/adapters/stream-utils.d.ts.map +1 -0
  56. package/dist/server/engine.d.ts +59 -0
  57. package/dist/server/engine.d.ts.map +1 -0
  58. package/dist/server/handler.d.ts +66 -0
  59. package/dist/server/handler.d.ts.map +1 -0
  60. package/dist/server/index.cjs +1824 -0
  61. package/dist/server/index.cjs.map +1 -0
  62. package/dist/server/index.d.ts +4 -0
  63. package/dist/server/index.d.ts.map +1 -0
  64. package/dist/server/index.js +890 -0
  65. package/dist/server/index.js.map +1 -0
  66. package/dist/types.d.ts +184 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/package.json +94 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kustora
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,500 @@
1
+ // src/server/adapters/stream-utils.ts
2
+ async function* readSseDataLines(body, signal) {
3
+ const reader = body.getReader();
4
+ const decoder = new TextDecoder();
5
+ let buffer = "";
6
+ try {
7
+ while (true) {
8
+ if (signal?.aborted) {
9
+ throw new Error("Streaming aborted by client");
10
+ }
11
+ const { done, value } = await reader.read();
12
+ if (done) break;
13
+ buffer += decoder.decode(value, { stream: true });
14
+ const frames = buffer.split("\n\n");
15
+ buffer = frames.pop() || "";
16
+ for (const frame of frames) {
17
+ for (const line of frame.split("\n")) {
18
+ const trimmed = line.trim();
19
+ if (trimmed.startsWith("data:")) {
20
+ yield trimmed.slice(5).trim();
21
+ }
22
+ }
23
+ }
24
+ }
25
+ } finally {
26
+ reader.releaseLock();
27
+ }
28
+ }
29
+
30
+ // src/server/adapters/gemini.ts
31
+ var GeminiAdapter = class {
32
+ name = "gemini";
33
+ apiKey;
34
+ model;
35
+ temperature;
36
+ baseUrl;
37
+ constructor(options) {
38
+ if (!options.apiKey) {
39
+ throw new Error("GeminiAdapter requires an apiKey");
40
+ }
41
+ this.apiKey = options.apiKey;
42
+ this.model = options.model || "gemini-2.5-flash";
43
+ this.temperature = options.temperature ?? 0.4;
44
+ this.baseUrl = options.baseUrl || "https://generativelanguage.googleapis.com/v1beta";
45
+ }
46
+ buildContents(params) {
47
+ const { userPrompt, messages: chatMessages } = params;
48
+ return chatMessages && chatMessages.length > 0 ? chatMessages.map((m) => ({
49
+ role: m.role === "assistant" ? "model" : "user",
50
+ parts: [{ text: m.content }]
51
+ })) : [
52
+ {
53
+ role: "user",
54
+ parts: [{ text: userPrompt }]
55
+ }
56
+ ];
57
+ }
58
+ async generate(params) {
59
+ const { systemPrompt, messages: chatMessages, signal } = params;
60
+ const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;
61
+ const contents = this.buildContents(params);
62
+ const generationConfig = {
63
+ temperature: this.temperature
64
+ };
65
+ if (params.jsonSchema || !chatMessages && (systemPrompt.includes("JSON") || systemPrompt.includes("json"))) {
66
+ generationConfig.responseMimeType = "application/json";
67
+ }
68
+ const body = {
69
+ system_instruction: {
70
+ parts: [{ text: systemPrompt }]
71
+ },
72
+ contents,
73
+ generationConfig
74
+ };
75
+ const res = await fetch(url, {
76
+ method: "POST",
77
+ headers: {
78
+ "Content-Type": "application/json"
79
+ },
80
+ body: JSON.stringify(body),
81
+ signal
82
+ });
83
+ if (!res.ok) {
84
+ const errText = await res.text().catch(() => "");
85
+ throw new Error(
86
+ `Gemini API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
87
+ );
88
+ }
89
+ const data = await res.json();
90
+ const candidateText = data.candidates?.[0]?.content?.parts?.[0]?.text;
91
+ if (!candidateText) {
92
+ throw new Error("Gemini returned an empty or invalid candidate response");
93
+ }
94
+ return {
95
+ text: candidateText,
96
+ usage: data.usageMetadata ? {
97
+ promptTokens: data.usageMetadata.promptTokenCount,
98
+ completionTokens: data.usageMetadata.candidatesTokenCount
99
+ } : void 0
100
+ };
101
+ }
102
+ /**
103
+ * Token-level streaming via Gemini's `streamGenerateContent` SSE endpoint (STORA-515).
104
+ */
105
+ async *generateStream(params) {
106
+ const { systemPrompt, signal } = params;
107
+ const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.apiKey)}`;
108
+ const contents = this.buildContents(params);
109
+ const generationConfig = {
110
+ temperature: this.temperature
111
+ };
112
+ const body = {
113
+ system_instruction: {
114
+ parts: [{ text: systemPrompt }]
115
+ },
116
+ contents,
117
+ generationConfig
118
+ };
119
+ const res = await fetch(url, {
120
+ method: "POST",
121
+ headers: {
122
+ "Content-Type": "application/json"
123
+ },
124
+ body: JSON.stringify(body),
125
+ signal
126
+ });
127
+ if (!res.ok) {
128
+ const errText = await res.text().catch(() => "");
129
+ throw new Error(
130
+ `Gemini API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
131
+ );
132
+ }
133
+ if (!res.body) {
134
+ throw new Error("Gemini streaming response has no readable body");
135
+ }
136
+ let text = "";
137
+ let usage;
138
+ for await (const raw of readSseDataLines(res.body, signal)) {
139
+ let parsed;
140
+ try {
141
+ parsed = JSON.parse(raw);
142
+ } catch {
143
+ continue;
144
+ }
145
+ const delta = parsed.candidates?.[0]?.content?.parts?.[0]?.text;
146
+ if (typeof delta === "string" && delta.length > 0) {
147
+ text += delta;
148
+ yield delta;
149
+ }
150
+ if (parsed.usageMetadata) {
151
+ usage = {
152
+ promptTokens: parsed.usageMetadata.promptTokenCount,
153
+ completionTokens: parsed.usageMetadata.candidatesTokenCount
154
+ };
155
+ }
156
+ }
157
+ return { text, usage };
158
+ }
159
+ };
160
+
161
+ // src/server/adapters/openai.ts
162
+ var OpenAiAdapter = class {
163
+ name = "openai";
164
+ apiKey;
165
+ model;
166
+ temperature;
167
+ baseUrl;
168
+ customHeaders;
169
+ constructor(options) {
170
+ if (!options.apiKey) {
171
+ throw new Error("OpenAiAdapter requires an apiKey");
172
+ }
173
+ this.apiKey = options.apiKey;
174
+ this.model = options.model || "gpt-4o";
175
+ this.temperature = options.temperature ?? 0.4;
176
+ this.baseUrl = (options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
177
+ this.customHeaders = options.headers;
178
+ }
179
+ buildMessages(params) {
180
+ const { systemPrompt, userPrompt, messages: chatMessages } = params;
181
+ return chatMessages && chatMessages.length > 0 ? [
182
+ { role: "system", content: systemPrompt },
183
+ ...chatMessages.map((m) => ({ role: m.role, content: m.content }))
184
+ ] : [
185
+ { role: "system", content: systemPrompt },
186
+ { role: "user", content: userPrompt }
187
+ ];
188
+ }
189
+ async generate(params) {
190
+ const { systemPrompt, signal } = params;
191
+ const url = `${this.baseUrl}/chat/completions`;
192
+ const messages = this.buildMessages(params);
193
+ const chatMessages = params.messages;
194
+ const body = {
195
+ model: this.model,
196
+ temperature: this.temperature,
197
+ messages
198
+ };
199
+ if (params.jsonSchema || !chatMessages && (systemPrompt.includes("JSON") || systemPrompt.includes("json"))) {
200
+ body.response_format = { type: "json_object" };
201
+ }
202
+ const res = await fetch(url, {
203
+ method: "POST",
204
+ headers: {
205
+ "Content-Type": "application/json",
206
+ Authorization: `Bearer ${this.apiKey}`,
207
+ ...this.customHeaders
208
+ },
209
+ body: JSON.stringify(body),
210
+ signal
211
+ });
212
+ if (!res.ok) {
213
+ const errText = await res.text().catch(() => "");
214
+ throw new Error(
215
+ `OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
216
+ );
217
+ }
218
+ const data = await res.json();
219
+ const content = data.choices?.[0]?.message?.content;
220
+ if (!content) {
221
+ throw new Error("OpenAI returned an empty message content");
222
+ }
223
+ return {
224
+ text: content,
225
+ usage: data.usage ? {
226
+ promptTokens: data.usage.prompt_tokens,
227
+ completionTokens: data.usage.completion_tokens
228
+ } : void 0
229
+ };
230
+ }
231
+ /**
232
+ * Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).
233
+ * Yields each `delta.content` fragment as it arrives and returns the fully assembled
234
+ * text + usage (when the final chunk includes it) once the stream ends.
235
+ */
236
+ async *generateStream(params) {
237
+ const { signal } = params;
238
+ const url = `${this.baseUrl}/chat/completions`;
239
+ const messages = this.buildMessages(params);
240
+ const body = {
241
+ model: this.model,
242
+ temperature: this.temperature,
243
+ messages,
244
+ stream: true
245
+ };
246
+ const res = await fetch(url, {
247
+ method: "POST",
248
+ headers: {
249
+ "Content-Type": "application/json",
250
+ Authorization: `Bearer ${this.apiKey}`,
251
+ ...this.customHeaders
252
+ },
253
+ body: JSON.stringify(body),
254
+ signal
255
+ });
256
+ if (!res.ok) {
257
+ const errText = await res.text().catch(() => "");
258
+ throw new Error(
259
+ `OpenAI API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
260
+ );
261
+ }
262
+ if (!res.body) {
263
+ throw new Error("OpenAI streaming response has no readable body");
264
+ }
265
+ let text = "";
266
+ let usage;
267
+ for await (const raw of readSseDataLines(res.body, signal)) {
268
+ if (raw === "[DONE]") break;
269
+ let parsed;
270
+ try {
271
+ parsed = JSON.parse(raw);
272
+ } catch {
273
+ continue;
274
+ }
275
+ const delta = parsed.choices?.[0]?.delta?.content;
276
+ if (typeof delta === "string" && delta.length > 0) {
277
+ text += delta;
278
+ yield delta;
279
+ }
280
+ if (parsed.usage) {
281
+ usage = {
282
+ promptTokens: parsed.usage.prompt_tokens,
283
+ completionTokens: parsed.usage.completion_tokens
284
+ };
285
+ }
286
+ }
287
+ return { text, usage };
288
+ }
289
+ };
290
+
291
+ // src/server/adapters/anthropic.ts
292
+ var AnthropicAdapter = class {
293
+ name = "anthropic";
294
+ apiKey;
295
+ model;
296
+ maxTokens;
297
+ temperature;
298
+ baseUrl;
299
+ anthropicVersion;
300
+ customHeaders;
301
+ constructor(options) {
302
+ if (!options.apiKey) {
303
+ throw new Error("AnthropicAdapter requires an apiKey");
304
+ }
305
+ this.apiKey = options.apiKey;
306
+ this.model = options.model || "claude-3-5-sonnet-20241022";
307
+ this.maxTokens = options.maxTokens ?? 4096;
308
+ this.temperature = options.temperature ?? 0.4;
309
+ this.baseUrl = (options.baseUrl || "https://api.anthropic.com/v1").replace(/\/+$/, "");
310
+ this.anthropicVersion = options.anthropicVersion || "2023-06-01";
311
+ this.customHeaders = options.headers;
312
+ }
313
+ buildMessages(params) {
314
+ const { userPrompt, messages: chatMessages } = params;
315
+ return chatMessages && chatMessages.length > 0 ? chatMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m.content })) : [
316
+ {
317
+ role: "user",
318
+ content: userPrompt
319
+ }
320
+ ];
321
+ }
322
+ async generate(params) {
323
+ const { systemPrompt, signal } = params;
324
+ const url = `${this.baseUrl}/messages`;
325
+ const messages = this.buildMessages(params);
326
+ const body = {
327
+ model: this.model,
328
+ max_tokens: this.maxTokens,
329
+ temperature: this.temperature,
330
+ system: systemPrompt,
331
+ messages
332
+ };
333
+ const res = await fetch(url, {
334
+ method: "POST",
335
+ headers: {
336
+ "Content-Type": "application/json",
337
+ "x-api-key": this.apiKey,
338
+ "anthropic-version": this.anthropicVersion,
339
+ ...this.customHeaders
340
+ },
341
+ body: JSON.stringify(body),
342
+ signal
343
+ });
344
+ if (!res.ok) {
345
+ const errText = await res.text().catch(() => "");
346
+ throw new Error(
347
+ `Anthropic API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
348
+ );
349
+ }
350
+ const data = await res.json();
351
+ const textPart = data.content?.find((c) => c.type === "text")?.text;
352
+ if (!textPart) {
353
+ throw new Error("Anthropic returned an empty text content");
354
+ }
355
+ return {
356
+ text: textPart,
357
+ usage: data.usage ? {
358
+ promptTokens: data.usage.input_tokens,
359
+ completionTokens: data.usage.output_tokens
360
+ } : void 0
361
+ };
362
+ }
363
+ /**
364
+ * Token-level streaming via Anthropic's `stream: true` Messages API (STORA-515).
365
+ * Parses the `content_block_delta` (`text_delta`) events for incremental text and the
366
+ * `message_start`/`message_delta` events for usage accounting.
367
+ */
368
+ async *generateStream(params) {
369
+ const { systemPrompt, signal } = params;
370
+ const url = `${this.baseUrl}/messages`;
371
+ const messages = this.buildMessages(params);
372
+ const body = {
373
+ model: this.model,
374
+ max_tokens: this.maxTokens,
375
+ temperature: this.temperature,
376
+ system: systemPrompt,
377
+ messages,
378
+ stream: true
379
+ };
380
+ const res = await fetch(url, {
381
+ method: "POST",
382
+ headers: {
383
+ "Content-Type": "application/json",
384
+ "x-api-key": this.apiKey,
385
+ "anthropic-version": this.anthropicVersion,
386
+ ...this.customHeaders
387
+ },
388
+ body: JSON.stringify(body),
389
+ signal
390
+ });
391
+ if (!res.ok) {
392
+ const errText = await res.text().catch(() => "");
393
+ throw new Error(
394
+ `Anthropic API error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
395
+ );
396
+ }
397
+ if (!res.body) {
398
+ throw new Error("Anthropic streaming response has no readable body");
399
+ }
400
+ let text = "";
401
+ let promptTokens;
402
+ let completionTokens;
403
+ for await (const raw of readSseDataLines(res.body, signal)) {
404
+ if (!raw) continue;
405
+ let evt;
406
+ try {
407
+ evt = JSON.parse(raw);
408
+ } catch {
409
+ continue;
410
+ }
411
+ if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") {
412
+ const delta = evt.delta.text;
413
+ if (typeof delta === "string" && delta.length > 0) {
414
+ text += delta;
415
+ yield delta;
416
+ }
417
+ } else if (evt.type === "message_start" && evt.message?.usage?.input_tokens !== void 0) {
418
+ promptTokens = evt.message.usage.input_tokens;
419
+ } else if (evt.type === "message_delta" && evt.usage?.output_tokens !== void 0) {
420
+ completionTokens = evt.usage.output_tokens;
421
+ }
422
+ }
423
+ return {
424
+ text,
425
+ usage: promptTokens !== void 0 || completionTokens !== void 0 ? { promptTokens, completionTokens } : void 0
426
+ };
427
+ }
428
+ };
429
+
430
+ // src/server/adapters/custom.ts
431
+ var CustomHttpAdapter = class {
432
+ name;
433
+ url;
434
+ headers;
435
+ formatRequestBody;
436
+ parseResponse;
437
+ constructor(options) {
438
+ if (!options.url) {
439
+ throw new Error("CustomHttpAdapter requires a url");
440
+ }
441
+ this.url = options.url;
442
+ this.name = options.name || "custom-http";
443
+ this.headers = options.headers;
444
+ this.formatRequestBody = options.formatRequestBody || ((params) => ({
445
+ messages: params.messages && params.messages.length > 0 ? [
446
+ { role: "system", content: params.systemPrompt },
447
+ ...params.messages.map((m) => ({ role: m.role, content: m.content }))
448
+ ] : [
449
+ { role: "system", content: params.systemPrompt },
450
+ { role: "user", content: params.userPrompt }
451
+ ]
452
+ }));
453
+ this.parseResponse = options.parseResponse || ((json) => {
454
+ if (!json || typeof json !== "object") {
455
+ throw new Error("Response is not a valid JSON object");
456
+ }
457
+ const obj = json;
458
+ if (typeof obj.response === "string") return obj.response;
459
+ if (typeof obj.text === "string") return obj.text;
460
+ const choices = obj.choices;
461
+ if (Array.isArray(choices) && choices[0]?.message?.content) {
462
+ return choices[0].message.content;
463
+ }
464
+ const message = obj.message;
465
+ if (message && typeof message.content === "string") {
466
+ return message.content;
467
+ }
468
+ throw new Error("Unable to extract generated text from custom HTTP response");
469
+ });
470
+ }
471
+ async generate(params) {
472
+ const body = this.formatRequestBody(params);
473
+ const res = await fetch(this.url, {
474
+ method: "POST",
475
+ headers: {
476
+ "Content-Type": "application/json",
477
+ ...this.headers
478
+ },
479
+ body: JSON.stringify(body),
480
+ signal: params.signal
481
+ });
482
+ if (!res.ok) {
483
+ const errText = await res.text().catch(() => "");
484
+ throw new Error(
485
+ `Custom HTTP adapter error [${res.status} ${res.statusText}]: ${errText || "Unknown error"}`
486
+ );
487
+ }
488
+ const json = await res.json();
489
+ const text = this.parseResponse(json);
490
+ return { text };
491
+ }
492
+ };
493
+
494
+ export {
495
+ GeminiAdapter,
496
+ OpenAiAdapter,
497
+ AnthropicAdapter,
498
+ CustomHttpAdapter
499
+ };
500
+ //# sourceMappingURL=chunk-27AY2KBI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/adapters/stream-utils.ts","../src/server/adapters/gemini.ts","../src/server/adapters/openai.ts","../src/server/adapters/anthropic.ts","../src/server/adapters/custom.ts"],"sourcesContent":["/**\n * Shared SSE (server-sent events) frame reader for provider adapters that support\n * token-level streaming (STORA-515) — OpenAI, Anthropic and Gemini all expose their\n * streaming APIs as `data: <json>\\n\\n` frames, so every adapter reuses this single\n * low-level reader instead of re-implementing buffering/decoding three times.\n *\n * Yields the raw string payload of each `data:` line (already trimmed), in arrival\n * order. Callers are responsible for `JSON.parse`-ing (or ignoring sentinel values\n * like OpenAI's `[DONE]`) and for interpreting provider-specific event shapes.\n */\nexport async function* readSseDataLines(\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<string, void, void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n if (signal?.aborted) {\n throw new Error('Streaming aborted by client');\n }\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split('\\n\\n');\n buffer = frames.pop() || '';\n\n for (const frame of frames) {\n for (const line of frame.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.startsWith('data:')) {\n yield trimmed.slice(5).trim();\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface GeminiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n}\n\nexport class GeminiAdapter implements AiProviderAdapter {\n readonly name = 'gemini';\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n\n constructor(options: GeminiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('GeminiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gemini-2.5-flash';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl =\n options.baseUrl || 'https://generativelanguage.googleapis.com/v1beta';\n }\n\n private buildContents(\n params: AiProviderGenerateParams,\n ): Array<{ role: string; parts: Array<{ text: string }> }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages.map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: m.content }],\n }))\n : [\n {\n role: 'user',\n parts: [{ text: userPrompt }],\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, messages: chatMessages, signal } = params;\n\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n if (params.jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n generationConfig.responseMimeType = 'application/json';\n }\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n candidates?: Array<{\n content?: {\n parts?: Array<{ text?: string }>;\n };\n }>;\n usageMetadata?: {\n promptTokenCount?: number;\n candidatesTokenCount?: number;\n };\n };\n\n const candidateText = data.candidates?.[0]?.content?.parts?.[0]?.text;\n if (!candidateText) {\n throw new Error('Gemini returned an empty or invalid candidate response');\n }\n\n return {\n text: candidateText,\n usage: data.usageMetadata\n ? {\n promptTokens: data.usageMetadata.promptTokenCount,\n completionTokens: data.usageMetadata.candidatesTokenCount,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Gemini's `streamGenerateContent` SSE endpoint (STORA-515).\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/models/${encodeURIComponent(this.model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.apiKey)}`;\n const contents = this.buildContents(params);\n\n const generationConfig: Record<string, unknown> = {\n temperature: this.temperature,\n };\n\n const body: Record<string, unknown> = {\n system_instruction: {\n parts: [{ text: systemPrompt }],\n },\n contents,\n generationConfig,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Gemini API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Gemini streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n let parsed: {\n candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;\n usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const delta = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n if (parsed.usageMetadata) {\n usage = {\n promptTokens: parsed.usageMetadata.promptTokenCount,\n completionTokens: parsed.usageMetadata.candidatesTokenCount,\n };\n }\n }\n\n return { text, usage };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface OpenAiAdapterOptions {\n apiKey: string;\n model?: string;\n temperature?: number;\n baseUrl?: string;\n headers?: Record<string, string>;\n}\n\nexport class OpenAiAdapter implements AiProviderAdapter {\n readonly name = 'openai';\n private apiKey: string;\n private model: string;\n private temperature: number;\n private baseUrl: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: OpenAiAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('OpenAiAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'gpt-4o';\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.openai.com/v1').replace(/\\/+$/, '');\n this.customHeaders = options.headers;\n }\n\n private buildMessages(params: AiProviderGenerateParams): Array<{ role: string; content: string }> {\n const { systemPrompt, userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? [\n { role: 'system', content: systemPrompt },\n ...chatMessages.map((m) => ({ role: m.role, content: m.content })),\n ]\n : [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, signal } = params;\n\n const url = `${this.baseUrl}/chat/completions`;\n const messages = this.buildMessages(params);\n const chatMessages = params.messages;\n\n const body: Record<string, unknown> = {\n model: this.model,\n temperature: this.temperature,\n messages,\n };\n\n // Only force json_object when JSON schema is provided or specifically requested,\n // allowing natural conversation text in chat mode.\n if (params.jsonSchema || (!chatMessages && (systemPrompt.includes('JSON') || systemPrompt.includes('json')))) {\n body.response_format = { type: 'json_object' };\n }\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `OpenAI API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n choices?: Array<{\n message?: {\n content?: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error('OpenAI returned an empty message content');\n }\n\n return {\n text: content,\n usage: data.usage\n ? {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via OpenAI's `stream: true` chat completions mode (STORA-515).\n * Yields each `delta.content` fragment as it arrives and returns the fully assembled\n * text + usage (when the final chunk includes it) once the stream ends.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { signal } = params;\n const url = `${this.baseUrl}/chat/completions`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n temperature: this.temperature,\n messages,\n stream: true,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `OpenAI API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('OpenAI streaming response has no readable body');\n }\n\n let text = '';\n let usage: AiProviderGenerateResult['usage'];\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (raw === '[DONE]') break;\n\n let parsed: {\n choices?: Array<{ delta?: { content?: string } }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n };\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n\n const delta = parsed.choices?.[0]?.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n\n if (parsed.usage) {\n usage = {\n promptTokens: parsed.usage.prompt_tokens,\n completionTokens: parsed.usage.completion_tokens,\n };\n }\n }\n\n return { text, usage };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\nimport { readSseDataLines } from './stream-utils';\n\nexport interface AnthropicAdapterOptions {\n apiKey: string;\n model?: string;\n maxTokens?: number;\n temperature?: number;\n baseUrl?: string;\n anthropicVersion?: string;\n headers?: Record<string, string>;\n}\n\nexport class AnthropicAdapter implements AiProviderAdapter {\n readonly name = 'anthropic';\n private apiKey: string;\n private model: string;\n private maxTokens: number;\n private temperature: number;\n private baseUrl: string;\n private anthropicVersion: string;\n private customHeaders?: Record<string, string>;\n\n constructor(options: AnthropicAdapterOptions) {\n if (!options.apiKey) {\n throw new Error('AnthropicAdapter requires an apiKey');\n }\n this.apiKey = options.apiKey;\n this.model = options.model || 'claude-3-5-sonnet-20241022';\n this.maxTokens = options.maxTokens ?? 4096;\n this.temperature = options.temperature ?? 0.4;\n this.baseUrl = (options.baseUrl || 'https://api.anthropic.com/v1').replace(/\\/+$/, '');\n this.anthropicVersion = options.anthropicVersion || '2023-06-01';\n this.customHeaders = options.headers;\n }\n\n private buildMessages(\n params: AiProviderGenerateParams,\n ): Array<{ role: 'user' | 'assistant'; content: string }> {\n const { userPrompt, messages: chatMessages } = params;\n return chatMessages && chatMessages.length > 0\n ? chatMessages\n .filter((m) => m.role === 'user' || m.role === 'assistant')\n .map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content }))\n : [\n {\n role: 'user' as const,\n content: userPrompt,\n },\n ];\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const { systemPrompt, signal } = params;\n\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const data = (await res.json()) as {\n content?: Array<{\n type?: string;\n text?: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const textPart = data.content?.find((c) => c.type === 'text')?.text;\n if (!textPart) {\n throw new Error('Anthropic returned an empty text content');\n }\n\n return {\n text: textPart,\n usage: data.usage\n ? {\n promptTokens: data.usage.input_tokens,\n completionTokens: data.usage.output_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Token-level streaming via Anthropic's `stream: true` Messages API (STORA-515).\n * Parses the `content_block_delta` (`text_delta`) events for incremental text and the\n * `message_start`/`message_delta` events for usage accounting.\n */\n async *generateStream(\n params: AiProviderGenerateParams,\n ): AsyncGenerator<string, AiProviderGenerateResult, void> {\n const { systemPrompt, signal } = params;\n const url = `${this.baseUrl}/messages`;\n const messages = this.buildMessages(params);\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: this.temperature,\n system: systemPrompt,\n messages,\n stream: true,\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': this.anthropicVersion,\n ...this.customHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Anthropic API error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n if (!res.body) {\n throw new Error('Anthropic streaming response has no readable body');\n }\n\n let text = '';\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n\n for await (const raw of readSseDataLines(res.body, signal)) {\n if (!raw) continue;\n\n let evt: {\n type?: string;\n delta?: { type?: string; text?: string };\n usage?: { output_tokens?: number };\n message?: { usage?: { input_tokens?: number } };\n };\n try {\n evt = JSON.parse(raw);\n } catch {\n continue;\n }\n\n if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') {\n const delta = evt.delta.text;\n if (typeof delta === 'string' && delta.length > 0) {\n text += delta;\n yield delta;\n }\n } else if (evt.type === 'message_start' && evt.message?.usage?.input_tokens !== undefined) {\n promptTokens = evt.message.usage.input_tokens;\n } else if (evt.type === 'message_delta' && evt.usage?.output_tokens !== undefined) {\n completionTokens = evt.usage.output_tokens;\n }\n }\n\n return {\n text,\n usage:\n promptTokens !== undefined || completionTokens !== undefined\n ? { promptTokens, completionTokens }\n : undefined,\n };\n }\n}\n","import type {\n AiProviderAdapter,\n AiProviderGenerateParams,\n AiProviderGenerateResult,\n} from '../../types';\n\nexport interface CustomHttpAdapterOptions {\n url: string;\n name?: string;\n headers?: Record<string, string>;\n formatRequestBody?: (params: AiProviderGenerateParams) => unknown;\n parseResponse?: (json: unknown) => string;\n}\n\nexport class CustomHttpAdapter implements AiProviderAdapter {\n readonly name: string;\n private url: string;\n private headers?: Record<string, string>;\n private formatRequestBody: (params: AiProviderGenerateParams) => unknown;\n private parseResponse: (json: unknown) => string;\n\n constructor(options: CustomHttpAdapterOptions) {\n if (!options.url) {\n throw new Error('CustomHttpAdapter requires a url');\n }\n this.url = options.url;\n this.name = options.name || 'custom-http';\n this.headers = options.headers;\n\n // Default body formatter (OpenAI compatible or Ollama chat compatible)\n this.formatRequestBody =\n options.formatRequestBody ||\n ((params) => ({\n messages:\n params.messages && params.messages.length > 0\n ? [\n { role: 'system', content: params.systemPrompt },\n ...params.messages.map((m) => ({ role: m.role, content: m.content })),\n ]\n : [\n { role: 'system', content: params.systemPrompt },\n { role: 'user', content: params.userPrompt },\n ],\n }));\n\n // Default response parser\n this.parseResponse =\n options.parseResponse ||\n ((json: unknown) => {\n if (!json || typeof json !== 'object') {\n throw new Error('Response is not a valid JSON object');\n }\n const obj = json as Record<string, unknown>;\n if (typeof obj.response === 'string') return obj.response; // Ollama generate\n if (typeof obj.text === 'string') return obj.text;\n const choices = obj.choices as Array<{ message?: { content?: string } }>;\n if (Array.isArray(choices) && choices[0]?.message?.content) {\n return choices[0].message.content; // OpenAI format\n }\n const message = obj.message as { content?: string };\n if (message && typeof message.content === 'string') {\n return message.content; // Ollama chat format\n }\n throw new Error('Unable to extract generated text from custom HTTP response');\n });\n }\n\n async generate(params: AiProviderGenerateParams): Promise<AiProviderGenerateResult> {\n const body = this.formatRequestBody(params);\n\n const res = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(body),\n signal: params.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n throw new Error(\n `Custom HTTP adapter error [${res.status} ${res.statusText}]: ${errText || 'Unknown error'}`,\n );\n }\n\n const json = await res.json();\n const text = this.parseResponse(json);\n\n return { text };\n }\n}\n"],"mappings":";AAUA,gBAAuB,iBACrB,MACA,QACoC;AACpC,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AAEb,MAAI;AACF,WAAO,MAAM;AACX,UAAI,QAAQ,SAAS;AACnB,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AAEA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,YAAM,SAAS,OAAO,MAAM,MAAM;AAClC,eAAS,OAAO,IAAI,KAAK;AAEzB,iBAAW,SAAS,QAAQ;AAC1B,mBAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,gBAAM,UAAU,KAAK,KAAK;AAC1B,cAAI,QAAQ,WAAW,OAAO,GAAG;AAC/B,kBAAM,QAAQ,MAAM,CAAC,EAAE,KAAK;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;;;AC7BO,IAAM,gBAAN,MAAiD;AAAA,EAC7C,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,UACH,QAAQ,WAAW;AAAA,EACvB;AAAA,EAEQ,cACN,QACyD;AACzD,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,WAAO,gBAAgB,aAAa,SAAS,IACzC,aAAa,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM,EAAE,SAAS,cAAc,UAAU;AAAA,MACzC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC;AAAA,IAC7B,EAAE,IACF;AAAA,MACE;AAAA,QACE,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;AAAA,MAC9B;AAAA,IACF;AAAA,EACN;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,EAAE,cAAc,UAAU,cAAc,OAAO,IAAI;AAEzD,UAAM,MAAM,GAAG,KAAK,OAAO,WAAW,mBAAmB,KAAK,KAAK,CAAC,wBAAwB,mBAAmB,KAAK,MAAM,CAAC;AAC3H,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,mBAA4C;AAAA,MAChD,aAAa,KAAK;AAAA,IACpB;AAEA,QAAI,OAAO,cAAe,CAAC,iBAAiB,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,MAAM,IAAK;AAC5G,uBAAiB,mBAAmB;AAAA,IACtC;AAEA,UAAM,OAAgC;AAAA,MACpC,oBAAoB;AAAA,QAClB,OAAO,CAAC,EAAE,MAAM,aAAa,CAAC;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAY7B,UAAM,gBAAgB,KAAK,aAAa,CAAC,GAAG,SAAS,QAAQ,CAAC,GAAG;AACjE,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,gBACR;AAAA,QACE,cAAc,KAAK,cAAc;AAAA,QACjC,kBAAkB,KAAK,cAAc;AAAA,MACvC,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,cAAc,OAAO,IAAI;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO,WAAW,mBAAmB,KAAK,KAAK,CAAC,sCAAsC,mBAAmB,KAAK,MAAM,CAAC;AACzI,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,mBAA4C;AAAA,MAChD,aAAa,KAAK;AAAA,IACpB;AAEA,UAAM,OAAgC;AAAA,MACpC,oBAAoB;AAAA,QAClB,OAAO,CAAC,EAAE,MAAM,aAAa,CAAC;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,QAAI,OAAO;AACX,QAAI;AAEJ,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI;AAIJ,UAAI;AACF,iBAAS,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,aAAa,CAAC,GAAG,SAAS,QAAQ,CAAC,GAAG;AAC3D,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,gBAAQ;AACR,cAAM;AAAA,MACR;AAEA,UAAI,OAAO,eAAe;AACxB,gBAAQ;AAAA,UACN,cAAc,OAAO,cAAc;AAAA,UACnC,kBAAkB,OAAO,cAAc;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AACF;;;AC5KO,IAAM,gBAAN,MAAiD;AAAA,EAC7C,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA+B;AACzC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,WAAW,QAAQ,WAAW,6BAA6B,QAAQ,QAAQ,EAAE;AAClF,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEQ,cAAc,QAA4E;AAChG,UAAM,EAAE,cAAc,YAAY,UAAU,aAAa,IAAI;AAC7D,WAAO,gBAAgB,aAAa,SAAS,IACzC;AAAA,MACE,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MACxC,GAAG,aAAa,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE;AAAA,IACnE,IACA;AAAA,MACE,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,MACxC,EAAE,MAAM,QAAQ,SAAS,WAAW;AAAA,IACtC;AAAA,EACN;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,EAAE,cAAc,OAAO,IAAI;AAEjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAC1C,UAAM,eAAe,OAAO;AAE5B,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB;AAAA,IACF;AAIA,QAAI,OAAO,cAAe,CAAC,iBAAiB,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,MAAM,IAAK;AAC5G,WAAK,kBAAkB,EAAE,MAAM,cAAc;AAAA,IAC/C;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAY7B,UAAM,UAAU,KAAK,UAAU,CAAC,GAAG,SAAS;AAC5C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,QACR;AAAA,QACE,cAAc,KAAK,MAAM;AAAA,QACzB,kBAAkB,KAAK,MAAM;AAAA,MAC/B,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,qBAAqB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,QAAI,OAAO;AACX,QAAI;AAEJ,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI,QAAQ,SAAU;AAEtB,UAAI;AAIJ,UAAI;AACF,iBAAS,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,UAAU,CAAC,GAAG,OAAO;AAC1C,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,gBAAQ;AACR,cAAM;AAAA,MACR;AAEA,UAAI,OAAO,OAAO;AAChB,gBAAQ;AAAA,UACN,cAAc,OAAO,MAAM;AAAA,UAC3B,kBAAkB,OAAO,MAAM;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AACF;;;ACxKO,IAAM,mBAAN,MAAoD;AAAA,EAChD,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAkC;AAC5C,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,WAAW,QAAQ,WAAW,gCAAgC,QAAQ,QAAQ,EAAE;AACrF,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEQ,cACN,QACwD;AACxD,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,WAAO,gBAAgB,aAAa,SAAS,IACzC,aACG,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,WAAW,EACzD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAA8B,SAAS,EAAE,QAAQ,EAAE,IAC5E;AAAA,MACE;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACN;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,EAAE,cAAc,OAAO,IAAI;AAEjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,qBAAqB,KAAK;AAAA,QAC1B,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,wBAAwB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACtF;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAW7B,UAAM,WAAW,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,GAAG;AAC/D,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK,QACR;AAAA,QACE,cAAc,KAAK,MAAM;AAAA,QACzB,kBAAkB,KAAK,MAAM;AAAA,MAC/B,IACA;AAAA,IACN;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,eACL,QACwD;AACxD,UAAM,EAAE,cAAc,OAAO,IAAI;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,UAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,UAAM,OAAgC;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,qBAAqB,KAAK;AAAA,QAC1B,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,wBAAwB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MACtF;AAAA,IACF;AAEA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAEA,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AAEJ,qBAAiB,OAAO,iBAAiB,IAAI,MAAM,MAAM,GAAG;AAC1D,UAAI,CAAC,IAAK;AAEV,UAAI;AAMJ,UAAI;AACF,cAAM,KAAK,MAAM,GAAG;AAAA,MACtB,QAAQ;AACN;AAAA,MACF;AAEA,UAAI,IAAI,SAAS,yBAAyB,IAAI,OAAO,SAAS,cAAc;AAC1E,cAAM,QAAQ,IAAI,MAAM;AACxB,YAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,kBAAQ;AACR,gBAAM;AAAA,QACR;AAAA,MACF,WAAW,IAAI,SAAS,mBAAmB,IAAI,SAAS,OAAO,iBAAiB,QAAW;AACzF,uBAAe,IAAI,QAAQ,MAAM;AAAA,MACnC,WAAW,IAAI,SAAS,mBAAmB,IAAI,OAAO,kBAAkB,QAAW;AACjF,2BAAmB,IAAI,MAAM;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,OACE,iBAAiB,UAAa,qBAAqB,SAC/C,EAAE,cAAc,iBAAiB,IACjC;AAAA,IACR;AAAA,EACF;AACF;;;AC1LO,IAAM,oBAAN,MAAqD;AAAA,EACjD;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAmC;AAC7C,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,SAAK,MAAM,QAAQ;AACnB,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,UAAU,QAAQ;AAGvB,SAAK,oBACH,QAAQ,sBACP,CAAC,YAAY;AAAA,MACZ,UACE,OAAO,YAAY,OAAO,SAAS,SAAS,IACxC;AAAA,QACE,EAAE,MAAM,UAAU,SAAS,OAAO,aAAa;AAAA,QAC/C,GAAG,OAAO,SAAS,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE;AAAA,MACtE,IACA;AAAA,QACE,EAAE,MAAM,UAAU,SAAS,OAAO,aAAa;AAAA,QAC/C,EAAE,MAAM,QAAQ,SAAS,OAAO,WAAW;AAAA,MAC7C;AAAA,IACR;AAGF,SAAK,gBACH,QAAQ,kBACP,CAAC,SAAkB;AAClB,UAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACvD;AACA,YAAM,MAAM;AACZ,UAAI,OAAO,IAAI,aAAa,SAAU,QAAO,IAAI;AACjD,UAAI,OAAO,IAAI,SAAS,SAAU,QAAO,IAAI;AAC7C,YAAM,UAAU,IAAI;AACpB,UAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,GAAG,SAAS,SAAS;AAC1D,eAAO,QAAQ,CAAC,EAAE,QAAQ;AAAA,MAC5B;AACA,YAAM,UAAU,IAAI;AACpB,UAAI,WAAW,OAAO,QAAQ,YAAY,UAAU;AAClD,eAAO,QAAQ;AAAA,MACjB;AACA,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AAAA,EACJ;AAAA,EAEA,MAAM,SAAS,QAAqE;AAClF,UAAM,OAAO,KAAK,kBAAkB,MAAM;AAE1C,UAAM,MAAM,MAAM,MAAM,KAAK,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG,KAAK;AAAA,MACV;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,OAAO;AAAA,IACjB,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC/C,YAAM,IAAI;AAAA,QACR,8BAA8B,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,WAAW,eAAe;AAAA,MAC5F;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,OAAO,KAAK,cAAc,IAAI;AAEpC,WAAO,EAAE,KAAK;AAAA,EAChB;AACF;","names":[]}