@templatical/ai-kit 0.51.0 → 0.52.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.
- package/dist/adapters/anthropic.d.ts +1 -3
- package/dist/adapters/anthropic.js +2 -3
- package/dist/adapters/openai.d.ts +1 -3
- package/dist/adapters/openai.js +2 -3
- package/dist/adapters/vercel.d.ts +1 -3
- package/dist/adapters/vercel.js +2 -3
- package/dist/{checks-DvLD3892.js → checks-B-TtaywP.js} +12 -22
- package/dist/{checks-DvLD3892.js.map → checks-B-TtaywP.js.map} +1 -1
- package/dist/client/index.d.ts +2 -6
- package/dist/client/index.js +2 -2
- package/dist/{errors-DWshWx18.js → errors-BtORdtK2.js} +3 -4
- package/dist/{errors-DWshWx18.js.map → errors-BtORdtK2.js.map} +1 -1
- package/dist/{http-provider-Cb41NXd5.d.ts → http-provider-B5riig95.d.ts} +1 -3
- package/dist/{http-provider-Cx2ZcLRB.js → http-provider-DLJYM4l8.js} +5 -7
- package/dist/{http-provider-Cx2ZcLRB.js.map → http-provider-DLJYM4l8.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{model-xJvGTfLB.d.ts → model-VqqOlLS2.d.ts} +1 -5
- package/dist/server/index.d.ts +2 -24
- package/dist/server/index.js +5 -8
- package/dist/server/index.js.map +1 -1
- package/dist/{shared-BuhccY6c.js → shared-DOEVSpBX.js} +4 -5
- package/dist/{shared-BuhccY6c.js.map → shared-DOEVSpBX.js.map} +1 -1
- package/dist/testing/index.d.ts +3 -15
- package/dist/testing/index.js +20 -35
- package/dist/testing/index.js.map +1 -1
- package/dist/{wire-Cv7Fk-7n.d.ts → wire-BJ4-EWvR.d.ts} +1 -3
- package/dist/{wire-B6AF_UGT.js → wire-cYO4W7GH.js} +2 -3
- package/dist/{wire-B6AF_UGT.js.map → wire-cYO4W7GH.js.map} +1 -1
- package/package.json +6 -6
package/dist/client/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as HttpAiProviderOptions, r as createHttpAiProvider, t as HttpAiFeatures } from "../http-provider-
|
|
2
|
-
import { C as AiWireEvent, p as AiErrorEvent } from "../wire-
|
|
1
|
+
import { n as HttpAiProviderOptions, r as createHttpAiProvider, t as HttpAiFeatures } from "../http-provider-B5riig95.js";
|
|
2
|
+
import { C as AiWireEvent, p as AiErrorEvent } from "../wire-BJ4-EWvR.js";
|
|
3
3
|
import { SdkError } from "@templatical/types";
|
|
4
|
-
//#region src/client/sse.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* The wire's events from an SSE body: `data:` lines, joined per event and
|
|
7
6
|
* dispatched at a blank line or the end; comments (`: ping`), other fields,
|
|
@@ -9,8 +8,6 @@ import { SdkError } from "@templatical/types";
|
|
|
9
8
|
* cancels the body.
|
|
10
9
|
*/
|
|
11
10
|
export declare function readSse(body: ReadableStream<Uint8Array>): AsyncGenerator<AiWireEvent>;
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/client/errors.d.ts
|
|
14
11
|
/** Shown when the editor refused an `operations` event (§4). */
|
|
15
12
|
export declare const AI_TEMPLATE_CHANGED_MESSAGE = "The template changed while the AI was working.";
|
|
16
13
|
/** `Retry-After` (seconds or an HTTP date) in milliseconds; a second when unreadable. */
|
|
@@ -19,6 +16,5 @@ export declare function parseRetryAfter(header: string | null, now?: number): nu
|
|
|
19
16
|
export declare function errorFromResponse(response: Response): Promise<SdkError>;
|
|
20
17
|
/** A stream's `error` event, with the status its code has before a stream (S7 D2). */
|
|
21
18
|
export declare function errorFromEvent(event: AiErrorEvent): SdkError;
|
|
22
|
-
//#endregion
|
|
23
19
|
export { type HttpAiFeatures, type HttpAiProviderOptions, createHttpAiProvider };
|
|
24
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/client/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as parseRetryAfter, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "../errors-
|
|
2
|
-
import { r as readSse, t as createHttpAiProvider } from "../http-provider-
|
|
1
|
+
import { i as parseRetryAfter, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "../errors-BtORdtK2.js";
|
|
2
|
+
import { r as readSse, t as createHttpAiProvider } from "../http-provider-DLJYM4l8.js";
|
|
3
3
|
export { AI_TEMPLATE_CHANGED_MESSAGE, createHttpAiProvider, errorFromEvent, errorFromResponse, parseRetryAfter, readSse };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as AI_ERROR_STATUS, t as AI_ERROR_MESSAGES } from "./wire-
|
|
1
|
+
import { n as AI_ERROR_STATUS, t as AI_ERROR_MESSAGES } from "./wire-cYO4W7GH.js";
|
|
2
2
|
import { CollaborationConflict, SdkError, ThrottledError } from "@templatical/types";
|
|
3
|
-
|
|
3
|
+
// src/client/errors.ts
|
|
4
4
|
/** Shown when the editor refused an `operations` event (§4). */
|
|
5
5
|
const AI_TEMPLATE_CHANGED_MESSAGE = "The template changed while the AI was working.";
|
|
6
6
|
const CONFLICT_CODES = /* @__PURE__ */ new Set([
|
|
@@ -49,7 +49,6 @@ function errorFromEvent(event) {
|
|
|
49
49
|
function providerFailure() {
|
|
50
50
|
return new SdkError(AI_ERROR_MESSAGES.ai_provider_error, 502, "ai_provider_error");
|
|
51
51
|
}
|
|
52
|
-
//#endregion
|
|
53
52
|
export { providerFailure as a, parseRetryAfter as i, errorFromEvent as n, errorFromResponse as r, AI_TEMPLATE_CHANGED_MESSAGE as t };
|
|
54
53
|
|
|
55
|
-
//# sourceMappingURL=errors-
|
|
54
|
+
//# sourceMappingURL=errors-BtORdtK2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-
|
|
1
|
+
{"version":3,"file":"errors-BtORdtK2.js","names":[],"sources":["../src/client/errors.ts"],"sourcesContent":["import type { CollaborationConflictCode } from \"@templatical/types\";\nimport {\n CollaborationConflict,\n SdkError,\n ThrottledError,\n} from \"@templatical/types\";\nimport { AI_ERROR_MESSAGES, AI_ERROR_STATUS, type AiErrorEvent } from \"../wire\";\n\n/** Shown when the editor refused an `operations` event (§4). */\nexport const AI_TEMPLATE_CHANGED_MESSAGE =\n \"The template changed while the AI was working.\";\n\nconst CONFLICT_CODES: ReadonlySet<string> = new Set([\n \"block_exists\",\n \"block_missing\",\n \"anchor_missing\",\n \"precondition_failed\",\n \"lock_held\",\n \"locked_by_others\",\n \"invalid_operation\",\n]);\n\nconst DEFAULT_RETRY_AFTER_MS = 1000;\n\n/** `Retry-After` (seconds or an HTTP date) in milliseconds; a second when unreadable. */\nexport function parseRetryAfter(\n header: string | null,\n now = Date.now(),\n): number {\n if (header === null || header.trim() === \"\") return DEFAULT_RETRY_AFTER_MS;\n const seconds = Number(header);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n return Number.isNaN(date) ? DEFAULT_RETRY_AFTER_MS : Math.max(0, date - now);\n}\n\nfunction build(\n status: number | undefined,\n body: Record<string, unknown>,\n retryAfterMs: number,\n): SdkError {\n const {\n message: rawMessage,\n errors,\n code: rawCode,\n opIndex,\n holder,\n holders,\n type: _type,\n ...details\n } = body;\n const firstValidation =\n typeof errors === \"object\" && errors !== null\n ? (\n Object.values(errors as Record<string, unknown>)[0] as\n unknown[] | undefined\n )?.[0]\n : undefined;\n const message =\n typeof firstValidation === \"string\"\n ? firstValidation\n : typeof rawMessage === \"string\" && rawMessage !== \"\"\n ? rawMessage\n : AI_ERROR_MESSAGES.ai_provider_error;\n const code = typeof rawCode === \"string\" ? rawCode : undefined;\n if (status === 429 || code === \"rate_limited\") {\n return new ThrottledError(\n message,\n retryAfterMs,\n code ?? \"rate_limited\",\n details,\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new CollaborationConflict(message, {\n code: code as CollaborationConflictCode,\n ...(status !== undefined ? { status } : {}),\n ...(typeof opIndex === \"number\" ? { opIndex } : {}),\n ...(holder ? { holder: holder as never } : {}),\n ...(Array.isArray(holders) ? { holders: holders as never } : {}),\n });\n }\n return new SdkError(message, status, code, details);\n}\n\n/** A refusal before the stream: `{ message, code, …details }` with its status. */\nexport async function errorFromResponse(response: Response): Promise<SdkError> {\n const body = (await response.json().catch(() => ({}))) as unknown;\n return build(\n response.status,\n typeof body === \"object\" && body !== null\n ? (body as Record<string, unknown>)\n : {},\n parseRetryAfter(response.headers.get(\"Retry-After\")),\n );\n}\n\n/** A stream's `error` event, with the status its code has before a stream (S7 D2). */\nexport function errorFromEvent(event: AiErrorEvent): SdkError {\n return build(AI_ERROR_STATUS[event.code], event, DEFAULT_RETRY_AFTER_MS);\n}\n\n/** The provider's failure: a cut stream, a network error, a malformed answer. */\nexport function providerFailure(): SdkError {\n return new SdkError(\n AI_ERROR_MESSAGES.ai_provider_error,\n 502,\n \"ai_provider_error\",\n );\n}\n"],"mappings":";;;;AASA,MAAa,8BACX;AAEF,MAAM,iCAAsC,IAAI,IAAI;CAClD;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,yBAAyB;;AAG/B,SAAgB,gBACd,QACA,MAAM,KAAK,IAAI,GACP;CACR,IAAI,WAAW,QAAQ,OAAO,KAAK,MAAM,IAAI,OAAO;CACpD,MAAM,UAAU,OAAO,MAAM;CAC7B,IAAI,OAAO,SAAS,OAAO,GAAG,OAAO,KAAK,IAAI,GAAG,UAAU,GAAI;CAC/D,MAAM,OAAO,KAAK,MAAM,MAAM;CAC9B,OAAO,OAAO,MAAM,IAAI,IAAI,yBAAyB,KAAK,IAAI,GAAG,OAAO,GAAG;AAC7E;AAEA,SAAS,MACP,QACA,MACA,cACU;CACV,MAAM,EACJ,SAAS,YACT,QACA,MAAM,SACN,SACA,QACA,SACA,MAAM,OACN,GAAG,YACD;CACJ,MAAM,kBACJ,OAAO,WAAW,YAAY,WAAW,OAEnC,OAAO,OAAO,MAAiC,CAAC,CAAC,EAAE,GAEjD,KACJ,KAAA;CACN,MAAM,UACJ,OAAO,oBAAoB,WACvB,kBACA,OAAO,eAAe,YAAY,eAAe,KAC/C,aACA,kBAAkB;CAC1B,MAAM,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;CACrD,IAAI,WAAW,OAAO,SAAS,gBAC7B,OAAO,IAAI,eACT,SACA,cACA,QAAQ,gBACR,OACF;CAEF,IAAI,SAAS,KAAA,KAAa,eAAe,IAAI,IAAI,GAC/C,OAAO,IAAI,sBAAsB,SAAS;EAClC;EACN,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EACzC,GAAI,OAAO,YAAY,WAAW,EAAE,QAAQ,IAAI,CAAC;EACjD,GAAI,SAAS,EAAU,OAAgB,IAAI,CAAC;EAC5C,GAAI,MAAM,QAAQ,OAAO,IAAI,EAAW,QAAiB,IAAI,CAAC;CAChE,CAAC;CAEH,OAAO,IAAI,SAAS,SAAS,QAAQ,MAAM,OAAO;AACpD;;AAGA,eAAsB,kBAAkB,UAAuC;CAC7E,MAAM,OAAQ,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;CACpD,OAAO,MACL,SAAS,QACT,OAAO,SAAS,YAAY,SAAS,OAChC,OACD,CAAC,GACL,gBAAgB,SAAS,QAAQ,IAAI,aAAa,CAAC,CACrD;AACF;;AAGA,SAAgB,eAAe,OAA+B;CAC5D,OAAO,MAAM,gBAAgB,MAAM,OAAO,OAAO,sBAAsB;AACzE;;AAGA,SAAgB,kBAA4B;CAC1C,OAAO,IAAI,SACT,kBAAkB,mBAClB,KACA,mBACF;AACF"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AiFeatureName, AiProvider } from "@templatical/types";
|
|
2
|
-
//#region src/client/http-provider.d.ts
|
|
3
2
|
type HttpAiFeatures = Partial<Record<AiFeatureName | "conversation", boolean>>;
|
|
4
3
|
interface HttpAiProviderOptions {
|
|
5
4
|
/** Where the wire's routes live: `{baseUrl}/templates/{t}/ai/…`. */
|
|
@@ -17,6 +16,5 @@ interface HttpAiProviderOptions {
|
|
|
17
16
|
* Errors are `SdkError`s; stopping a call rejects with an `AbortError`.
|
|
18
17
|
*/
|
|
19
18
|
declare function createHttpAiProvider(options: HttpAiProviderOptions): AiProvider;
|
|
20
|
-
//#endregion
|
|
21
19
|
export { HttpAiProviderOptions as n, createHttpAiProvider as r, HttpAiFeatures as t };
|
|
22
|
-
//# sourceMappingURL=http-provider-
|
|
20
|
+
//# sourceMappingURL=http-provider-B5riig95.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as AI_ROUTES, l as aiRoutePath, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER } from "./wire-
|
|
2
|
-
import { a as providerFailure, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "./errors-
|
|
1
|
+
import { a as AI_ROUTES, l as aiRoutePath, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER } from "./wire-cYO4W7GH.js";
|
|
2
|
+
import { a as providerFailure, n as errorFromEvent, r as errorFromResponse, t as AI_TEMPLATE_CHANGED_MESSAGE } from "./errors-BtORdtK2.js";
|
|
3
3
|
import { CollaborationConflict, SdkError } from "@templatical/types";
|
|
4
|
-
|
|
4
|
+
// src/client/sse.ts
|
|
5
5
|
const TYPES = /* @__PURE__ */ new Set([
|
|
6
6
|
"text",
|
|
7
7
|
"operations",
|
|
@@ -59,8 +59,7 @@ async function* readSse(body) {
|
|
|
59
59
|
reader.cancel().catch(() => {});
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
//#region src/client/http-provider.ts
|
|
62
|
+
// src/client/http-provider.ts
|
|
64
63
|
function abortError(signal) {
|
|
65
64
|
const reason = signal.reason;
|
|
66
65
|
return reason instanceof Error && reason.name === "AbortError" ? reason : new DOMException("The AI request was stopped.", "AbortError");
|
|
@@ -272,7 +271,6 @@ function createHttpAiProviderWithFetch(options, fetchRequest) {
|
|
|
272
271
|
} } : {}
|
|
273
272
|
};
|
|
274
273
|
}
|
|
275
|
-
//#endregion
|
|
276
274
|
export { createHttpAiProviderWithFetch as n, readSse as r, createHttpAiProvider as t };
|
|
277
275
|
|
|
278
|
-
//# sourceMappingURL=http-provider-
|
|
276
|
+
//# sourceMappingURL=http-provider-DLJYM4l8.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-provider-Cx2ZcLRB.js","names":[],"sources":["../src/client/sse.ts","../src/client/http-provider.ts"],"sourcesContent":["import type { AiWireEvent } from \"../wire\";\n\nconst TYPES = new Set([\"text\", \"operations\", \"applied\", \"done\", \"error\"]);\n\nfunction parse(data: string): AiWireEvent | null {\n try {\n const event = JSON.parse(data) as { type?: unknown };\n return typeof event === \"object\" &&\n event !== null &&\n typeof event.type === \"string\" &&\n TYPES.has(event.type)\n ? (event as AiWireEvent)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The wire's events from an SSE body: `data:` lines, joined per event and\n * dispatched at a blank line or the end; comments (`: ping`), other fields,\n * unparseable data and unknown types are skipped. Cancelling the generator\n * cancels the body.\n */\nexport async function* readSse(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<AiWireEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let data: string[] = [];\n\n function* dispatch(): Generator<AiWireEvent> {\n if (data.length === 0) return;\n const event = parse(data.join(\"\\n\"));\n data = [];\n if (event) yield event;\n }\n\n function* lines(text: string): Generator<AiWireEvent> {\n for (const raw of text.split(\"\\n\")) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n if (line === \"\") {\n yield* dispatch();\n } else if (line.startsWith(\"data:\")) {\n data.push(line.slice(line.startsWith(\"data: \") ? 6 : 5));\n }\n }\n }\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const end = buffer.lastIndexOf(\"\\n\");\n if (end === -1) continue;\n const complete = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n yield* lines(complete);\n }\n buffer += decoder.decode();\n if (buffer !== \"\") yield* lines(buffer);\n yield* dispatch();\n } finally {\n reader.cancel().catch(() => {});\n }\n}\n","import type {\n AiFeatureName,\n AiProvider,\n AiReplay,\n AiReplayOutcome,\n AiRequestBase,\n AiTurnHandlers,\n AiTurnResult,\n} from \"@templatical/types\";\nimport { CollaborationConflict, SdkError } from \"@templatical/types\";\nimport {\n AI_GENERATE_FIELDS,\n AI_ROUTES,\n AI_SESSION_HEADER,\n AI_WIRE_HEADER,\n AI_WIRE_VERSION,\n aiRoutePath,\n type AiDoneEvent,\n type AiRouteName,\n type AiWireEvent,\n} from \"../wire\";\nimport {\n AI_TEMPLATE_CHANGED_MESSAGE,\n errorFromEvent,\n errorFromResponse,\n providerFailure,\n} from \"./errors\";\nimport { readSse } from \"./sse\";\n\nexport type HttpAiFeatures = Partial<\n Record<AiFeatureName | \"conversation\", boolean>\n>;\n\nexport interface HttpAiProviderOptions {\n /** Where the wire's routes live: `{baseUrl}/templates/{t}/ai/…`. */\n baseUrl: string;\n /** Your auth headers, or a function (async for a token refresh) called per request. */\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n /** `fetch`'s `credentials`, for cookie auth. */\n credentials?: RequestCredentials;\n /** `false` turns a feature off, `conversation` included. Everything else is on. */\n features?: HttpAiFeatures;\n}\n\nfunction abortError(signal: AbortSignal): Error {\n const reason = signal.reason as unknown;\n return reason instanceof Error && reason.name === \"AbortError\"\n ? reason\n : new DOMException(\"The AI request was stopped.\", \"AbortError\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * An `AiProvider` over the wire (A1 §4): JSON or multipart requests, SSE\n * answers for turns and rewrite, JSON for history.\n * Errors are `SdkError`s; stopping a call rejects with an `AbortError`.\n */\nexport function createHttpAiProvider(\n options: HttpAiProviderOptions,\n): AiProvider {\n return createHttpAiProviderWithFetch(options, (input, init) =>\n fetch(input, init),\n );\n}\n\n/** @internal A per-instance transport for wire conformance; not a package export. */\nexport function createHttpAiProviderWithFetch(\n options: HttpAiProviderOptions,\n fetchRequest: typeof fetch,\n): AiProvider {\n const base = options.baseUrl.replace(/\\/+$/, \"\");\n const on = (feature: AiFeatureName | \"conversation\") =>\n options.features?.[feature] !== false;\n\n async function request(\n route: AiRouteName,\n params: { template: string; message?: string; replayId?: string },\n init: {\n json?: unknown;\n form?: FormData;\n session?: string;\n stream?: boolean;\n signal?: AbortSignal;\n },\n ): Promise<Response> {\n init.signal?.throwIfAborted();\n const own =\n typeof options.headers === \"function\"\n ? await options.headers()\n : (options.headers ?? {});\n const headers = new Headers(own);\n headers.set(AI_WIRE_HEADER, AI_WIRE_VERSION);\n headers.set(\n \"Accept\",\n init.stream ? \"text/event-stream\" : \"application/json\",\n );\n if (init.session) headers.set(AI_SESSION_HEADER, init.session);\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.json !== undefined) {\n headers.set(\"Content-Type\", \"application/json\");\n body = JSON.stringify(init.json);\n }\n let response: Response;\n try {\n init.signal?.throwIfAborted();\n response = await fetchRequest(`${base}${aiRoutePath(route, params)}`, {\n method: AI_ROUTES[route].method,\n headers,\n body,\n ...(options.credentials ? { credentials: options.credentials } : {}),\n signal: init.signal,\n });\n } catch (error) {\n if (init.signal?.aborted) throw abortError(init.signal);\n throw error instanceof SdkError ? error : providerFailure();\n }\n if (!response.ok) throw await errorFromResponse(response);\n return response;\n }\n\n /**\n * One streamed call. `each` handles every event before `done`; it may\n * throw to end the call (a refusal, the editor's own error), which aborts\n * the request.\n */\n async function streamed<T>(\n route: AiRouteName,\n template: string,\n init: { json?: unknown; form?: FormData; session?: string },\n signal: AbortSignal,\n // Never called with `done` or `error`: those end the call first.\n each: (event: AiWireEvent) => Promise<void> | void,\n done: (event: AiDoneEvent) => T | undefined,\n ): Promise<T> {\n const controller = new AbortController();\n const stop = () => controller.abort(signal.reason);\n if (signal.aborted) stop();\n signal.addEventListener(\"abort\", stop, { once: true });\n try {\n const response = await request(\n route,\n { template },\n {\n ...init,\n stream: true,\n signal: controller.signal,\n },\n );\n if (!response.body) throw providerFailure();\n for await (const event of readSse(response.body)) {\n if (signal.aborted) {\n // Already received server edits still need reconciliation after Stop.\n if (event.type === \"applied\") await each(event);\n if (event.type === \"done\" || event.type === \"error\")\n throw abortError(signal);\n continue;\n }\n if (event.type === \"error\") throw errorFromEvent(event);\n if (event.type === \"done\") {\n const value = done(event);\n if (value === undefined) throw providerFailure();\n return value;\n }\n await each(event);\n }\n throw providerFailure();\n } catch (error) {\n if (signal.aborted) throw abortError(signal);\n controller.abort();\n if (error instanceof SdkError) throw error;\n if (error instanceof Error && error.name === \"AbortError\") throw error;\n if (error instanceof Error && !(error instanceof TypeError)) throw error;\n throw providerFailure();\n } finally {\n signal.removeEventListener(\"abort\", stop);\n }\n }\n\n function turn(\n route: \"generate\" | \"fixFinding\",\n req: AiRequestBase,\n init: { json?: unknown; form?: FormData },\n handlers: AiTurnHandlers,\n signal: AbortSignal,\n ): Promise<AiTurnResult> {\n let text = \"\";\n return streamed(\n route,\n req.templateId,\n { ...init, session: req.sessionId },\n signal,\n async (event) => {\n if (event.type === \"text\") {\n text += event.text;\n handlers.onText(event.text);\n } else if (event.type === \"operations\") {\n const result = await handlers.onOperations(event.ops);\n if (!result.applied) {\n // SSE is one-way: the backend can't be told mid-turn, so the\n // request ends here with the operation's own code (§4).\n throw new CollaborationConflict(AI_TEMPLATE_CHANGED_MESSAGE, {\n code: result.code,\n opIndex: result.opIndex,\n });\n }\n } else if (event.type === \"applied\") {\n handlers.onApplied({\n batchId: event.batchId,\n revision: event.revision,\n operationCount: event.operationCount,\n });\n }\n },\n (done) =>\n typeof done.messageId === \"string\" || done.messageId === null\n ? { messageId: done.messageId, text }\n : undefined,\n );\n }\n\n async function json<T>(response: Response): Promise<T> {\n try {\n return (await response.json()) as T;\n } catch {\n throw providerFailure();\n }\n }\n\n async function replay(\n route: \"undoMessage\" | \"redoMessage\",\n templateId: string,\n messageId: string,\n sessionId: string,\n ): Promise<AiReplay> {\n const response = await request(\n route,\n { template: templateId, message: messageId },\n { session: sessionId },\n );\n const body = await json<{\n data?: {\n operations?: unknown;\n replayId?: unknown;\n batchId?: unknown;\n revision?: unknown;\n };\n }>(response);\n if (!isRecord(body)) throw providerFailure();\n const data = body.data ?? {};\n if (\n Array.isArray(data.operations) &&\n typeof data.replayId === \"string\" &&\n data.replayId !== \"\"\n )\n return { operations: data.operations, replayId: data.replayId };\n if (typeof data.batchId === \"string\" && typeof data.revision === \"number\") {\n return { applied: { batchId: data.batchId, revision: data.revision } };\n }\n throw providerFailure();\n }\n\n return {\n chatAttachments: on(\"chat\") && on(\"chatAttachments\"),\n chat: on(\"chat\")\n ? (req, handlers, signal) => {\n const body = {\n message: req.message,\n selectedBlockId: req.selectedBlockId,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n };\n if (req.attachments.length === 0)\n return turn(\"generate\", req, { json: body }, handlers, signal);\n const form = new FormData();\n form.set(AI_GENERATE_FIELDS.message, req.message);\n if (req.selectedBlockId !== null)\n form.set(AI_GENERATE_FIELDS.selectedBlockId, req.selectedBlockId);\n form.set(AI_GENERATE_FIELDS.mergeTags, JSON.stringify(req.mergeTags));\n form.set(AI_GENERATE_FIELDS.content, JSON.stringify(req.content));\n if (req.revision !== null)\n form.set(AI_GENERATE_FIELDS.revision, String(req.revision));\n for (const attachment of req.attachments)\n form.append(\n AI_GENERATE_FIELDS.attachment,\n attachment.file,\n attachment.name,\n );\n return turn(\"generate\", req, { form }, handlers, signal);\n }\n : false,\n fixFinding: on(\"fixFinding\")\n ? (req, handlers, signal) =>\n turn(\n \"fixFinding\",\n req,\n {\n json: {\n finding: req.finding,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n },\n },\n handlers,\n signal,\n )\n : false,\n rewrite: on(\"rewrite\")\n ? (req, handlers, signal) =>\n streamed(\n \"rewriteText\",\n req.templateId,\n {\n json: {\n html: req.html,\n preset: req.preset,\n language: req.language,\n instruction: req.instruction,\n mergeTags: req.mergeTags,\n },\n },\n signal,\n (event) => {\n if (event.type === \"text\") handlers.onText(event.text);\n },\n (done) =>\n typeof done.html === \"string\" ? { html: done.html } : undefined,\n )\n : false,\n altText: on(\"altText\")\n ? (req, signal) => {\n const { templateId, sessionId, ...body } = req;\n return streamed(\n \"altText\",\n templateId,\n { json: body, session: sessionId },\n signal,\n () => {},\n (done) =>\n typeof done.alt === \"string\" ? { alt: done.alt } : undefined,\n );\n }\n : false,\n ...(on(\"conversation\")\n ? {\n conversation: {\n settleReplay: async (\n templateId: string,\n messageId: string,\n outcome: AiReplayOutcome,\n ) => {\n await request(\n \"settleReplay\",\n {\n template: templateId,\n message: messageId,\n replayId: outcome.replayId,\n },\n {\n session: outcome.sessionId,\n json: {\n direction: outcome.direction,\n applied: outcome.applied,\n },\n },\n );\n },\n list: async (templateId: string) => {\n const response = await request(\n \"conversationMessages\",\n { template: templateId },\n {},\n );\n const body = await json<{ data?: unknown }>(response);\n if (!isRecord(body)) throw providerFailure();\n if (!Array.isArray(body.data)) throw providerFailure();\n return body.data;\n },\n clear: async (templateId: string) => {\n await request(\"clearConversation\", { template: templateId }, {});\n },\n undo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"undoMessage\", templateId, messageId, context.sessionId),\n redo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"redoMessage\", templateId, messageId, context.sessionId),\n },\n }\n : {}),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http-provider-DLJYM4l8.js","names":[],"sources":["../src/client/sse.ts","../src/client/http-provider.ts"],"sourcesContent":["import type { AiWireEvent } from \"../wire\";\n\nconst TYPES = new Set([\"text\", \"operations\", \"applied\", \"done\", \"error\"]);\n\nfunction parse(data: string): AiWireEvent | null {\n try {\n const event = JSON.parse(data) as { type?: unknown };\n return typeof event === \"object\" &&\n event !== null &&\n typeof event.type === \"string\" &&\n TYPES.has(event.type)\n ? (event as AiWireEvent)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The wire's events from an SSE body: `data:` lines, joined per event and\n * dispatched at a blank line or the end; comments (`: ping`), other fields,\n * unparseable data and unknown types are skipped. Cancelling the generator\n * cancels the body.\n */\nexport async function* readSse(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<AiWireEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let data: string[] = [];\n\n function* dispatch(): Generator<AiWireEvent> {\n if (data.length === 0) return;\n const event = parse(data.join(\"\\n\"));\n data = [];\n if (event) yield event;\n }\n\n function* lines(text: string): Generator<AiWireEvent> {\n for (const raw of text.split(\"\\n\")) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n if (line === \"\") {\n yield* dispatch();\n } else if (line.startsWith(\"data:\")) {\n data.push(line.slice(line.startsWith(\"data: \") ? 6 : 5));\n }\n }\n }\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const end = buffer.lastIndexOf(\"\\n\");\n if (end === -1) continue;\n const complete = buffer.slice(0, end);\n buffer = buffer.slice(end + 1);\n yield* lines(complete);\n }\n buffer += decoder.decode();\n if (buffer !== \"\") yield* lines(buffer);\n yield* dispatch();\n } finally {\n reader.cancel().catch(() => {});\n }\n}\n","import type {\n AiFeatureName,\n AiProvider,\n AiReplay,\n AiReplayOutcome,\n AiRequestBase,\n AiTurnHandlers,\n AiTurnResult,\n} from \"@templatical/types\";\nimport { CollaborationConflict, SdkError } from \"@templatical/types\";\nimport {\n AI_GENERATE_FIELDS,\n AI_ROUTES,\n AI_SESSION_HEADER,\n AI_WIRE_HEADER,\n AI_WIRE_VERSION,\n aiRoutePath,\n type AiDoneEvent,\n type AiRouteName,\n type AiWireEvent,\n} from \"../wire\";\nimport {\n AI_TEMPLATE_CHANGED_MESSAGE,\n errorFromEvent,\n errorFromResponse,\n providerFailure,\n} from \"./errors\";\nimport { readSse } from \"./sse\";\n\nexport type HttpAiFeatures = Partial<\n Record<AiFeatureName | \"conversation\", boolean>\n>;\n\nexport interface HttpAiProviderOptions {\n /** Where the wire's routes live: `{baseUrl}/templates/{t}/ai/…`. */\n baseUrl: string;\n /** Your auth headers, or a function (async for a token refresh) called per request. */\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n /** `fetch`'s `credentials`, for cookie auth. */\n credentials?: RequestCredentials;\n /** `false` turns a feature off, `conversation` included. Everything else is on. */\n features?: HttpAiFeatures;\n}\n\nfunction abortError(signal: AbortSignal): Error {\n const reason = signal.reason as unknown;\n return reason instanceof Error && reason.name === \"AbortError\"\n ? reason\n : new DOMException(\"The AI request was stopped.\", \"AbortError\");\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * An `AiProvider` over the wire (A1 §4): JSON or multipart requests, SSE\n * answers for turns and rewrite, JSON for history.\n * Errors are `SdkError`s; stopping a call rejects with an `AbortError`.\n */\nexport function createHttpAiProvider(\n options: HttpAiProviderOptions,\n): AiProvider {\n return createHttpAiProviderWithFetch(options, (input, init) =>\n fetch(input, init),\n );\n}\n\n/** @internal A per-instance transport for wire conformance; not a package export. */\nexport function createHttpAiProviderWithFetch(\n options: HttpAiProviderOptions,\n fetchRequest: typeof fetch,\n): AiProvider {\n const base = options.baseUrl.replace(/\\/+$/, \"\");\n const on = (feature: AiFeatureName | \"conversation\") =>\n options.features?.[feature] !== false;\n\n async function request(\n route: AiRouteName,\n params: { template: string; message?: string; replayId?: string },\n init: {\n json?: unknown;\n form?: FormData;\n session?: string;\n stream?: boolean;\n signal?: AbortSignal;\n },\n ): Promise<Response> {\n init.signal?.throwIfAborted();\n const own =\n typeof options.headers === \"function\"\n ? await options.headers()\n : (options.headers ?? {});\n const headers = new Headers(own);\n headers.set(AI_WIRE_HEADER, AI_WIRE_VERSION);\n headers.set(\n \"Accept\",\n init.stream ? \"text/event-stream\" : \"application/json\",\n );\n if (init.session) headers.set(AI_SESSION_HEADER, init.session);\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.json !== undefined) {\n headers.set(\"Content-Type\", \"application/json\");\n body = JSON.stringify(init.json);\n }\n let response: Response;\n try {\n init.signal?.throwIfAborted();\n response = await fetchRequest(`${base}${aiRoutePath(route, params)}`, {\n method: AI_ROUTES[route].method,\n headers,\n body,\n ...(options.credentials ? { credentials: options.credentials } : {}),\n signal: init.signal,\n });\n } catch (error) {\n if (init.signal?.aborted) throw abortError(init.signal);\n throw error instanceof SdkError ? error : providerFailure();\n }\n if (!response.ok) throw await errorFromResponse(response);\n return response;\n }\n\n /**\n * One streamed call. `each` handles every event before `done`; it may\n * throw to end the call (a refusal, the editor's own error), which aborts\n * the request.\n */\n async function streamed<T>(\n route: AiRouteName,\n template: string,\n init: { json?: unknown; form?: FormData; session?: string },\n signal: AbortSignal,\n // Never called with `done` or `error`: those end the call first.\n each: (event: AiWireEvent) => Promise<void> | void,\n done: (event: AiDoneEvent) => T | undefined,\n ): Promise<T> {\n const controller = new AbortController();\n const stop = () => controller.abort(signal.reason);\n if (signal.aborted) stop();\n signal.addEventListener(\"abort\", stop, { once: true });\n try {\n const response = await request(\n route,\n { template },\n {\n ...init,\n stream: true,\n signal: controller.signal,\n },\n );\n if (!response.body) throw providerFailure();\n for await (const event of readSse(response.body)) {\n if (signal.aborted) {\n // Already received server edits still need reconciliation after Stop.\n if (event.type === \"applied\") await each(event);\n if (event.type === \"done\" || event.type === \"error\")\n throw abortError(signal);\n continue;\n }\n if (event.type === \"error\") throw errorFromEvent(event);\n if (event.type === \"done\") {\n const value = done(event);\n if (value === undefined) throw providerFailure();\n return value;\n }\n await each(event);\n }\n throw providerFailure();\n } catch (error) {\n if (signal.aborted) throw abortError(signal);\n controller.abort();\n if (error instanceof SdkError) throw error;\n if (error instanceof Error && error.name === \"AbortError\") throw error;\n if (error instanceof Error && !(error instanceof TypeError)) throw error;\n throw providerFailure();\n } finally {\n signal.removeEventListener(\"abort\", stop);\n }\n }\n\n function turn(\n route: \"generate\" | \"fixFinding\",\n req: AiRequestBase,\n init: { json?: unknown; form?: FormData },\n handlers: AiTurnHandlers,\n signal: AbortSignal,\n ): Promise<AiTurnResult> {\n let text = \"\";\n return streamed(\n route,\n req.templateId,\n { ...init, session: req.sessionId },\n signal,\n async (event) => {\n if (event.type === \"text\") {\n text += event.text;\n handlers.onText(event.text);\n } else if (event.type === \"operations\") {\n const result = await handlers.onOperations(event.ops);\n if (!result.applied) {\n // SSE is one-way: the backend can't be told mid-turn, so the\n // request ends here with the operation's own code (§4).\n throw new CollaborationConflict(AI_TEMPLATE_CHANGED_MESSAGE, {\n code: result.code,\n opIndex: result.opIndex,\n });\n }\n } else if (event.type === \"applied\") {\n handlers.onApplied({\n batchId: event.batchId,\n revision: event.revision,\n operationCount: event.operationCount,\n });\n }\n },\n (done) =>\n typeof done.messageId === \"string\" || done.messageId === null\n ? { messageId: done.messageId, text }\n : undefined,\n );\n }\n\n async function json<T>(response: Response): Promise<T> {\n try {\n return (await response.json()) as T;\n } catch {\n throw providerFailure();\n }\n }\n\n async function replay(\n route: \"undoMessage\" | \"redoMessage\",\n templateId: string,\n messageId: string,\n sessionId: string,\n ): Promise<AiReplay> {\n const response = await request(\n route,\n { template: templateId, message: messageId },\n { session: sessionId },\n );\n const body = await json<{\n data?: {\n operations?: unknown;\n replayId?: unknown;\n batchId?: unknown;\n revision?: unknown;\n };\n }>(response);\n if (!isRecord(body)) throw providerFailure();\n const data = body.data ?? {};\n if (\n Array.isArray(data.operations) &&\n typeof data.replayId === \"string\" &&\n data.replayId !== \"\"\n )\n return { operations: data.operations, replayId: data.replayId };\n if (typeof data.batchId === \"string\" && typeof data.revision === \"number\") {\n return { applied: { batchId: data.batchId, revision: data.revision } };\n }\n throw providerFailure();\n }\n\n return {\n chatAttachments: on(\"chat\") && on(\"chatAttachments\"),\n chat: on(\"chat\")\n ? (req, handlers, signal) => {\n const body = {\n message: req.message,\n selectedBlockId: req.selectedBlockId,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n };\n if (req.attachments.length === 0)\n return turn(\"generate\", req, { json: body }, handlers, signal);\n const form = new FormData();\n form.set(AI_GENERATE_FIELDS.message, req.message);\n if (req.selectedBlockId !== null)\n form.set(AI_GENERATE_FIELDS.selectedBlockId, req.selectedBlockId);\n form.set(AI_GENERATE_FIELDS.mergeTags, JSON.stringify(req.mergeTags));\n form.set(AI_GENERATE_FIELDS.content, JSON.stringify(req.content));\n if (req.revision !== null)\n form.set(AI_GENERATE_FIELDS.revision, String(req.revision));\n for (const attachment of req.attachments)\n form.append(\n AI_GENERATE_FIELDS.attachment,\n attachment.file,\n attachment.name,\n );\n return turn(\"generate\", req, { form }, handlers, signal);\n }\n : false,\n fixFinding: on(\"fixFinding\")\n ? (req, handlers, signal) =>\n turn(\n \"fixFinding\",\n req,\n {\n json: {\n finding: req.finding,\n mergeTags: req.mergeTags,\n content: req.content,\n revision: req.revision,\n },\n },\n handlers,\n signal,\n )\n : false,\n rewrite: on(\"rewrite\")\n ? (req, handlers, signal) =>\n streamed(\n \"rewriteText\",\n req.templateId,\n {\n json: {\n html: req.html,\n preset: req.preset,\n language: req.language,\n instruction: req.instruction,\n mergeTags: req.mergeTags,\n },\n },\n signal,\n (event) => {\n if (event.type === \"text\") handlers.onText(event.text);\n },\n (done) =>\n typeof done.html === \"string\" ? { html: done.html } : undefined,\n )\n : false,\n altText: on(\"altText\")\n ? (req, signal) => {\n const { templateId, sessionId, ...body } = req;\n return streamed(\n \"altText\",\n templateId,\n { json: body, session: sessionId },\n signal,\n () => {},\n (done) =>\n typeof done.alt === \"string\" ? { alt: done.alt } : undefined,\n );\n }\n : false,\n ...(on(\"conversation\")\n ? {\n conversation: {\n settleReplay: async (\n templateId: string,\n messageId: string,\n outcome: AiReplayOutcome,\n ) => {\n await request(\n \"settleReplay\",\n {\n template: templateId,\n message: messageId,\n replayId: outcome.replayId,\n },\n {\n session: outcome.sessionId,\n json: {\n direction: outcome.direction,\n applied: outcome.applied,\n },\n },\n );\n },\n list: async (templateId: string) => {\n const response = await request(\n \"conversationMessages\",\n { template: templateId },\n {},\n );\n const body = await json<{ data?: unknown }>(response);\n if (!isRecord(body)) throw providerFailure();\n if (!Array.isArray(body.data)) throw providerFailure();\n return body.data;\n },\n clear: async (templateId: string) => {\n await request(\"clearConversation\", { template: templateId }, {});\n },\n undo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"undoMessage\", templateId, messageId, context.sessionId),\n redo: (\n templateId: string,\n messageId: string,\n context: { sessionId: string },\n ) =>\n replay(\"redoMessage\", templateId, messageId, context.sessionId),\n },\n }\n : {}),\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as AiWireEvent, S as AiTextEvent, T as matchAiRoute, _ as AiReplayResponse, a as AI_ROUTES, b as AiRouteMatch, c as AI_WIRE_VERSION, d as AiConversationResponse, f as AiDoneEvent, g as AiOperationsEvent, h as AiGenerateBody, i as AI_LIMITS, l as AiAltTextBody, m as AiFixFindingBody, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, p as AiErrorEvent, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as AiAppliedEvent, v as AiRewriteBody, w as aiRoutePath, x as AiRouteName, y as AiRoute } from "./wire-
|
|
1
|
+
import { C as AiWireEvent, S as AiTextEvent, T as matchAiRoute, _ as AiReplayResponse, a as AI_ROUTES, b as AiRouteMatch, c as AI_WIRE_VERSION, d as AiConversationResponse, f as AiDoneEvent, g as AiOperationsEvent, h as AiGenerateBody, i as AI_LIMITS, l as AiAltTextBody, m as AiFixFindingBody, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, p as AiErrorEvent, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as AiAppliedEvent, v as AiRewriteBody, w as aiRoutePath, x as AiRouteName, y as AiRoute } from "./wire-BJ4-EWvR.js";
|
|
2
2
|
import { AiAltTextRequest, AiAppliedBatch, AiChatAttachment, AiChatMessage, AiChatRequest, AiConversationProvider, AiErrorCode, AiFeatureName, AiFixFinding, AiFixRequest, AiLimitDetails, AiMergeTag, AiMergeTagSyntax, AiMergeTags, AiMessageBatch, AiMessageKind, AiOperationsResult, AiProvider, AiReplay, AiReplayContext, AiReplayOutcome, AiRequestBase, AiRewriteHandlers, AiRewritePreset, AiRewriteRequest, AiTurnHandlers, AiTurnResult, CollaborationConflict, Operation, SdkError, TemplateContent, ThrottledError } from "@templatical/types";
|
|
3
3
|
export { AI_ERROR_MESSAGES, AI_ERROR_STATUS, AI_GENERATE_FIELDS, AI_LIMITS, AI_ROUTES, AI_SESSION_HEADER, AI_WIRE_HEADER, AI_WIRE_VERSION, AiAltTextBody, type AiAltTextRequest, type AiAppliedBatch, AiAppliedEvent, type AiChatAttachment, type AiChatMessage, type AiChatRequest, type AiConversationProvider, AiConversationResponse, AiDoneEvent, type AiErrorCode, AiErrorEvent, type AiFeatureName, type AiFixFinding, AiFixFindingBody, type AiFixRequest, AiGenerateBody, type AiLimitDetails, type AiMergeTag, type AiMergeTagSyntax, type AiMergeTags, type AiMessageBatch, type AiMessageKind, AiOperationsEvent, type AiOperationsResult, type AiProvider, type AiReplay, type AiReplayContext, type AiReplayOutcome, AiReplayResponse, type AiRequestBase, AiRewriteBody, type AiRewriteHandlers, type AiRewritePreset, type AiRewriteRequest, AiRoute, AiRouteMatch, AiRouteName, AiTextEvent, type AiTurnHandlers, type AiTurnResult, AiWireEvent, CollaborationConflict, type Operation, SdkError, type TemplateContent, ThrottledError, aiRoutePath, matchAiRoute };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as AI_ROUTES, c as AI_WIRE_VERSION, i as AI_LIMITS, l as aiRoutePath, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "./wire-
|
|
1
|
+
import { a as AI_ROUTES, c as AI_WIRE_VERSION, i as AI_LIMITS, l as aiRoutePath, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "./wire-cYO4W7GH.js";
|
|
2
2
|
import { CollaborationConflict, SdkError, ThrottledError } from "@templatical/types";
|
|
3
3
|
export { AI_ERROR_MESSAGES, AI_ERROR_STATUS, AI_GENERATE_FIELDS, AI_LIMITS, AI_ROUTES, AI_SESSION_HEADER, AI_WIRE_HEADER, AI_WIRE_VERSION, CollaborationConflict, SdkError, ThrottledError, aiRoutePath, matchAiRoute };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/server/tool.d.ts
|
|
2
1
|
/** A tool in neutral JSON-schema form; adapters map it to their SDK's shape. */
|
|
3
2
|
interface AiToolDefinition {
|
|
4
3
|
name: string;
|
|
@@ -11,8 +10,6 @@ interface AiToolDefinition {
|
|
|
11
10
|
* survives every one of them.
|
|
12
11
|
*/
|
|
13
12
|
declare const applyOperationsTool: AiToolDefinition;
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/server/model.d.ts
|
|
16
13
|
/** A design file or an image URL attached to a user message. */
|
|
17
14
|
type AiModelAttachment = {
|
|
18
15
|
kind: "image" | "pdf";
|
|
@@ -79,6 +76,5 @@ interface AiModelAdapter {
|
|
|
79
76
|
signal?: AbortSignal;
|
|
80
77
|
}): AsyncIterable<AiModelPart>;
|
|
81
78
|
}
|
|
82
|
-
//#endregion
|
|
83
79
|
export { AiModelToolCall as a, applyOperationsTool as c, AiModelPart as i, AiModelAttachment as n, AiUsage as o, AiModelMessage as r, AiToolDefinition as s, AiModelAdapter as t };
|
|
84
|
-
//# sourceMappingURL=model-
|
|
80
|
+
//# sourceMappingURL=model-VqqOlLS2.d.ts.map
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as AiModelToolCall, c as applyOperationsTool, i as AiModelPart, n as AiModelAttachment, o as AiUsage, r as AiModelMessage, s as AiToolDefinition, t as AiModelAdapter } from "../model-
|
|
2
|
-
import { C as AiWireEvent, h as AiGenerateBody, l as AiAltTextBody, m as AiFixFindingBody, p as AiErrorEvent, v as AiRewriteBody } from "../wire-
|
|
1
|
+
import { a as AiModelToolCall, c as applyOperationsTool, i as AiModelPart, n as AiModelAttachment, o as AiUsage, r as AiModelMessage, s as AiToolDefinition, t as AiModelAdapter } from "../model-VqqOlLS2.js";
|
|
2
|
+
import { C as AiWireEvent, h as AiGenerateBody, l as AiAltTextBody, m as AiFixFindingBody, p as AiErrorEvent, v as AiRewriteBody } from "../wire-BJ4-EWvR.js";
|
|
3
3
|
import { AiAltTextRequest, AiFixFinding, AiMergeTagSyntax, AiMergeTags, AiRewritePreset, Operation, OperationError, SdkError, TemplateContent, ThrottledError } from "@templatical/types";
|
|
4
|
-
//#region src/generated/assets.d.ts
|
|
5
4
|
export declare const FIX_RULE_GUIDANCE: {
|
|
6
5
|
readonly "a11y.link-vague-text": "Rewrite the link's text so it names where the link goes or what it does, using the link's href and the sentence around it. Keep the href, the surrounding text and every merge tag.";
|
|
7
6
|
readonly "a11y.button-vague-label": "Rewrite the button's label so it names the action and where it leads, using its URL and the text near it. Keep the URL.";
|
|
@@ -11,8 +10,6 @@ export declare const FIX_RULE_GUIDANCE: {
|
|
|
11
10
|
readonly "deliverability.link-text-domain-mismatch": "The link's visible text looks like a web address that differs from where the link goes. Rewrite only the visible text into descriptive words for the destination. Never change the href, and never put a URL or domain in the text.";
|
|
12
11
|
readonly "deliverability.low-text": "The email has images but too little text. Add a short paragraph of real body copy built only from facts already in the email, such as its headings, buttons and captions. Don't invent products, prices or dates, and don't pad the alt text or the preheader.";
|
|
13
12
|
};
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/server/prompts.d.ts
|
|
16
13
|
/**
|
|
17
14
|
* The system prompt of chat and fix turns: the rules
|
|
18
15
|
* (`prompts/editing.md`), the operation vocabulary (`prompts/operations.md`)
|
|
@@ -59,8 +56,6 @@ export declare function buildAltTextRequest(request: AiAltTextRequest): {
|
|
|
59
56
|
system: string;
|
|
60
57
|
messages: AiModelMessage[];
|
|
61
58
|
};
|
|
62
|
-
//#endregion
|
|
63
|
-
//#region src/server/merge-tags.d.ts
|
|
64
59
|
/** The tag-shaped tokens in a text, in order. Nothing for `custom`. */
|
|
65
60
|
export declare function mergeTagTokens(text: string, syntax: AiMergeTagSyntax): string[];
|
|
66
61
|
/** The tag-shaped tokens in every string inside a JSON value. */
|
|
@@ -72,8 +67,6 @@ export declare function mergeTagTokensIn(value: unknown, syntax: AiMergeTagSynta
|
|
|
72
67
|
export declare function flattenMergeTags(html: string): string;
|
|
73
68
|
/** The distinct tokens of `found` that `allowed` lacks, in order. Nothing for `custom`. */
|
|
74
69
|
export declare function unknownMergeTags(syntax: AiMergeTagSyntax, found: readonly string[], allowed: readonly string[]): string[];
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/server/content-rules.d.ts
|
|
77
70
|
/**
|
|
78
71
|
* Every block in a list, at any depth, depth first. A section's children are
|
|
79
72
|
* columns (lists of blocks) and a wrapper's are blocks, so any list is walked
|
|
@@ -86,8 +79,6 @@ export declare function walkBlocks(nodes: unknown): Record<string, unknown>[];
|
|
|
86
79
|
* may keep the ones that are).
|
|
87
80
|
*/
|
|
88
81
|
export declare function contentRuleRefusal(blocks: unknown, existing: ReadonlyMap<string, Record<string, unknown>>, forbidden: readonly string[]): string | null;
|
|
89
|
-
//#endregion
|
|
90
|
-
//#region src/server/refusal.d.ts
|
|
91
82
|
/** "updateBlock b-1", "addBlock b-9", "updateSettings", or "unknown". */
|
|
92
83
|
export declare function describeOp(op: unknown): string;
|
|
93
84
|
/** "op 2 (updateBlock b-1) was refused: …", numbered from 0 like `opIndex`. */
|
|
@@ -98,8 +89,6 @@ interface NamedLock {
|
|
|
98
89
|
sessionId: string;
|
|
99
90
|
name: string;
|
|
100
91
|
}
|
|
101
|
-
//#endregion
|
|
102
|
-
//#region src/server/editing-session.d.ts
|
|
103
92
|
interface EditingSessionLimits {
|
|
104
93
|
/** Operations per call. */
|
|
105
94
|
maxOps: number;
|
|
@@ -149,8 +138,6 @@ interface EditingSession {
|
|
|
149
138
|
* engine (locks and preconditions included), the size; then applied.
|
|
150
139
|
*/
|
|
151
140
|
export declare function createEditingSession(options: EditingSessionOptions): EditingSession;
|
|
152
|
-
//#endregion
|
|
153
|
-
//#region src/server/run-editing-turn.d.ts
|
|
154
141
|
interface EditingTurnRequest {
|
|
155
142
|
content: TemplateContent;
|
|
156
143
|
revision?: number | null;
|
|
@@ -199,8 +186,6 @@ interface EditingTurnResult {
|
|
|
199
186
|
* `messageId` and send it.
|
|
200
187
|
*/
|
|
201
188
|
export declare function runEditingTurn(options: RunEditingTurnOptions): Promise<EditingTurnResult>;
|
|
202
|
-
//#endregion
|
|
203
|
-
//#region src/server/event-stream.d.ts
|
|
204
189
|
interface AiEventStreamController {
|
|
205
190
|
/** Write an event. Ignored once `done` or `error` was written. */
|
|
206
191
|
send(event: AiWireEvent): void;
|
|
@@ -220,8 +205,6 @@ interface AiEventStreamOptions {
|
|
|
220
205
|
* `done` or `error` ends it with the provider error.
|
|
221
206
|
*/
|
|
222
207
|
export declare function aiEventStream(producer: (stream: AiEventStreamController) => Promise<void>, options?: AiEventStreamOptions): Response;
|
|
223
|
-
//#endregion
|
|
224
|
-
//#region src/server/errors.d.ts
|
|
225
208
|
/** An error as the stream's last event. Anything but a coded `SdkError` hides behind the provider error. */
|
|
226
209
|
export declare function aiErrorEvent(error: unknown): AiErrorEvent;
|
|
227
210
|
/** A JSON response with the wire's header; `204` has no body. */
|
|
@@ -231,8 +214,6 @@ export declare function aiJsonResponse(body: unknown, status?: number): Response
|
|
|
231
214
|
* code's status, `Retry-After` (seconds) for a `ThrottledError`.
|
|
232
215
|
*/
|
|
233
216
|
export declare function aiErrorResponse(error: unknown): Response;
|
|
234
|
-
//#endregion
|
|
235
|
-
//#region src/server/read-request.d.ts
|
|
236
217
|
interface Base {
|
|
237
218
|
template: string;
|
|
238
219
|
/** `X-Templatical-Session`, when sent. */
|
|
@@ -270,8 +251,6 @@ type AiWireRequest = Base & ({
|
|
|
270
251
|
* oversized content throws `content_too_large` (413).
|
|
271
252
|
*/
|
|
272
253
|
export declare function readAiRequest(request: Request): Promise<AiWireRequest | null>;
|
|
273
|
-
//#endregion
|
|
274
|
-
//#region src/server/checks.d.ts
|
|
275
254
|
/**
|
|
276
255
|
* A rewrite's output, checked: code fences dropped, not empty, no merge tag
|
|
277
256
|
* that is neither declared nor in the input. On `ok: false`, ask once more
|
|
@@ -308,6 +287,5 @@ export declare function buildAltTextRepairRequest(built: {
|
|
|
308
287
|
export declare function checkAltText(answer: string, { maxLength }: {
|
|
309
288
|
maxLength: number;
|
|
310
289
|
}): string;
|
|
311
|
-
//#endregion
|
|
312
290
|
export { type AiEventStreamController, type AiEventStreamOptions, type AiModelAdapter, type AiModelAttachment, type AiModelMessage, type AiModelPart, type AiModelToolCall, type AiToolDefinition, type AiUsage, type AiWireRequest, type EditingApplyResult, type EditingPromptInput, type EditingSession, type EditingSessionLimits, type EditingSessionOptions, type EditingTurnRequest, type EditingTurnResult, type NamedLock, type RewritePromptInput, type RunEditingTurnOptions, SdkError, ThrottledError, applyOperationsTool };
|
|
313
291
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/server/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as AI_LIMITS, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "../wire-
|
|
2
|
-
import { C as mergeTagTokens, E as FIX_RULE_GUIDANCE, S as flattenMergeTags, T as unknownMergeTags, _ as buildEditingPrompt, a as checkRewrite, b as editingInstructions, c as createEditingSession, d as opRefusal, f as contentRuleRefusal, g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, l as applyOperationsTool, m as REWRITE_PRESET_INSTRUCTIONS, n as buildRewriteRepairPrompt, o as runEditingTurn, p as walkBlocks, r as checkAltText, s as EDITING_SESSION_LIMITS, t as buildAltTextRepairRequest, u as describeOp, v as buildFixRequest, w as mergeTagTokensIn, x as rewriteInstructions, y as buildRewritePrompt } from "../checks-
|
|
1
|
+
import { i as AI_LIMITS, n as AI_ERROR_STATUS, o as AI_SESSION_HEADER, r as AI_GENERATE_FIELDS, s as AI_WIRE_HEADER, t as AI_ERROR_MESSAGES, u as matchAiRoute } from "../wire-cYO4W7GH.js";
|
|
2
|
+
import { C as mergeTagTokens, E as FIX_RULE_GUIDANCE, S as flattenMergeTags, T as unknownMergeTags, _ as buildEditingPrompt, a as checkRewrite, b as editingInstructions, c as createEditingSession, d as opRefusal, f as contentRuleRefusal, g as buildChatRequest, h as buildAltTextRequest, i as checkAltTextAnswer, l as applyOperationsTool, m as REWRITE_PRESET_INSTRUCTIONS, n as buildRewriteRepairPrompt, o as runEditingTurn, p as walkBlocks, r as checkAltText, s as EDITING_SESSION_LIMITS, t as buildAltTextRepairRequest, u as describeOp, v as buildFixRequest, w as mergeTagTokensIn, x as rewriteInstructions, y as buildRewritePrompt } from "../checks-B-TtaywP.js";
|
|
3
3
|
import { CollaborationConflict, SdkError, SdkError as SdkError$1, ThrottledError, ThrottledError as ThrottledError$1 } from "@templatical/types";
|
|
4
|
-
|
|
4
|
+
// src/server/errors.ts
|
|
5
5
|
const PROVIDER_ERROR = {
|
|
6
6
|
code: "ai_provider_error",
|
|
7
7
|
message: AI_ERROR_MESSAGES.ai_provider_error
|
|
@@ -61,8 +61,7 @@ function aiErrorResponse(error) {
|
|
|
61
61
|
if (error instanceof ThrottledError$1) response.headers.set("Retry-After", String(Math.ceil(error.retryAfterMs / 1e3)));
|
|
62
62
|
return response;
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
//#region src/server/event-stream.ts
|
|
64
|
+
// src/server/event-stream.ts
|
|
66
65
|
/**
|
|
67
66
|
* The SSE response for any Fetch-API server (Next, Hono, Workers, Deno, Bun;
|
|
68
67
|
* Express through an adapter). The producer writes events; a producer that
|
|
@@ -137,8 +136,7 @@ function aiEventStream(producer, options = {}) {
|
|
|
137
136
|
headers.set(AI_WIRE_HEADER, "1");
|
|
138
137
|
return new Response(body, { headers });
|
|
139
138
|
}
|
|
140
|
-
|
|
141
|
-
//#region src/server/read-request.ts
|
|
139
|
+
// src/server/read-request.ts
|
|
142
140
|
const PRESETS = [
|
|
143
141
|
"shorten",
|
|
144
142
|
"expand",
|
|
@@ -349,7 +347,6 @@ async function readAiRequest(request) {
|
|
|
349
347
|
}
|
|
350
348
|
}
|
|
351
349
|
}
|
|
352
|
-
//#endregion
|
|
353
350
|
export { EDITING_SESSION_LIMITS, FIX_RULE_GUIDANCE, REWRITE_PRESET_INSTRUCTIONS, SdkError, ThrottledError, aiErrorEvent, aiErrorResponse, aiEventStream, aiJsonResponse, applyOperationsTool, buildAltTextRepairRequest, buildAltTextRequest, buildChatRequest, buildEditingPrompt, buildFixRequest, buildRewritePrompt, buildRewriteRepairPrompt, checkAltText, checkAltTextAnswer, checkRewrite, contentRuleRefusal, createEditingSession, describeOp, editingInstructions, flattenMergeTags, mergeTagTokens, mergeTagTokensIn, opRefusal, readAiRequest, rewriteInstructions, runEditingTurn, unknownMergeTags, walkBlocks };
|
|
354
351
|
|
|
355
352
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["SdkError","ThrottledError","SdkError"],"sources":["../../src/server/errors.ts","../../src/server/event-stream.ts","../../src/server/read-request.ts"],"sourcesContent":["import {\n CollaborationConflict,\n SdkError,\n ThrottledError,\n} from \"@templatical/types\";\nimport {\n AI_ERROR_MESSAGES,\n AI_ERROR_STATUS,\n AI_WIRE_HEADER,\n AI_WIRE_VERSION,\n type AiErrorEvent,\n} from \"../wire\";\n\nconst PROVIDER_ERROR = {\n code: \"ai_provider_error\",\n message: AI_ERROR_MESSAGES.ai_provider_error,\n} as const;\n\nfunction known(error: unknown): error is SdkError & { code: string } {\n return error instanceof SdkError && typeof error.code === \"string\";\n}\n\nfunction conflictDetails(error: unknown): Record<string, unknown> {\n if (!(error instanceof CollaborationConflict)) return {};\n return {\n ...(error.opIndex !== undefined ? { opIndex: error.opIndex } : {}),\n ...(error.holder !== undefined ? { holder: error.holder } : {}),\n ...(error.holders !== undefined ? { holders: error.holders } : {}),\n };\n}\n\n/** An error as the stream's last event. Anything but a coded `SdkError` hides behind the provider error. */\nexport function aiErrorEvent(error: unknown): AiErrorEvent {\n if (!known(error)) return { type: \"error\", ...PROVIDER_ERROR };\n return {\n ...error.details,\n ...conflictDetails(error),\n type: \"error\",\n code: error.code,\n message: error.message,\n };\n}\n\n/** A JSON response with the wire's header; `204` has no body. */\nexport function aiJsonResponse(body: unknown, status = 200): Response {\n const headers = new Headers({ [AI_WIRE_HEADER]: AI_WIRE_VERSION });\n if (status === 204) return new Response(null, { status, headers });\n headers.set(\"Content-Type\", \"application/json\");\n return new Response(JSON.stringify(body), { status, headers });\n}\n\n/**\n * An error before the stream starts: `{ message, code, …details }` with the\n * code's status, `Retry-After` (seconds) for a `ThrottledError`.\n */\nexport function aiErrorResponse(error: unknown): Response {\n if (!(error instanceof SdkError))\n return aiJsonResponse({ ...PROVIDER_ERROR }, 502);\n // A validation failure (`readAiRequest`'s 422) has no code, as Laravel's has none.\n const code = typeof error.code === \"string\" ? error.code : undefined;\n const status =\n error.statusCode ??\n (code !== undefined ? AI_ERROR_STATUS[code] : undefined) ??\n 400;\n const response = aiJsonResponse(\n {\n ...error.details,\n ...conflictDetails(error),\n message: error.message,\n ...(code !== undefined ? { code } : {}),\n },\n status,\n );\n if (error instanceof ThrottledError) {\n response.headers.set(\n \"Retry-After\",\n String(Math.ceil(error.retryAfterMs / 1000)),\n );\n }\n return response;\n}\n\nexport { PROVIDER_ERROR };\n","import { AI_WIRE_HEADER, AI_WIRE_VERSION, type AiWireEvent } from \"../wire\";\nimport { aiErrorEvent, PROVIDER_ERROR } from \"./errors\";\n\nexport interface AiEventStreamController {\n /** Write an event. Ignored once `done` or `error` was written. */\n send(event: AiWireEvent): void;\n /** Aborted when the client goes away or `options.signal` aborts. */\n readonly signal: AbortSignal;\n}\n\nexport interface AiEventStreamOptions {\n /** Write `: ping` after this long without a write (15 s). */\n pingMs?: number;\n signal?: AbortSignal;\n headers?: HeadersInit;\n}\n\n/**\n * The SSE response for any Fetch-API server (Next, Hono, Workers, Deno, Bun;\n * Express through an adapter). The producer writes events; a producer that\n * throws ends the stream with an `error` event, and one that returns without\n * `done` or `error` ends it with the provider error.\n */\nexport function aiEventStream(\n producer: (stream: AiEventStreamController) => Promise<void>,\n options: AiEventStreamOptions = {},\n): Response {\n const pingMs = options.pingMs ?? 15_000;\n const encoder = new TextEncoder();\n const abort = new AbortController();\n function forwardAbort(): void {\n abort.abort(options.signal?.reason);\n }\n if (options.signal) {\n if (options.signal.aborted) abort.abort(options.signal.reason);\n else options.signal.addEventListener(\"abort\", forwardAbort, { once: true });\n }\n let timer: ReturnType<typeof setInterval> | null = null;\n let closed = false;\n\n function cleanup(): void {\n if (timer) clearInterval(timer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n const body = new ReadableStream<Uint8Array>({\n start(controller) {\n let finished = false;\n let lastWrite = Date.now();\n const write = (chunk: string): void => {\n if (closed) return;\n controller.enqueue(encoder.encode(chunk));\n lastWrite = Date.now();\n };\n const send = (event: AiWireEvent): void => {\n if (finished) return;\n write(`data: ${JSON.stringify(event)}\\n\\n`);\n if (event.type === \"done\" || event.type === \"error\") finished = true;\n };\n timer = setInterval(\n () => {\n if (Date.now() - lastWrite >= pingMs) write(\": ping\\n\\n\");\n },\n Math.max(1, Math.floor(pingMs / 3)),\n );\n void (async () => {\n try {\n await producer({ send, signal: abort.signal });\n if (!abort.signal.aborted) send({ type: \"error\", ...PROVIDER_ERROR });\n } catch (error) {\n if (!abort.signal.aborted) send(aiErrorEvent(error));\n } finally {\n cleanup();\n if (!closed) {\n closed = true;\n controller.close();\n }\n }\n })();\n },\n cancel(reason) {\n closed = true;\n cleanup();\n abort.abort(reason);\n },\n });\n\n const headers = new Headers(options.headers);\n headers.set(\"Content-Type\", \"text/event-stream\");\n headers.set(\"Cache-Control\", \"no-cache\");\n headers.set(\"X-Accel-Buffering\", \"no\");\n headers.set(AI_WIRE_HEADER, AI_WIRE_VERSION);\n return new Response(body, { headers });\n}\n","import type {\n AiFixFinding,\n AiChatAttachment,\n AiMergeTags,\n AiRewritePreset,\n TemplateContent,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport {\n AI_GENERATE_FIELDS,\n AI_ERROR_MESSAGES,\n AI_LIMITS,\n AI_SESSION_HEADER,\n matchAiRoute,\n type AiFixFindingBody,\n type AiAltTextBody,\n type AiGenerateBody,\n type AiRewriteBody,\n} from \"../wire\";\n\ninterface Base {\n template: string;\n /** `X-Templatical-Session`, when sent. */\n sessionId: string | null;\n}\n\nexport type AiWireRequest = Base &\n (\n | { route: \"generate\"; body: AiGenerateBody }\n | { route: \"fixFinding\"; body: AiFixFindingBody }\n | { route: \"rewriteText\"; body: AiRewriteBody }\n | { route: \"altText\"; body: AiAltTextBody }\n | { route: \"conversationMessages\" | \"clearConversation\" }\n | { route: \"undoMessage\" | \"redoMessage\"; message: string }\n | {\n route: \"settleReplay\";\n message: string;\n replayId: string;\n body: { direction: \"undo\" | \"redo\"; applied: boolean };\n }\n );\n\nconst PRESETS: readonly AiRewritePreset[] = [\n \"shorten\",\n \"expand\",\n \"friendlier\",\n \"formal\",\n \"fixGrammar\",\n \"translate\",\n];\nconst IMAGE_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"];\nconst MERGE_TAG_SYNTAXES = [\n \"liquid\",\n \"handlebars\",\n \"mailchimp\",\n \"ampscript\",\n \"custom\",\n];\n\nfunction invalid(message: string): never {\n throw new SdkError(message, 422);\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isBoundedText = (value: unknown, max: number): value is string =>\n typeof value === \"string\" && Array.from(value).length <= max;\n\nfunction content(value: unknown): TemplateContent {\n if (\n !isRecord(value) ||\n !Array.isArray(value.blocks) ||\n !isRecord(value.settings)\n ) {\n invalid(\"content must be the template's content.\");\n }\n if (\n new TextEncoder().encode(JSON.stringify(value)).length >\n AI_LIMITS.contentMaxBytes\n )\n throw new SdkError(\n AI_ERROR_MESSAGES.content_too_large,\n 413,\n \"content_too_large\",\n );\n return value as unknown as TemplateContent;\n}\n\nfunction mergeTags(value: unknown): AiMergeTags {\n if (\n !isRecord(value) ||\n typeof value.syntax !== \"string\" ||\n !MERGE_TAG_SYNTAXES.includes(value.syntax) ||\n !Array.isArray(value.tags) ||\n value.tags.length > AI_LIMITS.mergeTagsMax\n ) {\n invalid(\"mergeTags must be { syntax, tags } with at most 500 tags.\");\n }\n for (const tag of value.tags) {\n if (\n !isRecord(tag) ||\n !isBoundedText(tag.value, 200) ||\n !isBoundedText(tag.label, 100) ||\n (tag.group !== undefined && !isBoundedText(tag.group, 100)) ||\n (tag.description !== undefined && !isBoundedText(tag.description, 200))\n ) {\n invalid(\n \"Each merge tag must have string value (at most 200 characters) and label (at most 100), with optional string group (at most 100) and description (at most 200).\",\n );\n }\n }\n return value as unknown as AiMergeTags;\n}\n\nfunction revision(value: unknown): number | null {\n if (value === null || value === undefined || value === \"\") return null;\n const number = typeof value === \"string\" ? Number(value) : value;\n if (typeof number !== \"number\" || !Number.isInteger(number) || number < 0) {\n invalid(\"revision must be a whole number or null.\");\n }\n return number;\n}\n\nfunction optionalText(\n value: unknown,\n max: number,\n message: string,\n): string | null {\n if (value === null || value === undefined || value === \"\") return null;\n if (typeof value !== \"string\" || Array.from(value).length > max)\n invalid(message);\n return value;\n}\n\nfunction finding(value: unknown): AiFixFinding {\n if (!isRecord(value))\n invalid(\n \"finding must be { ruleId, message, suggestion, reference, blockId, excerpt? }.\",\n );\n if (\n typeof value.ruleId !== \"string\" ||\n value.ruleId.length === 0 ||\n value.ruleId.length > 200\n )\n invalid(\"finding.ruleId must be 1 to 200 characters.\");\n if (!isBoundedText(value.message, 1000))\n invalid(\"finding.message must be at most 1,000 code points.\");\n if (!isBoundedText(value.suggestion, 1000))\n invalid(\"finding.suggestion must be at most 1,000 code points.\");\n let reference: URL | null = null;\n if (typeof value.reference === \"string\" && value.reference.length <= 2048) {\n try {\n reference = new URL(value.reference);\n } catch {\n /* Invalid URL. */\n }\n }\n if (\n !reference ||\n (reference.protocol !== \"http:\" && reference.protocol !== \"https:\")\n )\n invalid(\n \"finding.reference must be an http: or https: URL of at most 2,048 characters.\",\n );\n if (\n value.blockId !== null &&\n (typeof value.blockId !== \"string\" || value.blockId.length > 200)\n )\n invalid(\n \"finding.blockId must be a string of at most 200 characters or null.\",\n );\n if (value.excerpt !== undefined && !isBoundedText(value.excerpt, 1000))\n invalid(\"finding.excerpt must be at most 1,000 code points when present.\");\n return value as unknown as AiFixFinding;\n}\n\n/**\n * The wire's request, parsed and checked against `AI_LIMITS`; `null` when the\n * request is no route of the wire. Field validation throws `SdkError(message, 422)`;\n * oversized content throws `content_too_large` (413).\n */\nexport async function readAiRequest(\n request: Request,\n): Promise<AiWireRequest | null> {\n const match = matchAiRoute(request.method, new URL(request.url).pathname);\n if (!match) return null;\n const base: Base = {\n template: match.template,\n sessionId: request.headers.get(AI_SESSION_HEADER),\n };\n\n if (base.sessionId !== null && !isBoundedText(base.sessionId, 200))\n invalid(\"The session id must be at most 200 characters.\");\n\n switch (match.route) {\n case \"conversationMessages\":\n case \"clearConversation\":\n return { ...base, route: match.route };\n case \"undoMessage\":\n case \"redoMessage\":\n return { ...base, route: match.route, message: match.message ?? \"\" };\n default:\n break;\n }\n\n let attachments: AiChatAttachment[] = [];\n let body: unknown;\n if (\n match.route === \"generate\" &&\n request.headers.get(\"Content-Type\")?.startsWith(\"multipart/form-data\")\n ) {\n const form = await request\n .formData()\n .catch(() => invalid(\"Send the message as multipart form data.\"));\n const files = form.getAll(AI_GENERATE_FIELDS.attachment);\n if (files.length > AI_LIMITS.attachmentsMax)\n invalid(\"Attach at most 4 files.\");\n if (\n files.some(\n (file) =>\n !(file instanceof Blob) ||\n (!IMAGE_TYPES.includes(file.type) && file.type !== \"application/pdf\"),\n )\n )\n invalid(\"Attach PNG, JPEG, WebP or GIF images, or PDFs.\");\n const blobs = files as File[];\n if (blobs.some((file) => file.size > AI_LIMITS.attachmentMaxBytes))\n invalid(\"Each attachment must be 5 MB or smaller.\");\n if (\n blobs.reduce((total, file) => total + file.size, 0) >\n AI_LIMITS.attachmentsTotalMaxBytes\n )\n invalid(\"Attachments must be 10 MB or smaller together.\");\n if (blobs.some((file) => file.name.length > 255))\n invalid(\"An attachment's file name must be at most 255 characters.\");\n attachments = blobs.map((file) => ({\n file,\n kind: file.type === \"application/pdf\" ? \"pdf\" : \"image\",\n name: file.name,\n }));\n const json = (field: string): unknown => {\n const raw = form.get(field);\n if (raw === null) return null;\n try {\n return JSON.parse(String(raw));\n } catch {\n return invalid(`${field} must be JSON.`);\n }\n };\n body = {\n message: form.get(AI_GENERATE_FIELDS.message),\n selectedBlockId: form.get(AI_GENERATE_FIELDS.selectedBlockId),\n revision: form.get(AI_GENERATE_FIELDS.revision),\n mergeTags: json(AI_GENERATE_FIELDS.mergeTags),\n content: json(AI_GENERATE_FIELDS.content),\n };\n } else {\n body = await request.json().catch(() => invalid(\"Send a JSON body.\"));\n }\n if (!isRecord(body)) invalid(\"Send a JSON object.\");\n\n switch (match.route) {\n case \"altText\": {\n if (\n typeof body.blockId !== \"string\" ||\n body.blockId.length === 0 ||\n body.blockId.length > 200\n )\n invalid(\"blockId must be 1 to 200 characters.\");\n let url: URL | null = null;\n if (\n isRecord(body.image) &&\n typeof body.image.url === \"string\" &&\n body.image.url.length <= 2048\n ) {\n try {\n url = new URL(body.image.url);\n } catch {\n /* Invalid URL. */\n }\n }\n if (!url || (url.protocol !== \"http:\" && url.protocol !== \"https:\"))\n invalid(\n \"image.url must be an http: or https: URL of at most 2,048 characters.\",\n );\n for (const key of [\"currentAlt\", \"linkHref\"] as const) {\n if (\n body[key] !== null &&\n (typeof body[key] !== \"string\" || body[key].length > 2048)\n )\n invalid(\n `${key} must be a string of at most 2,048 characters or null.`,\n );\n }\n if (!isBoundedText(body.nearbyText, 2000))\n invalid(\"nearbyText must be at most 2,000 code points.\");\n if (\n typeof body.locale !== \"string\" ||\n body.locale.length > 35 ||\n !/^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/.test(body.locale)\n )\n invalid(\"locale must be a BCP 47 tag of at most 35 characters.\");\n if (\n typeof body.maxLength !== \"number\" ||\n !Number.isInteger(body.maxLength) ||\n body.maxLength < 1 ||\n body.maxLength > 1000\n )\n invalid(\"maxLength must be an integer from 1 to 1,000.\");\n return {\n ...base,\n route: \"altText\",\n body: {\n blockId: body.blockId,\n image: { url: (body.image as { url: string }).url },\n currentAlt: body.currentAlt as string | null,\n linkHref: body.linkHref as string | null,\n nearbyText: body.nearbyText,\n locale: body.locale,\n maxLength: body.maxLength,\n },\n };\n }\n case \"settleReplay\": {\n if (\n (body.direction !== \"undo\" && body.direction !== \"redo\") ||\n typeof body.applied !== \"boolean\"\n )\n invalid(\n \"A replay outcome must have direction undo or redo and boolean applied.\",\n );\n return {\n ...base,\n route: \"settleReplay\",\n message: match.message!,\n replayId: match.replayId!,\n body: { direction: body.direction, applied: body.applied },\n };\n }\n case \"generate\": {\n const message = body.message;\n if (\n typeof message !== \"string\" ||\n message.trim() === \"\" ||\n Array.from(message).length > AI_LIMITS.messageMaxChars\n ) {\n invalid(\"The message must be 1 to 4,000 characters.\");\n }\n if (\n typeof body.selectedBlockId === \"string\" &&\n !isBoundedText(body.selectedBlockId, 200)\n )\n invalid(\"selectedBlockId must be at most 200 characters.\");\n return {\n ...base,\n route: \"generate\",\n body: {\n message,\n attachments,\n selectedBlockId:\n typeof body.selectedBlockId === \"string\"\n ? body.selectedBlockId\n : null,\n mergeTags: mergeTags(body.mergeTags),\n content: content(body.content),\n revision: revision(body.revision),\n },\n };\n }\n case \"fixFinding\":\n return {\n ...base,\n route: \"fixFinding\",\n body: {\n finding: finding(body.finding),\n mergeTags: mergeTags(body.mergeTags),\n content: content(body.content),\n revision: revision(body.revision),\n },\n };\n case \"rewriteText\": {\n const html = body.html;\n if (\n typeof html !== \"string\" ||\n html === \"\" ||\n Array.from(html).length > AI_LIMITS.rewriteHtmlMaxChars\n ) {\n invalid(\"html must be 1 to 20,000 characters.\");\n }\n const preset = body.preset ?? null;\n if (preset !== null && !PRESETS.includes(preset as AiRewritePreset))\n invalid(\"Unknown preset.\");\n const instruction = optionalText(\n body.instruction,\n AI_LIMITS.instructionMaxChars,\n \"The instruction must be at most 500 characters.\",\n );\n if (preset === null && instruction === null)\n invalid(\"Choose a preset or write an instruction.\");\n const language = body.language === \"\" ? null : (body.language ?? null);\n if (\n language !== null &&\n (typeof language !== \"string\" ||\n !/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(language))\n ) {\n invalid(\"language must be a BCP 47 tag.\");\n }\n if (preset === \"translate\" && language === null)\n invalid(\"Choose a language to translate into.\");\n return {\n ...base,\n route: \"rewriteText\",\n body: {\n html,\n preset: preset as AiRewritePreset | null,\n language,\n instruction,\n mergeTags: mergeTags(body.mergeTags),\n },\n };\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["SdkError","ThrottledError","SdkError"],"sources":["../../src/server/errors.ts","../../src/server/event-stream.ts","../../src/server/read-request.ts"],"sourcesContent":["import {\n CollaborationConflict,\n SdkError,\n ThrottledError,\n} from \"@templatical/types\";\nimport {\n AI_ERROR_MESSAGES,\n AI_ERROR_STATUS,\n AI_WIRE_HEADER,\n AI_WIRE_VERSION,\n type AiErrorEvent,\n} from \"../wire\";\n\nconst PROVIDER_ERROR = {\n code: \"ai_provider_error\",\n message: AI_ERROR_MESSAGES.ai_provider_error,\n} as const;\n\nfunction known(error: unknown): error is SdkError & { code: string } {\n return error instanceof SdkError && typeof error.code === \"string\";\n}\n\nfunction conflictDetails(error: unknown): Record<string, unknown> {\n if (!(error instanceof CollaborationConflict)) return {};\n return {\n ...(error.opIndex !== undefined ? { opIndex: error.opIndex } : {}),\n ...(error.holder !== undefined ? { holder: error.holder } : {}),\n ...(error.holders !== undefined ? { holders: error.holders } : {}),\n };\n}\n\n/** An error as the stream's last event. Anything but a coded `SdkError` hides behind the provider error. */\nexport function aiErrorEvent(error: unknown): AiErrorEvent {\n if (!known(error)) return { type: \"error\", ...PROVIDER_ERROR };\n return {\n ...error.details,\n ...conflictDetails(error),\n type: \"error\",\n code: error.code,\n message: error.message,\n };\n}\n\n/** A JSON response with the wire's header; `204` has no body. */\nexport function aiJsonResponse(body: unknown, status = 200): Response {\n const headers = new Headers({ [AI_WIRE_HEADER]: AI_WIRE_VERSION });\n if (status === 204) return new Response(null, { status, headers });\n headers.set(\"Content-Type\", \"application/json\");\n return new Response(JSON.stringify(body), { status, headers });\n}\n\n/**\n * An error before the stream starts: `{ message, code, …details }` with the\n * code's status, `Retry-After` (seconds) for a `ThrottledError`.\n */\nexport function aiErrorResponse(error: unknown): Response {\n if (!(error instanceof SdkError))\n return aiJsonResponse({ ...PROVIDER_ERROR }, 502);\n // A validation failure (`readAiRequest`'s 422) has no code, as Laravel's has none.\n const code = typeof error.code === \"string\" ? error.code : undefined;\n const status =\n error.statusCode ??\n (code !== undefined ? AI_ERROR_STATUS[code] : undefined) ??\n 400;\n const response = aiJsonResponse(\n {\n ...error.details,\n ...conflictDetails(error),\n message: error.message,\n ...(code !== undefined ? { code } : {}),\n },\n status,\n );\n if (error instanceof ThrottledError) {\n response.headers.set(\n \"Retry-After\",\n String(Math.ceil(error.retryAfterMs / 1000)),\n );\n }\n return response;\n}\n\nexport { PROVIDER_ERROR };\n","import { AI_WIRE_HEADER, AI_WIRE_VERSION, type AiWireEvent } from \"../wire\";\nimport { aiErrorEvent, PROVIDER_ERROR } from \"./errors\";\n\nexport interface AiEventStreamController {\n /** Write an event. Ignored once `done` or `error` was written. */\n send(event: AiWireEvent): void;\n /** Aborted when the client goes away or `options.signal` aborts. */\n readonly signal: AbortSignal;\n}\n\nexport interface AiEventStreamOptions {\n /** Write `: ping` after this long without a write (15 s). */\n pingMs?: number;\n signal?: AbortSignal;\n headers?: HeadersInit;\n}\n\n/**\n * The SSE response for any Fetch-API server (Next, Hono, Workers, Deno, Bun;\n * Express through an adapter). The producer writes events; a producer that\n * throws ends the stream with an `error` event, and one that returns without\n * `done` or `error` ends it with the provider error.\n */\nexport function aiEventStream(\n producer: (stream: AiEventStreamController) => Promise<void>,\n options: AiEventStreamOptions = {},\n): Response {\n const pingMs = options.pingMs ?? 15_000;\n const encoder = new TextEncoder();\n const abort = new AbortController();\n function forwardAbort(): void {\n abort.abort(options.signal?.reason);\n }\n if (options.signal) {\n if (options.signal.aborted) abort.abort(options.signal.reason);\n else options.signal.addEventListener(\"abort\", forwardAbort, { once: true });\n }\n let timer: ReturnType<typeof setInterval> | null = null;\n let closed = false;\n\n function cleanup(): void {\n if (timer) clearInterval(timer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n const body = new ReadableStream<Uint8Array>({\n start(controller) {\n let finished = false;\n let lastWrite = Date.now();\n const write = (chunk: string): void => {\n if (closed) return;\n controller.enqueue(encoder.encode(chunk));\n lastWrite = Date.now();\n };\n const send = (event: AiWireEvent): void => {\n if (finished) return;\n write(`data: ${JSON.stringify(event)}\\n\\n`);\n if (event.type === \"done\" || event.type === \"error\") finished = true;\n };\n timer = setInterval(\n () => {\n if (Date.now() - lastWrite >= pingMs) write(\": ping\\n\\n\");\n },\n Math.max(1, Math.floor(pingMs / 3)),\n );\n void (async () => {\n try {\n await producer({ send, signal: abort.signal });\n if (!abort.signal.aborted) send({ type: \"error\", ...PROVIDER_ERROR });\n } catch (error) {\n if (!abort.signal.aborted) send(aiErrorEvent(error));\n } finally {\n cleanup();\n if (!closed) {\n closed = true;\n controller.close();\n }\n }\n })();\n },\n cancel(reason) {\n closed = true;\n cleanup();\n abort.abort(reason);\n },\n });\n\n const headers = new Headers(options.headers);\n headers.set(\"Content-Type\", \"text/event-stream\");\n headers.set(\"Cache-Control\", \"no-cache\");\n headers.set(\"X-Accel-Buffering\", \"no\");\n headers.set(AI_WIRE_HEADER, AI_WIRE_VERSION);\n return new Response(body, { headers });\n}\n","import type {\n AiFixFinding,\n AiChatAttachment,\n AiMergeTags,\n AiRewritePreset,\n TemplateContent,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport {\n AI_GENERATE_FIELDS,\n AI_ERROR_MESSAGES,\n AI_LIMITS,\n AI_SESSION_HEADER,\n matchAiRoute,\n type AiFixFindingBody,\n type AiAltTextBody,\n type AiGenerateBody,\n type AiRewriteBody,\n} from \"../wire\";\n\ninterface Base {\n template: string;\n /** `X-Templatical-Session`, when sent. */\n sessionId: string | null;\n}\n\nexport type AiWireRequest = Base &\n (\n | { route: \"generate\"; body: AiGenerateBody }\n | { route: \"fixFinding\"; body: AiFixFindingBody }\n | { route: \"rewriteText\"; body: AiRewriteBody }\n | { route: \"altText\"; body: AiAltTextBody }\n | { route: \"conversationMessages\" | \"clearConversation\" }\n | { route: \"undoMessage\" | \"redoMessage\"; message: string }\n | {\n route: \"settleReplay\";\n message: string;\n replayId: string;\n body: { direction: \"undo\" | \"redo\"; applied: boolean };\n }\n );\n\nconst PRESETS: readonly AiRewritePreset[] = [\n \"shorten\",\n \"expand\",\n \"friendlier\",\n \"formal\",\n \"fixGrammar\",\n \"translate\",\n];\nconst IMAGE_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"];\nconst MERGE_TAG_SYNTAXES = [\n \"liquid\",\n \"handlebars\",\n \"mailchimp\",\n \"ampscript\",\n \"custom\",\n];\n\nfunction invalid(message: string): never {\n throw new SdkError(message, 422);\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isBoundedText = (value: unknown, max: number): value is string =>\n typeof value === \"string\" && Array.from(value).length <= max;\n\nfunction content(value: unknown): TemplateContent {\n if (\n !isRecord(value) ||\n !Array.isArray(value.blocks) ||\n !isRecord(value.settings)\n ) {\n invalid(\"content must be the template's content.\");\n }\n if (\n new TextEncoder().encode(JSON.stringify(value)).length >\n AI_LIMITS.contentMaxBytes\n )\n throw new SdkError(\n AI_ERROR_MESSAGES.content_too_large,\n 413,\n \"content_too_large\",\n );\n return value as unknown as TemplateContent;\n}\n\nfunction mergeTags(value: unknown): AiMergeTags {\n if (\n !isRecord(value) ||\n typeof value.syntax !== \"string\" ||\n !MERGE_TAG_SYNTAXES.includes(value.syntax) ||\n !Array.isArray(value.tags) ||\n value.tags.length > AI_LIMITS.mergeTagsMax\n ) {\n invalid(\"mergeTags must be { syntax, tags } with at most 500 tags.\");\n }\n for (const tag of value.tags) {\n if (\n !isRecord(tag) ||\n !isBoundedText(tag.value, 200) ||\n !isBoundedText(tag.label, 100) ||\n (tag.group !== undefined && !isBoundedText(tag.group, 100)) ||\n (tag.description !== undefined && !isBoundedText(tag.description, 200))\n ) {\n invalid(\n \"Each merge tag must have string value (at most 200 characters) and label (at most 100), with optional string group (at most 100) and description (at most 200).\",\n );\n }\n }\n return value as unknown as AiMergeTags;\n}\n\nfunction revision(value: unknown): number | null {\n if (value === null || value === undefined || value === \"\") return null;\n const number = typeof value === \"string\" ? Number(value) : value;\n if (typeof number !== \"number\" || !Number.isInteger(number) || number < 0) {\n invalid(\"revision must be a whole number or null.\");\n }\n return number;\n}\n\nfunction optionalText(\n value: unknown,\n max: number,\n message: string,\n): string | null {\n if (value === null || value === undefined || value === \"\") return null;\n if (typeof value !== \"string\" || Array.from(value).length > max)\n invalid(message);\n return value;\n}\n\nfunction finding(value: unknown): AiFixFinding {\n if (!isRecord(value))\n invalid(\n \"finding must be { ruleId, message, suggestion, reference, blockId, excerpt? }.\",\n );\n if (\n typeof value.ruleId !== \"string\" ||\n value.ruleId.length === 0 ||\n value.ruleId.length > 200\n )\n invalid(\"finding.ruleId must be 1 to 200 characters.\");\n if (!isBoundedText(value.message, 1000))\n invalid(\"finding.message must be at most 1,000 code points.\");\n if (!isBoundedText(value.suggestion, 1000))\n invalid(\"finding.suggestion must be at most 1,000 code points.\");\n let reference: URL | null = null;\n if (typeof value.reference === \"string\" && value.reference.length <= 2048) {\n try {\n reference = new URL(value.reference);\n } catch {\n /* Invalid URL. */\n }\n }\n if (\n !reference ||\n (reference.protocol !== \"http:\" && reference.protocol !== \"https:\")\n )\n invalid(\n \"finding.reference must be an http: or https: URL of at most 2,048 characters.\",\n );\n if (\n value.blockId !== null &&\n (typeof value.blockId !== \"string\" || value.blockId.length > 200)\n )\n invalid(\n \"finding.blockId must be a string of at most 200 characters or null.\",\n );\n if (value.excerpt !== undefined && !isBoundedText(value.excerpt, 1000))\n invalid(\"finding.excerpt must be at most 1,000 code points when present.\");\n return value as unknown as AiFixFinding;\n}\n\n/**\n * The wire's request, parsed and checked against `AI_LIMITS`; `null` when the\n * request is no route of the wire. Field validation throws `SdkError(message, 422)`;\n * oversized content throws `content_too_large` (413).\n */\nexport async function readAiRequest(\n request: Request,\n): Promise<AiWireRequest | null> {\n const match = matchAiRoute(request.method, new URL(request.url).pathname);\n if (!match) return null;\n const base: Base = {\n template: match.template,\n sessionId: request.headers.get(AI_SESSION_HEADER),\n };\n\n if (base.sessionId !== null && !isBoundedText(base.sessionId, 200))\n invalid(\"The session id must be at most 200 characters.\");\n\n switch (match.route) {\n case \"conversationMessages\":\n case \"clearConversation\":\n return { ...base, route: match.route };\n case \"undoMessage\":\n case \"redoMessage\":\n return { ...base, route: match.route, message: match.message ?? \"\" };\n default:\n break;\n }\n\n let attachments: AiChatAttachment[] = [];\n let body: unknown;\n if (\n match.route === \"generate\" &&\n request.headers.get(\"Content-Type\")?.startsWith(\"multipart/form-data\")\n ) {\n const form = await request\n .formData()\n .catch(() => invalid(\"Send the message as multipart form data.\"));\n const files = form.getAll(AI_GENERATE_FIELDS.attachment);\n if (files.length > AI_LIMITS.attachmentsMax)\n invalid(\"Attach at most 4 files.\");\n if (\n files.some(\n (file) =>\n !(file instanceof Blob) ||\n (!IMAGE_TYPES.includes(file.type) && file.type !== \"application/pdf\"),\n )\n )\n invalid(\"Attach PNG, JPEG, WebP or GIF images, or PDFs.\");\n const blobs = files as File[];\n if (blobs.some((file) => file.size > AI_LIMITS.attachmentMaxBytes))\n invalid(\"Each attachment must be 5 MB or smaller.\");\n if (\n blobs.reduce((total, file) => total + file.size, 0) >\n AI_LIMITS.attachmentsTotalMaxBytes\n )\n invalid(\"Attachments must be 10 MB or smaller together.\");\n if (blobs.some((file) => file.name.length > 255))\n invalid(\"An attachment's file name must be at most 255 characters.\");\n attachments = blobs.map((file) => ({\n file,\n kind: file.type === \"application/pdf\" ? \"pdf\" : \"image\",\n name: file.name,\n }));\n const json = (field: string): unknown => {\n const raw = form.get(field);\n if (raw === null) return null;\n try {\n return JSON.parse(String(raw));\n } catch {\n return invalid(`${field} must be JSON.`);\n }\n };\n body = {\n message: form.get(AI_GENERATE_FIELDS.message),\n selectedBlockId: form.get(AI_GENERATE_FIELDS.selectedBlockId),\n revision: form.get(AI_GENERATE_FIELDS.revision),\n mergeTags: json(AI_GENERATE_FIELDS.mergeTags),\n content: json(AI_GENERATE_FIELDS.content),\n };\n } else {\n body = await request.json().catch(() => invalid(\"Send a JSON body.\"));\n }\n if (!isRecord(body)) invalid(\"Send a JSON object.\");\n\n switch (match.route) {\n case \"altText\": {\n if (\n typeof body.blockId !== \"string\" ||\n body.blockId.length === 0 ||\n body.blockId.length > 200\n )\n invalid(\"blockId must be 1 to 200 characters.\");\n let url: URL | null = null;\n if (\n isRecord(body.image) &&\n typeof body.image.url === \"string\" &&\n body.image.url.length <= 2048\n ) {\n try {\n url = new URL(body.image.url);\n } catch {\n /* Invalid URL. */\n }\n }\n if (!url || (url.protocol !== \"http:\" && url.protocol !== \"https:\"))\n invalid(\n \"image.url must be an http: or https: URL of at most 2,048 characters.\",\n );\n for (const key of [\"currentAlt\", \"linkHref\"] as const) {\n if (\n body[key] !== null &&\n (typeof body[key] !== \"string\" || body[key].length > 2048)\n )\n invalid(\n `${key} must be a string of at most 2,048 characters or null.`,\n );\n }\n if (!isBoundedText(body.nearbyText, 2000))\n invalid(\"nearbyText must be at most 2,000 code points.\");\n if (\n typeof body.locale !== \"string\" ||\n body.locale.length > 35 ||\n !/^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/.test(body.locale)\n )\n invalid(\"locale must be a BCP 47 tag of at most 35 characters.\");\n if (\n typeof body.maxLength !== \"number\" ||\n !Number.isInteger(body.maxLength) ||\n body.maxLength < 1 ||\n body.maxLength > 1000\n )\n invalid(\"maxLength must be an integer from 1 to 1,000.\");\n return {\n ...base,\n route: \"altText\",\n body: {\n blockId: body.blockId,\n image: { url: (body.image as { url: string }).url },\n currentAlt: body.currentAlt as string | null,\n linkHref: body.linkHref as string | null,\n nearbyText: body.nearbyText,\n locale: body.locale,\n maxLength: body.maxLength,\n },\n };\n }\n case \"settleReplay\": {\n if (\n (body.direction !== \"undo\" && body.direction !== \"redo\") ||\n typeof body.applied !== \"boolean\"\n )\n invalid(\n \"A replay outcome must have direction undo or redo and boolean applied.\",\n );\n return {\n ...base,\n route: \"settleReplay\",\n message: match.message!,\n replayId: match.replayId!,\n body: { direction: body.direction, applied: body.applied },\n };\n }\n case \"generate\": {\n const message = body.message;\n if (\n typeof message !== \"string\" ||\n message.trim() === \"\" ||\n Array.from(message).length > AI_LIMITS.messageMaxChars\n ) {\n invalid(\"The message must be 1 to 4,000 characters.\");\n }\n if (\n typeof body.selectedBlockId === \"string\" &&\n !isBoundedText(body.selectedBlockId, 200)\n )\n invalid(\"selectedBlockId must be at most 200 characters.\");\n return {\n ...base,\n route: \"generate\",\n body: {\n message,\n attachments,\n selectedBlockId:\n typeof body.selectedBlockId === \"string\"\n ? body.selectedBlockId\n : null,\n mergeTags: mergeTags(body.mergeTags),\n content: content(body.content),\n revision: revision(body.revision),\n },\n };\n }\n case \"fixFinding\":\n return {\n ...base,\n route: \"fixFinding\",\n body: {\n finding: finding(body.finding),\n mergeTags: mergeTags(body.mergeTags),\n content: content(body.content),\n revision: revision(body.revision),\n },\n };\n case \"rewriteText\": {\n const html = body.html;\n if (\n typeof html !== \"string\" ||\n html === \"\" ||\n Array.from(html).length > AI_LIMITS.rewriteHtmlMaxChars\n ) {\n invalid(\"html must be 1 to 20,000 characters.\");\n }\n const preset = body.preset ?? null;\n if (preset !== null && !PRESETS.includes(preset as AiRewritePreset))\n invalid(\"Unknown preset.\");\n const instruction = optionalText(\n body.instruction,\n AI_LIMITS.instructionMaxChars,\n \"The instruction must be at most 500 characters.\",\n );\n if (preset === null && instruction === null)\n invalid(\"Choose a preset or write an instruction.\");\n const language = body.language === \"\" ? null : (body.language ?? null);\n if (\n language !== null &&\n (typeof language !== \"string\" ||\n !/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(language))\n ) {\n invalid(\"language must be a BCP 47 tag.\");\n }\n if (preset === \"translate\" && language === null)\n invalid(\"Choose a language to translate into.\");\n return {\n ...base,\n route: \"rewriteText\",\n body: {\n html,\n preset: preset as AiRewritePreset | null,\n language,\n instruction,\n mergeTags: mergeTags(body.mergeTags),\n },\n };\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as AI_ERROR_MESSAGES } from "./wire-
|
|
2
|
-
import { i as parseRetryAfter } from "./errors-
|
|
1
|
+
import { t as AI_ERROR_MESSAGES } from "./wire-cYO4W7GH.js";
|
|
2
|
+
import { i as parseRetryAfter } from "./errors-BtORdtK2.js";
|
|
3
3
|
import { SdkError, ThrottledError } from "@templatical/types";
|
|
4
|
-
|
|
4
|
+
// src/adapters/shared.ts
|
|
5
5
|
/** The content an adapter stored as `{ adapter, content }` state, if it's this adapter's. */
|
|
6
6
|
function ownState(state, adapter) {
|
|
7
7
|
if (typeof state !== "object" || state === null) return void 0;
|
|
@@ -55,7 +55,6 @@ function providerError(error) {
|
|
|
55
55
|
if ((failure.status ?? failure.statusCode) !== 429) return error;
|
|
56
56
|
return new ThrottledError(AI_ERROR_MESSAGES.rate_limited, parseRetryAfter(retryAfterHeader(failure.headers ?? failure.responseHeaders)));
|
|
57
57
|
}
|
|
58
|
-
//#endregion
|
|
59
58
|
export { refused as a, providerError as i, cutOff as n, toolArguments as o, ownState as r, toolInput as s, blobBase64 as t };
|
|
60
59
|
|
|
61
|
-
//# sourceMappingURL=shared-
|
|
60
|
+
//# sourceMappingURL=shared-DOEVSpBX.js.map
|