| import { | ||
| BatchworkError, | ||
| getAdapter, | ||
| isTerminalStatus | ||
| } from "./chunk-g481f961.js"; | ||
| // src/server/events.ts | ||
| var EVENT_BY_STATUS = { | ||
| cancelled: "batch.cancelled", | ||
| completed: "batch.completed", | ||
| expired: "batch.expired", | ||
| failed: "batch.failed" | ||
| }; | ||
| var toEvent = (provider, snapshot) => ({ | ||
| completedAt: snapshot.completedAt?.toISOString(), | ||
| createdAt: snapshot.createdAt?.toISOString(), | ||
| id: snapshot.id, | ||
| provider, | ||
| requestCounts: snapshot.requestCounts, | ||
| type: EVENT_BY_STATUS[snapshot.status] ?? "batch.completed" | ||
| }); | ||
| // src/server/signing.ts | ||
| var SIGNATURE_VERSION = "v1"; | ||
| var TOLERANCE_SECONDS = 300; | ||
| var SECRET_PREFIX = "whsec_"; | ||
| var encoder = new TextEncoder; | ||
| var replayCache = new Map; | ||
| var defaultReplayStore = { | ||
| get: (id) => replayCache.get(id), | ||
| set: (id, expiresAt) => { | ||
| replayCache.set(id, expiresAt); | ||
| } | ||
| }; | ||
| var pruneReplayCache = (now) => { | ||
| for (const [id, expiresAt] of replayCache) { | ||
| if (expiresAt <= now) { | ||
| replayCache.delete(id); | ||
| } | ||
| } | ||
| }; | ||
| var base64ToBytes = (value) => Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0); | ||
| var bytesToBase64 = (bytes) => { | ||
| let binary = ""; | ||
| for (const byte of bytes) { | ||
| binary += String.fromCodePoint(byte); | ||
| } | ||
| return btoa(binary); | ||
| }; | ||
| var importKey = (secret) => { | ||
| const raw = secret.startsWith(SECRET_PREFIX) ? base64ToBytes(secret.slice(SECRET_PREFIX.length)) : encoder.encode(secret); | ||
| return crypto.subtle.importKey("raw", raw, { hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"]); | ||
| }; | ||
| var signContent = async (secret, content) => { | ||
| const key = await importKey(secret); | ||
| const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(content)); | ||
| return bytesToBase64(new Uint8Array(signature)); | ||
| }; | ||
| var verifyContent = async (secret, content, signature) => { | ||
| const key = await importKey(secret); | ||
| return crypto.subtle.verify("HMAC", key, base64ToBytes(signature), encoder.encode(content)); | ||
| }; | ||
| var rememberWebhookId = async (id, timestamp, now, store) => { | ||
| if (store === defaultReplayStore) { | ||
| pruneReplayCache(now); | ||
| } | ||
| const expiresAt = await store.get(id); | ||
| if (expiresAt && expiresAt > now) { | ||
| throw new BatchworkError("batchwork: webhook replay detected."); | ||
| } | ||
| await store.set(id, timestamp + TOLERANCE_SECONDS); | ||
| }; | ||
| var signWebhook = async (secret, id, body, timestampSeconds) => { | ||
| const timestamp = Math.floor(timestampSeconds).toString(); | ||
| const signature = await signContent(secret, `${id}.${timestamp}.${body}`); | ||
| return { | ||
| "webhook-id": id, | ||
| "webhook-signature": `${SIGNATURE_VERSION},${signature}`, | ||
| "webhook-timestamp": timestamp | ||
| }; | ||
| }; | ||
| var verifyWebhook = async (request, secret, options) => { | ||
| const id = request.headers.get("webhook-id"); | ||
| const timestamp = request.headers.get("webhook-timestamp"); | ||
| const signatureHeader = request.headers.get("webhook-signature"); | ||
| if (!(id && timestamp && signatureHeader)) { | ||
| throw new BatchworkError("batchwork: missing webhook signature headers."); | ||
| } | ||
| const seconds = Number(timestamp); | ||
| const now = Date.now() / 1000; | ||
| if (!Number.isFinite(seconds) || Math.abs(now - seconds) > TOLERANCE_SECONDS) { | ||
| throw new BatchworkError("batchwork: webhook timestamp outside tolerance."); | ||
| } | ||
| const body = await request.text(); | ||
| const content = `${id}.${timestamp}.${body}`; | ||
| const signatures = signatureHeader.split(" ").map((part) => { | ||
| const comma = part.indexOf(","); | ||
| return comma === -1 ? part : part.slice(comma + 1); | ||
| }); | ||
| let valid = false; | ||
| for (const signature of signatures) { | ||
| if (await verifyContent(secret, content, signature)) { | ||
| valid = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!valid) { | ||
| throw new BatchworkError("batchwork: webhook signature verification failed."); | ||
| } | ||
| await rememberWebhookId(id, seconds, now, options?.replayStore ?? defaultReplayStore); | ||
| return { body, id, timestamp: seconds }; | ||
| }; | ||
| var verifyBatchWebhook = async (request, secret, options) => { | ||
| const { body } = await verifyWebhook(request, secret, options); | ||
| return JSON.parse(body); | ||
| }; | ||
| // src/server/poller.ts | ||
| var parseIpv4 = (host) => { | ||
| if (!/^\d{1,3}(?:\.\d{1,3}){3}$/u.test(host)) { | ||
| return; | ||
| } | ||
| const parts = host.split(".").map(Number); | ||
| const valid = parts.every((part) => Number.isInteger(part) && part >= 0 && part <= 255); | ||
| return valid ? parts : undefined; | ||
| }; | ||
| var isPrivateIpv4 = (parts) => { | ||
| const [a = 0, b = 0] = parts; | ||
| return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224; | ||
| }; | ||
| var isPrivateIpv6 = (host) => { | ||
| const normalized = host.replace(/^\[/u, "").replace(/\]$/u, "").toLowerCase(); | ||
| return normalized === "::" || normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:"); | ||
| }; | ||
| var isRedirectStatus = (status) => status >= 300 && status < 400; | ||
| var assertSafeWebhookUrl = (url) => { | ||
| if (url.protocol !== "https:") { | ||
| throw new BatchworkError("batchwork: webhookUrl must use https."); | ||
| } | ||
| if (url.username || url.password) { | ||
| throw new BatchworkError("batchwork: webhookUrl must not include credentials."); | ||
| } | ||
| const host = url.hostname.toLowerCase(); | ||
| const ipv4 = parseIpv4(host); | ||
| if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || ipv4 && isPrivateIpv4(ipv4) || isPrivateIpv6(host)) { | ||
| throw new BatchworkError("batchwork: webhookUrl must not target localhost or private networks."); | ||
| } | ||
| }; | ||
| var validateWebhookUrl = async (rawUrl, validator) => { | ||
| let url; | ||
| try { | ||
| url = new URL(rawUrl); | ||
| } catch (error) { | ||
| throw new BatchworkError("batchwork: webhookUrl must be a valid URL.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| await validator(url); | ||
| return url.toString(); | ||
| }; | ||
| var createWebhookSink = (validator) => async (record, snapshot) => { | ||
| if (!record.webhookUrl) { | ||
| throw new BatchworkError("batchwork: tracked batch has no webhookUrl to deliver to."); | ||
| } | ||
| const webhookUrl = await validateWebhookUrl(record.webhookUrl, validator); | ||
| const body = JSON.stringify(toEvent(record.provider, snapshot)); | ||
| const headers = { | ||
| "content-type": "application/json" | ||
| }; | ||
| if (record.webhookSecret) { | ||
| Object.assign(headers, await signWebhook(record.webhookSecret, record.id, body, Date.now() / 1000)); | ||
| } | ||
| const response = await fetch(webhookUrl, { | ||
| body, | ||
| headers, | ||
| method: "POST", | ||
| redirect: "manual" | ||
| }); | ||
| if (isRedirectStatus(response.status)) { | ||
| throw new BatchworkError(`batchwork: webhook delivery to ${webhookUrl} redirected (${response.status}).`); | ||
| } | ||
| if (!response.ok) { | ||
| throw new BatchworkError(`batchwork: webhook delivery to ${webhookUrl} failed (${response.status}).`); | ||
| } | ||
| }; | ||
| var createBatchPoller = (options) => { | ||
| const resolveCredentials = (provider) => { | ||
| if (typeof options.credentials === "function") { | ||
| return options.credentials(provider); | ||
| } | ||
| return options.credentials ?? {}; | ||
| }; | ||
| const webhookUrlValidator = options.validateWebhookUrl ?? assertSafeWebhookUrl; | ||
| const sink = options.onComplete ?? createWebhookSink(webhookUrlValidator); | ||
| const deliver = async (record, snapshot) => { | ||
| await sink(record, snapshot); | ||
| await options.store.set({ | ||
| ...record, | ||
| deliveredAt: new Date().toISOString(), | ||
| status: snapshot.status | ||
| }); | ||
| }; | ||
| const track = async (target, opts) => { | ||
| const webhookUrl = opts.webhookUrl && !options.onComplete ? await validateWebhookUrl(opts.webhookUrl, webhookUrlValidator) : opts.webhookUrl; | ||
| const record = { | ||
| createdAt: new Date().toISOString(), | ||
| id: target.id, | ||
| provider: target.provider, | ||
| status: target.status ?? "in_progress", | ||
| webhookSecret: opts.secret, | ||
| webhookUrl | ||
| }; | ||
| await options.store.set(record); | ||
| return record; | ||
| }; | ||
| const process = async (record, delivered) => { | ||
| const adapter = getAdapter(record.provider); | ||
| const snapshot = await adapter.retrieve(record.id, resolveCredentials(record.provider)); | ||
| if (isTerminalStatus(snapshot.status)) { | ||
| await deliver(record, snapshot); | ||
| delivered.push(record.id); | ||
| } else if (snapshot.status !== record.status) { | ||
| await options.store.set({ ...record, status: snapshot.status }); | ||
| } | ||
| }; | ||
| const tick = async () => { | ||
| const pending = await options.store.list({ delivered: false }); | ||
| const delivered = []; | ||
| const failed = []; | ||
| for (const record of pending) { | ||
| try { | ||
| await process(record, delivered); | ||
| } catch (error) { | ||
| if (!options.onError) { | ||
| throw error; | ||
| } | ||
| options.onError(record, error); | ||
| failed.push({ | ||
| error: error instanceof Error ? error.message : String(error), | ||
| id: record.id | ||
| }); | ||
| } | ||
| } | ||
| return failed.length > 0 ? { checked: pending.length, delivered, failed } : { checked: pending.length, delivered }; | ||
| }; | ||
| const openaiWebhookHandler = (config) => async (request) => { | ||
| let verified; | ||
| try { | ||
| verified = await verifyWebhook(request, config.signingSecret); | ||
| } catch { | ||
| return new Response("invalid signature", { status: 400 }); | ||
| } | ||
| const payload = JSON.parse(verified.body); | ||
| if (!payload.type?.startsWith("batch.")) { | ||
| return new Response("ignored", { status: 202 }); | ||
| } | ||
| const id = payload.data?.id; | ||
| if (!id) { | ||
| return new Response("missing batch id", { status: 400 }); | ||
| } | ||
| const record = await options.store.get(id); | ||
| if (!record || record.deliveredAt) { | ||
| return new Response("ok", { status: 200 }); | ||
| } | ||
| const snapshot = await getAdapter("openai").retrieve(id, resolveCredentials("openai")); | ||
| if (isTerminalStatus(snapshot.status)) { | ||
| await deliver(record, snapshot); | ||
| } | ||
| return new Response("ok", { status: 200 }); | ||
| }; | ||
| return { deliver, openaiWebhookHandler, tick, track }; | ||
| }; | ||
| // src/server/store.ts | ||
| var createMemoryStore = () => { | ||
| const records = new Map; | ||
| return { | ||
| delete: (id) => { | ||
| records.delete(id); | ||
| return Promise.resolve(); | ||
| }, | ||
| get: (id) => Promise.resolve(records.get(id) ?? null), | ||
| list: (filter) => { | ||
| const all = [...records.values()]; | ||
| if (filter?.delivered === undefined) { | ||
| return Promise.resolve(all); | ||
| } | ||
| const { delivered } = filter; | ||
| return Promise.resolve(all.filter((record) => record.deliveredAt !== undefined === delivered)); | ||
| }, | ||
| set: (record) => { | ||
| records.set(record.id, record); | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| }; | ||
| export { toEvent, signWebhook, verifyWebhook, verifyBatchWebhook, createBatchPoller, createMemoryStore }; | ||
| //# debugId=14EA1AD74C5676A164756E2164756E21 | ||
| //# sourceMappingURL=chunk-e6qn48qa.js.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/server/events.ts", "../src/server/signing.ts", "../src/server/poller.ts", "../src/server/store.ts"], | ||
| "sourcesContent": [ | ||
| "import type { BatchProvider, BatchSnapshot, BatchStatus } from \"../types\";\nimport type { BatchWebhookEvent, BatchWebhookEventType } from \"./types\";\n\nconst EVENT_BY_STATUS: Partial<Record<BatchStatus, BatchWebhookEventType>> = {\n cancelled: \"batch.cancelled\",\n completed: \"batch.completed\",\n expired: \"batch.expired\",\n failed: \"batch.failed\",\n};\n\n/** Map a terminal snapshot to the unified webhook event batchwork delivers. */\nexport const toEvent = (\n provider: BatchProvider,\n snapshot: BatchSnapshot\n): BatchWebhookEvent => ({\n completedAt: snapshot.completedAt?.toISOString(),\n createdAt: snapshot.createdAt?.toISOString(),\n id: snapshot.id,\n provider,\n requestCounts: snapshot.requestCounts,\n type: EVENT_BY_STATUS[snapshot.status] ?? \"batch.completed\",\n});\n", | ||
| "import { BatchworkError } from \"../errors\";\nimport type { BatchWebhookEvent } from \"./types\";\n\n// Standard Webhooks-style HMAC-SHA256 signing, compatible with OpenAI's webhook\n// signatures (so the same verifier handles inbound OpenAI events and batchwork's\n// own outbound deliveries). Uses Web Crypto so it runs on edge runtimes.\n\nconst SIGNATURE_VERSION = \"v1\";\nconst TOLERANCE_SECONDS = 300;\nconst SECRET_PREFIX = \"whsec_\";\nconst encoder = new TextEncoder();\n\nexport interface WebhookReplayStore {\n get: (id: string) => number | Promise<number | undefined> | undefined;\n set: (id: string, expiresAt: number) => Promise<void> | void;\n}\n\nexport interface VerifyWebhookOptions {\n replayStore?: WebhookReplayStore;\n}\n\nconst replayCache = new Map<string, number>();\n\nconst defaultReplayStore: WebhookReplayStore = {\n get: (id) => replayCache.get(id),\n set: (id, expiresAt) => {\n replayCache.set(id, expiresAt);\n },\n};\n\nconst pruneReplayCache = (now: number): void => {\n for (const [id, expiresAt] of replayCache) {\n if (expiresAt <= now) {\n replayCache.delete(id);\n }\n }\n};\n\nconst base64ToBytes = (value: string): Uint8Array<ArrayBuffer> =>\n Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0);\n\nconst bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary);\n};\n\nconst importKey = (secret: string): Promise<CryptoKey> => {\n const raw = secret.startsWith(SECRET_PREFIX)\n ? base64ToBytes(secret.slice(SECRET_PREFIX.length))\n : encoder.encode(secret);\n return crypto.subtle.importKey(\n \"raw\",\n raw,\n { hash: \"SHA-256\", name: \"HMAC\" },\n false,\n [\"sign\", \"verify\"]\n );\n};\n\nconst signContent = async (\n secret: string,\n content: string\n): Promise<string> => {\n const key = await importKey(secret);\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n encoder.encode(content)\n );\n return bytesToBase64(new Uint8Array(signature));\n};\n\nconst verifyContent = async (\n secret: string,\n content: string,\n signature: string\n): Promise<boolean> => {\n const key = await importKey(secret);\n return crypto.subtle.verify(\n \"HMAC\",\n key,\n base64ToBytes(signature),\n encoder.encode(content)\n );\n};\n\nconst rememberWebhookId = async (\n id: string,\n timestamp: number,\n now: number,\n store: WebhookReplayStore\n): Promise<void> => {\n if (store === defaultReplayStore) {\n pruneReplayCache(now);\n }\n const expiresAt = await store.get(id);\n if (expiresAt && expiresAt > now) {\n throw new BatchworkError(\"batchwork: webhook replay detected.\");\n }\n await store.set(id, timestamp + TOLERANCE_SECONDS);\n};\n\n/** Build Standard Webhooks signature headers for an outbound delivery. */\nexport const signWebhook = async (\n secret: string,\n id: string,\n body: string,\n timestampSeconds: number\n): Promise<Record<string, string>> => {\n const timestamp = Math.floor(timestampSeconds).toString();\n const signature = await signContent(secret, `${id}.${timestamp}.${body}`);\n return {\n \"webhook-id\": id,\n \"webhook-signature\": `${SIGNATURE_VERSION},${signature}`,\n \"webhook-timestamp\": timestamp,\n };\n};\n\nexport interface VerifiedWebhook {\n id: string;\n timestamp: number;\n body: string;\n}\n\n/**\n * Verify a Standard Webhooks-signed request and return its raw body. Throws if\n * headers are missing, the timestamp is outside tolerance, or no signature\n * matches. Consumes the request body.\n */\nexport const verifyWebhook = async (\n request: Request,\n secret: string,\n options?: VerifyWebhookOptions\n): Promise<VerifiedWebhook> => {\n const id = request.headers.get(\"webhook-id\");\n const timestamp = request.headers.get(\"webhook-timestamp\");\n const signatureHeader = request.headers.get(\"webhook-signature\");\n if (!(id && timestamp && signatureHeader)) {\n throw new BatchworkError(\"batchwork: missing webhook signature headers.\");\n }\n\n const seconds = Number(timestamp);\n const now = Date.now() / 1000;\n if (\n !Number.isFinite(seconds) ||\n Math.abs(now - seconds) > TOLERANCE_SECONDS\n ) {\n throw new BatchworkError(\"batchwork: webhook timestamp outside tolerance.\");\n }\n\n const body = await request.text();\n const content = `${id}.${timestamp}.${body}`;\n const signatures = signatureHeader.split(\" \").map((part) => {\n const comma = part.indexOf(\",\");\n return comma === -1 ? part : part.slice(comma + 1);\n });\n\n let valid = false;\n for (const signature of signatures) {\n // oxlint-disable-next-line no-await-in-loop -- usually a single signature.\n if (await verifyContent(secret, content, signature)) {\n valid = true;\n break;\n }\n }\n if (!valid) {\n throw new BatchworkError(\n \"batchwork: webhook signature verification failed.\"\n );\n }\n\n await rememberWebhookId(\n id,\n seconds,\n now,\n options?.replayStore ?? defaultReplayStore\n );\n\n return { body, id, timestamp: seconds };\n};\n\n/**\n * Verify and parse a batchwork webhook on your receiving endpoint. Returns the\n * unified {@link BatchWebhookEvent}.\n */\nexport const verifyBatchWebhook = async (\n request: Request,\n secret: string,\n options?: VerifyWebhookOptions\n): Promise<BatchWebhookEvent> => {\n const { body } = await verifyWebhook(request, secret, options);\n return JSON.parse(body) as BatchWebhookEvent;\n};\n", | ||
| "import { BatchworkError } from \"../errors\";\nimport { isTerminalStatus } from \"../job\";\nimport { getAdapter } from \"../providers\";\nimport type {\n BatchProvider,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { toEvent } from \"./events\";\nimport { signWebhook, verifyWebhook } from \"./signing\";\nimport type { BatchStore, TrackedBatch } from \"./types\";\n\n/** Credentials for polling: a fixed config, or one resolved per provider. */\nexport type CredentialResolver =\n | ProviderCredentials\n | ((provider: BatchProvider) => ProviderCredentials);\n\n/**\n * Handles a batch reaching a terminal status. Replaces the default signed\n * webhook delivery — e.g. to invoke a callback instead (see `batchwork/next`).\n */\nexport type CompletionSink = (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n) => Promise<void>;\n\nexport type WebhookUrlValidator = (url: URL) => void | Promise<void>;\n\nexport interface BatchPollerOptions {\n store: BatchStore;\n /** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */\n credentials?: CredentialResolver;\n /** Replaces signed-webhook delivery when a batch finishes. */\n onComplete?: CompletionSink;\n /** Override the default webhook URL policy with an application allowlist. */\n validateWebhookUrl?: WebhookUrlValidator;\n /**\n * Called when processing a single batch throws during `tick`. When provided,\n * the tick reports the error and continues to the next batch; when omitted,\n * the error propagates out of `tick`.\n */\n onError?: (record: TrackedBatch, error: unknown) => void;\n}\n\nexport interface TrackTarget {\n id: string;\n provider: BatchProvider;\n status?: BatchStatus;\n}\n\nexport interface TrackOptions {\n /** Where to POST the completion webhook. Omit for callback-based delivery. */\n webhookUrl?: string;\n /** Signs the outbound webhook (Standard Webhooks HMAC) when provided. */\n secret?: string;\n}\n\nexport interface TickResult {\n checked: number;\n delivered: string[];\n /** Batches whose processing threw this tick (only when `onError` is set). */\n failed?: { id: string; error: string }[];\n}\n\nexport interface OpenAIWebhookOptions {\n /** The OpenAI webhook signing secret (`whsec_…`). */\n signingSecret: string;\n}\n\nexport interface BatchPoller {\n track: (target: TrackTarget, options: TrackOptions) => Promise<TrackedBatch>;\n tick: () => Promise<TickResult>;\n deliver: (record: TrackedBatch, snapshot: BatchSnapshot) => Promise<void>;\n openaiWebhookHandler: (\n options: OpenAIWebhookOptions\n ) => (request: Request) => Promise<Response>;\n}\n\nconst parseIpv4 = (host: string): number[] | undefined => {\n if (!/^\\d{1,3}(?:\\.\\d{1,3}){3}$/u.test(host)) {\n return;\n }\n const parts = host.split(\".\").map(Number);\n const valid = parts.every(\n (part) => Number.isInteger(part) && part >= 0 && part <= 255\n );\n return valid ? parts : undefined;\n};\n\nconst isPrivateIpv4 = (parts: number[]): boolean => {\n const [a = 0, b = 0] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n};\n\nconst isPrivateIpv6 = (host: string): boolean => {\n const normalized = host.replace(/^\\[/u, \"\").replace(/\\]$/u, \"\").toLowerCase();\n return (\n normalized === \"::\" ||\n normalized === \"::1\" ||\n normalized.startsWith(\"fc\") ||\n normalized.startsWith(\"fd\") ||\n normalized.startsWith(\"fe80:\")\n );\n};\n\nconst isRedirectStatus = (status: number): boolean =>\n status >= 300 && status < 400;\n\nconst assertSafeWebhookUrl: WebhookUrlValidator = (url) => {\n if (url.protocol !== \"https:\") {\n throw new BatchworkError(\"batchwork: webhookUrl must use https.\");\n }\n if (url.username || url.password) {\n throw new BatchworkError(\n \"batchwork: webhookUrl must not include credentials.\"\n );\n }\n\n const host = url.hostname.toLowerCase();\n const ipv4 = parseIpv4(host);\n if (\n host === \"localhost\" ||\n host.endsWith(\".localhost\") ||\n host.endsWith(\".local\") ||\n (ipv4 && isPrivateIpv4(ipv4)) ||\n isPrivateIpv6(host)\n ) {\n throw new BatchworkError(\n \"batchwork: webhookUrl must not target localhost or private networks.\"\n );\n }\n};\n\nconst validateWebhookUrl = async (\n rawUrl: string,\n validator: WebhookUrlValidator\n): Promise<string> => {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch (error) {\n throw new BatchworkError(\"batchwork: webhookUrl must be a valid URL.\", {\n cause: error,\n });\n }\n await validator(url);\n return url.toString();\n};\n\n/** The default completion sink: POST a signed webhook to the tracked URL. */\nconst createWebhookSink =\n (validator: WebhookUrlValidator): CompletionSink =>\n async (record, snapshot) => {\n if (!record.webhookUrl) {\n throw new BatchworkError(\n \"batchwork: tracked batch has no webhookUrl to deliver to.\"\n );\n }\n const webhookUrl = await validateWebhookUrl(record.webhookUrl, validator);\n const body = JSON.stringify(toEvent(record.provider, snapshot));\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (record.webhookSecret) {\n Object.assign(\n headers,\n await signWebhook(\n record.webhookSecret,\n record.id,\n body,\n Date.now() / 1000\n )\n );\n }\n const response = await fetch(webhookUrl, {\n body,\n headers,\n method: \"POST\",\n redirect: \"manual\",\n });\n if (isRedirectStatus(response.status)) {\n throw new BatchworkError(\n `batchwork: webhook delivery to ${webhookUrl} redirected (${response.status}).`\n );\n }\n if (!response.ok) {\n throw new BatchworkError(\n `batchwork: webhook delivery to ${webhookUrl} failed (${response.status}).`\n );\n }\n };\n\n/**\n * Create a managed poller: register submitted batches with `track`, then run\n * `tick` on a schedule (cron) to poll open batches and deliver one unified,\n * signed webhook per batch when it finishes. For OpenAI, mount\n * `openaiWebhookHandler` to skip polling and react to native webhooks instead.\n */\nexport const createBatchPoller = (options: BatchPollerOptions): BatchPoller => {\n const resolveCredentials = (provider: BatchProvider): ProviderCredentials => {\n if (typeof options.credentials === \"function\") {\n return options.credentials(provider);\n }\n return options.credentials ?? {};\n };\n\n const webhookUrlValidator =\n options.validateWebhookUrl ?? assertSafeWebhookUrl;\n const sink = options.onComplete ?? createWebhookSink(webhookUrlValidator);\n\n const deliver = async (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n ): Promise<void> => {\n // Run the side effect before marking delivered: if it throws, the record\n // stays pending and is retried on the next tick (at-least-once delivery).\n await sink(record, snapshot);\n await options.store.set({\n ...record,\n deliveredAt: new Date().toISOString(),\n status: snapshot.status,\n });\n };\n\n const track = async (\n target: TrackTarget,\n opts: TrackOptions\n ): Promise<TrackedBatch> => {\n const webhookUrl =\n opts.webhookUrl && !options.onComplete\n ? await validateWebhookUrl(opts.webhookUrl, webhookUrlValidator)\n : opts.webhookUrl;\n const record: TrackedBatch = {\n createdAt: new Date().toISOString(),\n id: target.id,\n provider: target.provider,\n status: target.status ?? \"in_progress\",\n webhookSecret: opts.secret,\n webhookUrl,\n };\n await options.store.set(record);\n return record;\n };\n\n const process = async (\n record: TrackedBatch,\n delivered: string[]\n ): Promise<void> => {\n const adapter = getAdapter(record.provider);\n const snapshot = await adapter.retrieve(\n record.id,\n resolveCredentials(record.provider)\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n delivered.push(record.id);\n } else if (snapshot.status !== record.status) {\n await options.store.set({ ...record, status: snapshot.status });\n }\n };\n\n const tick = async (): Promise<TickResult> => {\n const pending = await options.store.list({ delivered: false });\n const delivered: string[] = [];\n const failed: { id: string; error: string }[] = [];\n for (const record of pending) {\n // oxlint-disable-next-line no-await-in-loop -- batches are polled serially\n // to avoid hammering provider rate limits; deliver before the next.\n try {\n // oxlint-disable-next-line no-await-in-loop -- see above.\n await process(record, delivered);\n } catch (error) {\n // Without an `onError` handler, preserve the propagate-the-throw\n // behavior; with one, report and continue so a single failing batch\n // can't starve the rest of the queue. Either way the record stays\n // pending (it was never marked delivered) and retries next tick.\n if (!options.onError) {\n throw error;\n }\n options.onError(record, error);\n failed.push({\n error: error instanceof Error ? error.message : String(error),\n id: record.id,\n });\n }\n }\n return failed.length > 0\n ? { checked: pending.length, delivered, failed }\n : { checked: pending.length, delivered };\n };\n\n const openaiWebhookHandler =\n (config: OpenAIWebhookOptions) =>\n async (request: Request): Promise<Response> => {\n let verified: { body: string };\n try {\n verified = await verifyWebhook(request, config.signingSecret);\n } catch {\n return new Response(\"invalid signature\", { status: 400 });\n }\n\n const payload = JSON.parse(verified.body) as {\n type?: string;\n data?: { id?: string };\n };\n if (!payload.type?.startsWith(\"batch.\")) {\n return new Response(\"ignored\", { status: 202 });\n }\n const id = payload.data?.id;\n if (!id) {\n return new Response(\"missing batch id\", { status: 400 });\n }\n const record = await options.store.get(id);\n if (!record || record.deliveredAt) {\n return new Response(\"ok\", { status: 200 });\n }\n const snapshot = await getAdapter(\"openai\").retrieve(\n id,\n resolveCredentials(\"openai\")\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n }\n return new Response(\"ok\", { status: 200 });\n };\n\n return { deliver, openaiWebhookHandler, tick, track };\n};\n", | ||
| "import type { BatchStore, TrackedBatch } from \"./types\";\n\n/** An in-memory `BatchStore`. Suitable for development and single-process use. */\nexport const createMemoryStore = (): BatchStore => {\n const records = new Map<string, TrackedBatch>();\n\n return {\n delete: (id) => {\n records.delete(id);\n return Promise.resolve();\n },\n get: (id) => Promise.resolve(records.get(id) ?? null),\n list: (filter) => {\n const all = [...records.values()];\n if (filter?.delivered === undefined) {\n return Promise.resolve(all);\n }\n const { delivered } = filter;\n return Promise.resolve(\n all.filter((record) => (record.deliveredAt !== undefined) === delivered)\n );\n },\n set: (record) => {\n records.set(record.id, record);\n return Promise.resolve();\n },\n };\n};\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "14EA1AD74C5676A164756E2164756E21", | ||
| "names": [] | ||
| } |
| // src/errors.ts | ||
| class BatchworkError extends Error { | ||
| constructor(message, options) { | ||
| super(message, options); | ||
| this.name = "BatchworkError"; | ||
| } | ||
| } | ||
| class UnsupportedProviderError extends BatchworkError { | ||
| provider; | ||
| constructor(provider) { | ||
| super(`batchwork: provider "${provider}" is not supported yet. Supported providers: openai, anthropic, google, groq, mistral, together, xai.`); | ||
| this.name = "UnsupportedProviderError"; | ||
| this.provider = provider; | ||
| } | ||
| } | ||
| class MissingDependencyError extends BatchworkError { | ||
| constructor(pkg, provider) { | ||
| super(`batchwork: install \`${pkg}\` to batch ${provider} models (\`npm install ${pkg}\`).`); | ||
| this.name = "MissingDependencyError"; | ||
| } | ||
| } | ||
| // src/job.ts | ||
| var DEFAULT_POLL_INTERVAL_MS = 15000; | ||
| var TERMINAL_STATUSES = new Set([ | ||
| "completed", | ||
| "failed", | ||
| "expired", | ||
| "cancelled" | ||
| ]); | ||
| var isTerminalStatus = (status) => TERMINAL_STATUSES.has(status); | ||
| var delay = (ms, signal) => new Promise((resolve, reject) => { | ||
| if (signal?.aborted) { | ||
| reject(new BatchworkError("batchwork: wait aborted.")); | ||
| return; | ||
| } | ||
| const timer = setTimeout(resolve, ms); | ||
| signal?.addEventListener("abort", () => { | ||
| clearTimeout(timer); | ||
| reject(new BatchworkError("batchwork: wait aborted.")); | ||
| }, { once: true }); | ||
| }); | ||
| class BatchJob { | ||
| provider; | ||
| id; | ||
| #adapter; | ||
| #credentials; | ||
| #snapshot; | ||
| constructor(adapter, credentials, snapshot) { | ||
| this.#adapter = adapter; | ||
| this.#credentials = credentials; | ||
| this.#snapshot = snapshot; | ||
| this.id = snapshot.id; | ||
| this.provider = snapshot.provider; | ||
| } | ||
| get status() { | ||
| return this.#snapshot.status; | ||
| } | ||
| get requestCounts() { | ||
| return this.#snapshot.requestCounts; | ||
| } | ||
| get snapshot() { | ||
| return this.#snapshot; | ||
| } | ||
| async poll() { | ||
| this.#snapshot = await this.#adapter.retrieve(this.id, this.#credentials); | ||
| return this.#snapshot; | ||
| } | ||
| async wait(options = {}) { | ||
| const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS; | ||
| const deadline = options.timeoutMs ? Date.now() + options.timeoutMs : undefined; | ||
| let snapshot = await this.poll(); | ||
| options.onPoll?.(snapshot); | ||
| while (!isTerminalStatus(snapshot.status)) { | ||
| if (options.signal?.aborted) { | ||
| throw new BatchworkError("batchwork: wait aborted."); | ||
| } | ||
| if (deadline !== undefined && Date.now() > deadline) { | ||
| throw new BatchworkError(`batchwork: timed out waiting for batch "${this.id}".`); | ||
| } | ||
| await delay(interval, options.signal); | ||
| snapshot = await this.poll(); | ||
| options.onPoll?.(snapshot); | ||
| } | ||
| return snapshot; | ||
| } | ||
| results() { | ||
| return this.#adapter.results(this.id, this.#credentials); | ||
| } | ||
| async collect() { | ||
| const out = []; | ||
| for await (const result of this.results()) { | ||
| out.push(result); | ||
| } | ||
| return out; | ||
| } | ||
| async cancel() { | ||
| await this.#adapter.cancel(this.id, this.#credentials); | ||
| return await this.poll(); | ||
| } | ||
| } | ||
| // src/limits.ts | ||
| var DEFAULT_LIMITS = { | ||
| captureConcurrency: 16, | ||
| maxRequestBytes: 20 * 1024 * 1024, | ||
| maxRequests: 50000, | ||
| maxUploadBytes: 200 * 1024 * 1024 | ||
| }; | ||
| var encoder = new TextEncoder; | ||
| var positiveInteger = (name, value) => { | ||
| if (!(Number.isInteger(value) && value > 0)) { | ||
| throw new BatchworkError(`batchwork: limits.${name} must be a positive integer.`); | ||
| } | ||
| return value; | ||
| }; | ||
| var resolveBatchLimits = (limits) => ({ | ||
| captureConcurrency: positiveInteger("captureConcurrency", limits?.captureConcurrency ?? DEFAULT_LIMITS.captureConcurrency), | ||
| maxRequestBytes: positiveInteger("maxRequestBytes", limits?.maxRequestBytes ?? DEFAULT_LIMITS.maxRequestBytes), | ||
| maxRequests: positiveInteger("maxRequests", limits?.maxRequests ?? DEFAULT_LIMITS.maxRequests), | ||
| maxUploadBytes: positiveInteger("maxUploadBytes", limits?.maxUploadBytes ?? DEFAULT_LIMITS.maxUploadBytes) | ||
| }); | ||
| var byteLength = (value) => encoder.encode(value).length; | ||
| var assertByteLength = (label, value, maxBytes) => { | ||
| const bytes = byteLength(value); | ||
| if (bytes > maxBytes) { | ||
| throw new BatchworkError(`batchwork: ${label} is ${bytes} bytes, exceeding the ${maxBytes} byte limit.`); | ||
| } | ||
| }; | ||
| var mapWithConcurrency = async (items, concurrency, mapper) => { | ||
| const results = []; | ||
| results.length = items.length; | ||
| let nextIndex = 0; | ||
| const workerCount = Math.min(concurrency, items.length); | ||
| const runNext = async () => { | ||
| const index = nextIndex; | ||
| nextIndex += 1; | ||
| if (index >= items.length) { | ||
| return; | ||
| } | ||
| results[index] = await mapper(items[index]); | ||
| await runNext(); | ||
| }; | ||
| await Promise.all(Array.from({ length: workerCount }, () => runNext())); | ||
| return results; | ||
| }; | ||
| // src/http.ts | ||
| var assertOk = (url, init, response) => { | ||
| if (!response.ok) { | ||
| throw new BatchworkError(`batchwork: ${init.method ?? "GET"} ${url} failed with ${response.status}.`); | ||
| } | ||
| }; | ||
| var requestJson = async (url, init) => { | ||
| const response = await fetch(url, init); | ||
| assertOk(url, init, response); | ||
| return await response.json(); | ||
| }; | ||
| var requestStream = async (url, init) => { | ||
| const response = await fetch(url, init); | ||
| assertOk(url, init, response); | ||
| if (!response.body) { | ||
| throw new BatchworkError(`batchwork: ${url} returned an empty body.`); | ||
| } | ||
| return response.body; | ||
| }; | ||
| // src/jsonl.ts | ||
| var NEWLINE = ` | ||
| `; | ||
| var DEFAULT_MAX_JSONL_LINE_BYTES = 20 * 1024 * 1024; | ||
| var resolveMaxLineBytes = (options) => { | ||
| const maxLineBytes = options?.maxLineBytes ?? DEFAULT_MAX_JSONL_LINE_BYTES; | ||
| if (!(Number.isInteger(maxLineBytes) && maxLineBytes > 0)) { | ||
| throw new BatchworkError("batchwork: JSONL maxLineBytes must be a positive integer."); | ||
| } | ||
| return maxLineBytes; | ||
| }; | ||
| var assertLineSize = (line, lineNumber, maxLineBytes) => { | ||
| const bytes = byteLength(line); | ||
| if (bytes > maxLineBytes) { | ||
| throw new BatchworkError(`batchwork: JSONL line ${lineNumber} is ${bytes} bytes, exceeding the ${maxLineBytes} byte limit.`); | ||
| } | ||
| }; | ||
| var parseLine = (line, lineNumber, maxLineBytes) => { | ||
| assertLineSize(line, lineNumber, maxLineBytes); | ||
| const trimmed = line.trim(); | ||
| if (trimmed.length === 0) { | ||
| return; | ||
| } | ||
| try { | ||
| return JSON.parse(trimmed); | ||
| } catch (error) { | ||
| throw new BatchworkError(`batchwork: invalid JSONL at line ${lineNumber}.`, { cause: error }); | ||
| } | ||
| }; | ||
| var encodeJsonl = (items) => { | ||
| if (items.length === 0) { | ||
| return ""; | ||
| } | ||
| const body = items.map((item) => JSON.stringify(item)).join(NEWLINE); | ||
| return `${body}${NEWLINE}`; | ||
| }; | ||
| var isReadableStream = (source) => ("getReader" in source) && typeof source.getReader === "function"; | ||
| async function* toByteIterable(source) { | ||
| if (isReadableStream(source)) { | ||
| const reader = source.getReader(); | ||
| try { | ||
| let chunk = await reader.read(); | ||
| while (!chunk.done) { | ||
| if (chunk.value) { | ||
| yield chunk.value; | ||
| } | ||
| chunk = await reader.read(); | ||
| } | ||
| } finally { | ||
| reader.releaseLock(); | ||
| } | ||
| return; | ||
| } | ||
| yield* source; | ||
| } | ||
| async function* streamJsonl(source, options) { | ||
| const decoder = new TextDecoder; | ||
| const maxLineBytes = resolveMaxLineBytes(options); | ||
| let buffer = ""; | ||
| let lineNumber = 1; | ||
| for await (const chunk of toByteIterable(source)) { | ||
| buffer += decoder.decode(chunk, { stream: true }); | ||
| let newlineIndex = buffer.indexOf(NEWLINE); | ||
| while (newlineIndex !== -1) { | ||
| const line = buffer.slice(0, newlineIndex); | ||
| buffer = buffer.slice(newlineIndex + 1); | ||
| const parsed2 = parseLine(line, lineNumber, maxLineBytes); | ||
| if (parsed2 !== undefined) { | ||
| yield parsed2; | ||
| } | ||
| lineNumber += 1; | ||
| newlineIndex = buffer.indexOf(NEWLINE); | ||
| } | ||
| assertLineSize(buffer, lineNumber, maxLineBytes); | ||
| } | ||
| buffer += decoder.decode(); | ||
| const parsed = parseLine(buffer, lineNumber, maxLineBytes); | ||
| if (parsed !== undefined) { | ||
| yield parsed; | ||
| } | ||
| } | ||
| // src/util.ts | ||
| var asRecord = (value) => { | ||
| if (typeof value === "object" && value !== null) { | ||
| return value; | ||
| } | ||
| return {}; | ||
| }; | ||
| var asString = (value) => typeof value === "string" ? value : undefined; | ||
| var asNumber = (value) => typeof value === "number" ? value : undefined; | ||
| var asArray = (value) => Array.isArray(value) ? value : []; | ||
| var omit = (obj, key) => { | ||
| const result = {}; | ||
| for (const [k, v] of Object.entries(obj)) { | ||
| if (k !== key) { | ||
| result[k] = v; | ||
| } | ||
| } | ||
| return result; | ||
| }; | ||
| var toDate = (value) => { | ||
| if (typeof value === "string") { | ||
| return new Date(value); | ||
| } | ||
| if (typeof value === "number") { | ||
| return new Date(value * 1000); | ||
| } | ||
| }; | ||
| // src/providers/ids.ts | ||
| var SIMPLE_PROVIDER_ID = /^[A-Za-z0-9_-]+$/u; | ||
| var assertSimpleProviderId = (label, id) => { | ||
| if (!SIMPLE_PROVIDER_ID.test(id)) { | ||
| throw new BatchworkError(`batchwork: invalid ${label}.`); | ||
| } | ||
| return id; | ||
| }; | ||
| var assertPrefixedProviderId = (label, id, prefix) => { | ||
| const [actualPrefix, value, ...rest] = id.split("/"); | ||
| if (rest.length > 0 || actualPrefix !== prefix || !value || !SIMPLE_PROVIDER_ID.test(value)) { | ||
| throw new BatchworkError(`batchwork: invalid ${label}.`); | ||
| } | ||
| return id; | ||
| }; | ||
| // src/providers/anthropic.ts | ||
| var ANTHROPIC_BASE = "https://api.anthropic.com"; | ||
| var ANTHROPIC_VERSION = "2023-06-01"; | ||
| var apiKey = (credentials) => { | ||
| const key = credentials.apiKey ?? process.env.ANTHROPIC_API_KEY; | ||
| if (!key) { | ||
| throw new BatchworkError("batchwork: missing Anthropic API key. Set ANTHROPIC_API_KEY or pass `apiKey`."); | ||
| } | ||
| return key; | ||
| }; | ||
| var baseUrl = (credentials) => credentials.baseURL ?? ANTHROPIC_BASE; | ||
| var validateResultsUrl = (rawUrl, credentials) => { | ||
| let resultsUrl; | ||
| let expectedBase; | ||
| try { | ||
| resultsUrl = new URL(rawUrl); | ||
| expectedBase = new URL(baseUrl(credentials)); | ||
| } catch (error) { | ||
| throw new BatchworkError("batchwork: invalid Anthropic results_url.", { | ||
| cause: error | ||
| }); | ||
| } | ||
| if (resultsUrl.origin !== expectedBase.origin) { | ||
| throw new BatchworkError("batchwork: Anthropic results_url must match the configured API origin."); | ||
| } | ||
| if (resultsUrl.username || resultsUrl.password) { | ||
| throw new BatchworkError("batchwork: Anthropic results_url must not include credentials."); | ||
| } | ||
| return resultsUrl.toString(); | ||
| }; | ||
| var headers = (credentials) => ({ | ||
| "anthropic-version": ANTHROPIC_VERSION, | ||
| "content-type": "application/json", | ||
| "x-api-key": apiKey(credentials), | ||
| ...credentials.headers | ||
| }); | ||
| var mapStatus = (status) => { | ||
| if (status === "ended") { | ||
| return "completed"; | ||
| } | ||
| if (status === "canceling") { | ||
| return "cancelling"; | ||
| } | ||
| return "in_progress"; | ||
| }; | ||
| var normalizeSnapshot = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const counts = asRecord(obj.request_counts); | ||
| const succeeded = asNumber(counts.succeeded) ?? 0; | ||
| const errored = asNumber(counts.errored) ?? 0; | ||
| const processing = asNumber(counts.processing) ?? 0; | ||
| const canceled = asNumber(counts.canceled) ?? 0; | ||
| const expired = asNumber(counts.expired) ?? 0; | ||
| return { | ||
| completedAt: toDate(obj.ended_at), | ||
| createdAt: toDate(obj.created_at), | ||
| expiresAt: toDate(obj.expires_at), | ||
| id: asString(obj.id) ?? "", | ||
| provider: "anthropic", | ||
| raw, | ||
| requestCounts: { | ||
| canceled, | ||
| completed: succeeded, | ||
| expired, | ||
| failed: errored, | ||
| processing, | ||
| total: succeeded + errored + processing + canceled + expired | ||
| }, | ||
| status: mapStatus(asString(obj.processing_status)) | ||
| }; | ||
| }; | ||
| var textFromMessage = (message) => { | ||
| const text = asArray(asRecord(message).content).map((block) => asRecord(block)).filter((block) => block.type === "text").map((block) => asString(block.text) ?? "").join(""); | ||
| return text.length > 0 ? text : undefined; | ||
| }; | ||
| var usageFromMessage = (message) => { | ||
| const usage = asRecord(asRecord(message).usage); | ||
| const inputTokens = asNumber(usage.input_tokens); | ||
| const outputTokens = asNumber(usage.output_tokens); | ||
| if (inputTokens === undefined && outputTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var normalizeResult = (line) => { | ||
| const obj = asRecord(line); | ||
| const customId = asString(obj.custom_id) ?? ""; | ||
| const result = asRecord(obj.result); | ||
| const type = asString(result.type); | ||
| if (type === "succeeded") { | ||
| return { | ||
| customId, | ||
| response: result.message, | ||
| status: "succeeded", | ||
| text: textFromMessage(result.message), | ||
| usage: usageFromMessage(result.message) | ||
| }; | ||
| } | ||
| if (type === "errored") { | ||
| const error = asRecord(result.error); | ||
| const nested = asRecord(error.error); | ||
| const source = Object.keys(nested).length > 0 ? nested : error; | ||
| return { | ||
| customId, | ||
| error: { | ||
| message: asString(source.message) ?? "Request errored.", | ||
| type: asString(source.type) | ||
| }, | ||
| response: result.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| if (type === "expired") { | ||
| return { customId, status: "expired" }; | ||
| } | ||
| return { customId, status: "canceled" }; | ||
| }; | ||
| var submit = async (input) => { | ||
| const limits = resolveBatchLimits(input.limits); | ||
| const requests = input.built.map((item) => ({ | ||
| custom_id: item.customId, | ||
| params: omit(item.body, "stream") | ||
| })); | ||
| const body = JSON.stringify({ requests }); | ||
| assertByteLength("batch upload payload", body, limits.maxUploadBytes); | ||
| const raw = await requestJson(`${baseUrl(input.credentials)}/v1/messages/batches`, { | ||
| body, | ||
| headers: headers(input.credentials), | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot(raw); | ||
| }; | ||
| var retrieve = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId("Anthropic batch id", id); | ||
| const raw = await requestJson(`${baseUrl(credentials)}/v1/messages/batches/${batchId}`, { headers: headers(credentials) }); | ||
| return normalizeSnapshot(raw); | ||
| }; | ||
| async function* results(id, credentials) { | ||
| const snapshot = await retrieve(id, credentials); | ||
| const resultsUrl = asString(asRecord(snapshot.raw).results_url); | ||
| if (!resultsUrl) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| const stream = await requestStream(validateResultsUrl(resultsUrl, credentials), { | ||
| headers: headers(credentials) | ||
| }); | ||
| for await (const line of streamJsonl(stream)) { | ||
| yield normalizeResult(line); | ||
| } | ||
| } | ||
| var cancel = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId("Anthropic batch id", id); | ||
| await requestJson(`${baseUrl(credentials)}/v1/messages/batches/${batchId}/cancel`, { | ||
| headers: headers(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var anthropicAdapter = { | ||
| cancel, | ||
| id: "anthropic", | ||
| results, | ||
| retrieve, | ||
| submit | ||
| }; | ||
| // src/providers/google.ts | ||
| var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta"; | ||
| var GOOGLE_BATCH_PREFIX = "batches"; | ||
| var apiKey2 = (credentials) => { | ||
| const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY; | ||
| if (!key) { | ||
| throw new BatchworkError("batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`."); | ||
| } | ||
| return key; | ||
| }; | ||
| var baseUrl2 = (credentials) => credentials.baseURL ?? GOOGLE_BASE; | ||
| var headers2 = (credentials) => ({ | ||
| "content-type": "application/json", | ||
| "x-goog-api-key": apiKey2(credentials), | ||
| ...credentials.headers | ||
| }); | ||
| var mapState = (state, done) => { | ||
| if (state) { | ||
| if (state.endsWith("SUCCEEDED")) { | ||
| return "completed"; | ||
| } | ||
| if (state.endsWith("FAILED")) { | ||
| return "failed"; | ||
| } | ||
| if (state.endsWith("CANCELLED")) { | ||
| return "cancelled"; | ||
| } | ||
| if (state.endsWith("EXPIRED")) { | ||
| return "expired"; | ||
| } | ||
| if (state.endsWith("PENDING")) { | ||
| return "validating"; | ||
| } | ||
| if (state.endsWith("RUNNING")) { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| return done ? "completed" : "in_progress"; | ||
| }; | ||
| var inlinedResponses = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const response = asRecord(obj.response); | ||
| const dest = asRecord(obj.dest); | ||
| const responseInline = response.inlinedResponses ?? response.inlined_responses; | ||
| const destInline = dest.inlinedResponses ?? dest.inlined_responses; | ||
| const nestedResponseInline = asRecord(responseInline); | ||
| const nestedDestInline = asRecord(destInline); | ||
| return [ | ||
| ...asArray(responseInline), | ||
| ...asArray(nestedResponseInline.inlinedResponses), | ||
| ...asArray(nestedResponseInline.inlined_responses), | ||
| ...asArray(destInline), | ||
| ...asArray(nestedDestInline.inlinedResponses), | ||
| ...asArray(nestedDestInline.inlined_responses) | ||
| ]; | ||
| }; | ||
| var normalizeSnapshot2 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const items = inlinedResponses(raw); | ||
| const failed = items.filter((item) => asRecord(item).error).length; | ||
| const id = asString(obj.name) ?? ""; | ||
| return { | ||
| id: id ? assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX) : "", | ||
| provider: "google", | ||
| raw, | ||
| requestCounts: { | ||
| completed: items.length - failed, | ||
| failed, | ||
| total: items.length | ||
| }, | ||
| status: mapState(asString(obj.state) ?? asString(asRecord(obj.state).name) ?? asString(asRecord(obj.metadata).state), obj.done === true) | ||
| }; | ||
| }; | ||
| var textFromResponse = (response) => { | ||
| const candidate = asRecord(asArray(asRecord(response).candidates)[0]); | ||
| const text = asArray(asRecord(candidate.content).parts).map((part) => asString(asRecord(part).text) ?? "").join(""); | ||
| return text.length > 0 ? text : undefined; | ||
| }; | ||
| var usageFromResponse = (response) => { | ||
| const usage = asRecord(asRecord(response).usageMetadata); | ||
| const inputTokens = asNumber(usage.promptTokenCount); | ||
| const outputTokens = asNumber(usage.candidatesTokenCount); | ||
| const totalTokens = asNumber(usage.totalTokenCount); | ||
| if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var normalizeResult2 = (item) => { | ||
| const obj = asRecord(item); | ||
| const customId = asString(asRecord(obj.metadata).key) ?? asString(obj.key) ?? asString(obj.custom_id) ?? ""; | ||
| if (obj.error) { | ||
| const error = asRecord(obj.error); | ||
| return { | ||
| customId, | ||
| error: { | ||
| code: asNumber(error.code) ?? asString(error.code), | ||
| message: asString(error.message) ?? "Request errored.", | ||
| type: asString(error.status) | ||
| }, | ||
| response: obj.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| return { | ||
| customId, | ||
| response: obj.response, | ||
| status: "succeeded", | ||
| text: textFromResponse(obj.response), | ||
| usage: usageFromResponse(obj.response) | ||
| }; | ||
| }; | ||
| var submit2 = async (input) => { | ||
| const limits = resolveBatchLimits(input.limits); | ||
| const requests = input.built.map((item) => ({ | ||
| metadata: { key: item.customId }, | ||
| request: omit(item.body, "stream") | ||
| })); | ||
| const body = JSON.stringify({ | ||
| batch: { | ||
| display_name: "batchwork", | ||
| input_config: { requests: { requests } } | ||
| } | ||
| }); | ||
| assertByteLength("batch upload payload", body, limits.maxUploadBytes); | ||
| const raw = await requestJson(`${baseUrl2(input.credentials)}/models/${input.modelId}:batchGenerateContent`, { | ||
| body, | ||
| headers: headers2(input.credentials), | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot2(raw); | ||
| }; | ||
| var retrieve2 = async (id, credentials) => { | ||
| const operationId = assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX); | ||
| const raw = await requestJson(`${baseUrl2(credentials)}/${operationId}`, { | ||
| headers: headers2(credentials) | ||
| }); | ||
| return normalizeSnapshot2(raw); | ||
| }; | ||
| async function* results2(id, credentials) { | ||
| const snapshot = await retrieve2(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const response = asRecord(raw.response); | ||
| const dest = asRecord(raw.dest); | ||
| const responsesFile = asString(asRecord(response.responsesFile).name) ?? asString(response.responsesFile) ?? asString(asRecord(response.responses_file).name) ?? asString(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name); | ||
| if (responsesFile) { | ||
| throw new BatchworkError(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`); | ||
| } | ||
| const items = inlinedResponses(raw); | ||
| if (items.length === 0) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| for (const item of items) { | ||
| yield normalizeResult2(item); | ||
| } | ||
| } | ||
| var cancel2 = async (id, credentials) => { | ||
| const operationId = assertPrefixedProviderId("Google operation id", id, GOOGLE_BATCH_PREFIX); | ||
| await requestJson(`${baseUrl2(credentials)}/${operationId}:cancel`, { | ||
| headers: headers2(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var googleAdapter = { | ||
| cancel: cancel2, | ||
| id: "google", | ||
| results: results2, | ||
| retrieve: retrieve2, | ||
| submit: submit2 | ||
| }; | ||
| // src/providers/shared.ts | ||
| var HTTP_OK_MIN = 200; | ||
| var HTTP_OK_MAX = 300; | ||
| var resolveApiKey = (credentials, envVar, label) => { | ||
| const key = credentials.apiKey ?? process.env[envVar]; | ||
| if (!key) { | ||
| throw new BatchworkError(`batchwork: missing ${label} API key. Set ${envVar} or pass \`apiKey\`.`); | ||
| } | ||
| return key; | ||
| }; | ||
| var textFromBody = (body) => { | ||
| const obj = asRecord(body); | ||
| const choices = asArray(obj.choices); | ||
| if (choices.length > 0) { | ||
| const content = asString(asRecord(asRecord(choices[0]).message).content); | ||
| if (content) { | ||
| return content; | ||
| } | ||
| } | ||
| return asString(obj.output_text); | ||
| }; | ||
| var usageFromBody = (body) => { | ||
| const usage = asRecord(asRecord(body).usage); | ||
| const inputTokens = asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens); | ||
| const outputTokens = asNumber(usage.completion_tokens) ?? asNumber(usage.output_tokens); | ||
| const totalTokens = asNumber(usage.total_tokens); | ||
| if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var errorFromValue = (value, fallback) => { | ||
| const obj = asRecord(value); | ||
| const nested = asRecord(obj.error); | ||
| const source = nested.message ? nested : obj; | ||
| return { | ||
| code: asNumber(source.code) ?? asString(source.code), | ||
| message: asString(source.message) ?? fallback, | ||
| type: asString(source.type) | ||
| }; | ||
| }; | ||
| var normalizeOpenAIResult = (line) => { | ||
| const obj = asRecord(line); | ||
| const customId = asString(obj.custom_id) ?? ""; | ||
| if (obj.error) { | ||
| return { | ||
| customId, | ||
| error: errorFromValue(obj.error, "Request errored."), | ||
| response: obj.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| const response = asRecord(obj.response); | ||
| const statusCode = asNumber(response.status_code) ?? 0; | ||
| if (statusCode >= HTTP_OK_MIN && statusCode < HTTP_OK_MAX) { | ||
| return { | ||
| customId, | ||
| response: response.body, | ||
| status: "succeeded", | ||
| text: textFromBody(response.body), | ||
| usage: usageFromBody(response.body) | ||
| }; | ||
| } | ||
| return { | ||
| customId, | ||
| error: errorFromValue(response.body, `Request failed with status ${statusCode}.`), | ||
| response: response.body, | ||
| status: "errored" | ||
| }; | ||
| }; | ||
| var uploadInputFile = async (jsonl, baseUrl3, headers3, options = {}) => { | ||
| const form = new FormData; | ||
| const purpose = options.purpose === undefined ? "batch" : options.purpose; | ||
| if (purpose !== null) { | ||
| form.append("purpose", purpose); | ||
| } | ||
| form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl"); | ||
| const raw = await requestJson(`${baseUrl3}/files`, { | ||
| body: form, | ||
| headers: headers3, | ||
| method: "POST" | ||
| }); | ||
| return raw.id; | ||
| }; | ||
| async function* streamResultFile(fileId, baseUrl3, headers3) { | ||
| const stream = await requestStream(`${baseUrl3}/files/${fileId}/content`, { | ||
| headers: headers3 | ||
| }); | ||
| for await (const line of streamJsonl(stream)) { | ||
| yield normalizeOpenAIResult(line); | ||
| } | ||
| } | ||
| // src/providers/openai-compatible.ts | ||
| var DEFAULT_COMPLETION_WINDOW = "24h"; | ||
| var mapStatus2 = (status) => { | ||
| const normalized = status?.toLowerCase(); | ||
| switch (normalized) { | ||
| case "validating": | ||
| case "in_progress": | ||
| case "finalizing": | ||
| case "completed": | ||
| case "failed": | ||
| case "expired": | ||
| case "cancelling": | ||
| case "cancelled": { | ||
| return normalized; | ||
| } | ||
| default: { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| }; | ||
| var normalizeSnapshot3 = (raw, provider) => { | ||
| const outer = asRecord(raw); | ||
| const obj = asRecord(outer.job); | ||
| const source = Object.keys(obj).length > 0 ? obj : outer; | ||
| const counts = asRecord(source.request_counts); | ||
| return { | ||
| completedAt: toDate(source.completed_at), | ||
| createdAt: toDate(source.created_at), | ||
| expiresAt: toDate(source.expires_at), | ||
| id: asString(source.id) ?? "", | ||
| provider, | ||
| raw: source, | ||
| requestCounts: { | ||
| completed: asNumber(counts.completed) ?? 0, | ||
| failed: asNumber(counts.failed) ?? 0, | ||
| total: asNumber(counts.total) ?? 0 | ||
| }, | ||
| status: mapStatus2(asString(source.status)) | ||
| }; | ||
| }; | ||
| var createOpenAICompatibleAdapter = (config) => { | ||
| const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW; | ||
| const lineFormat = config.lineFormat ?? "method-url"; | ||
| const baseUrl3 = (credentials) => credentials.baseURL ?? config.baseUrl; | ||
| const authHeaders = (credentials) => ({ | ||
| Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`, | ||
| ...credentials.headers | ||
| }); | ||
| const submit3 = async (input) => { | ||
| const limits = resolveBatchLimits(input.limits); | ||
| const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint; | ||
| const jsonl = encodeJsonl(input.built.map((item) => { | ||
| const body = omit(item.body, "stream"); | ||
| if (lineFormat === "body-only") { | ||
| return { body, custom_id: item.customId }; | ||
| } | ||
| return { | ||
| body, | ||
| custom_id: item.customId, | ||
| method: "POST", | ||
| url: endpoint | ||
| }; | ||
| })); | ||
| assertByteLength("batch upload JSONL", jsonl, limits.maxUploadBytes); | ||
| const headers3 = authHeaders(input.credentials); | ||
| const url = baseUrl3(input.credentials); | ||
| const purpose = config.filePurpose ?? "batch"; | ||
| const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers3, jsonl, purpose }) : uploadInputFile(jsonl, url, headers3, { purpose })); | ||
| const raw = await requestJson(`${url}/batches`, { | ||
| body: JSON.stringify({ | ||
| completion_window: completionWindow, | ||
| endpoint, | ||
| input_file_id: inputFileId, | ||
| metadata: input.metadata | ||
| }), | ||
| headers: { ...headers3, "content-type": "application/json" }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot3(raw, config.id); | ||
| }; | ||
| const retrieve3 = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId(`${config.id} batch id`, id); | ||
| const raw = await requestJson(`${baseUrl3(credentials)}/batches/${batchId}`, { | ||
| headers: authHeaders(credentials) | ||
| }); | ||
| return normalizeSnapshot3(raw, config.id); | ||
| }; | ||
| async function* results3(id, credentials) { | ||
| const snapshot = await retrieve3(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const outputFileId = asString(raw.output_file_id); | ||
| const errorFileId = asString(raw.error_file_id); | ||
| if (!(outputFileId || errorFileId)) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| const headers3 = authHeaders(credentials); | ||
| if (outputFileId) { | ||
| yield* streamResultFile(assertSimpleProviderId(`${config.id} output file id`, outputFileId), baseUrl3(credentials), headers3); | ||
| } | ||
| if (errorFileId) { | ||
| yield* streamResultFile(assertSimpleProviderId(`${config.id} error file id`, errorFileId), baseUrl3(credentials), headers3); | ||
| } | ||
| } | ||
| const cancel3 = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId(`${config.id} batch id`, id); | ||
| await requestJson(`${baseUrl3(credentials)}/batches/${batchId}/cancel`, { | ||
| headers: authHeaders(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| return { cancel: cancel3, id: config.id, results: results3, retrieve: retrieve3, submit: submit3 }; | ||
| }; | ||
| // src/providers/groq.ts | ||
| var groqAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "GROQ_API_KEY", | ||
| apiKeyLabel: "Groq", | ||
| baseUrl: "https://api.groq.com/openai/v1", | ||
| id: "groq", | ||
| lineFormat: "method-url", | ||
| normalizeEndpoint: (endpoint) => endpoint.replace(/^\/openai/u, "") | ||
| }); | ||
| // src/providers/mistral.ts | ||
| var MISTRAL_BASE = "https://api.mistral.ai/v1"; | ||
| var apiKey3 = (credentials) => resolveApiKey(credentials, "MISTRAL_API_KEY", "Mistral"); | ||
| var baseUrl3 = (credentials) => credentials.baseURL ?? MISTRAL_BASE; | ||
| var authHeaders = (credentials) => ({ | ||
| Authorization: `Bearer ${apiKey3(credentials)}`, | ||
| ...credentials.headers | ||
| }); | ||
| var mapStatus3 = (status) => { | ||
| switch (status) { | ||
| case "QUEUED": { | ||
| return "validating"; | ||
| } | ||
| case "SUCCESS": { | ||
| return "completed"; | ||
| } | ||
| case "FAILED": { | ||
| return "failed"; | ||
| } | ||
| case "TIMEOUT_EXCEEDED": { | ||
| return "expired"; | ||
| } | ||
| case "CANCELLATION_REQUESTED": { | ||
| return "cancelling"; | ||
| } | ||
| case "CANCELLED": { | ||
| return "cancelled"; | ||
| } | ||
| default: { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| }; | ||
| var normalizeSnapshot4 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const succeeded = asNumber(obj.succeeded_requests) ?? 0; | ||
| const failed = asNumber(obj.failed_requests) ?? 0; | ||
| const id = asString(obj.id) ?? ""; | ||
| return { | ||
| completedAt: toDate(obj.completed_at), | ||
| createdAt: toDate(obj.created_at), | ||
| id: id ? assertSimpleProviderId("Mistral job id", id) : "", | ||
| provider: "mistral", | ||
| raw, | ||
| requestCounts: { | ||
| completed: succeeded, | ||
| failed, | ||
| total: asNumber(obj.total_requests) ?? succeeded + failed | ||
| }, | ||
| status: mapStatus3(asString(obj.status)) | ||
| }; | ||
| }; | ||
| var submit3 = async (input) => { | ||
| const limits = resolveBatchLimits(input.limits); | ||
| const jsonl = encodeJsonl(input.built.map((item) => ({ | ||
| body: omit(omit(item.body, "stream"), "model"), | ||
| custom_id: item.customId | ||
| }))); | ||
| assertByteLength("batch upload JSONL", jsonl, limits.maxUploadBytes); | ||
| const inputFileId = await uploadInputFile(jsonl, baseUrl3(input.credentials), authHeaders(input.credentials)); | ||
| const raw = await requestJson(`${baseUrl3(input.credentials)}/batch/jobs`, { | ||
| body: JSON.stringify({ | ||
| endpoint: input.endpoint, | ||
| input_files: [inputFileId], | ||
| metadata: input.metadata, | ||
| model: input.modelId | ||
| }), | ||
| headers: { | ||
| ...authHeaders(input.credentials), | ||
| "content-type": "application/json" | ||
| }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot4(raw); | ||
| }; | ||
| var retrieve3 = async (id, credentials) => { | ||
| const jobId = assertSimpleProviderId("Mistral job id", id); | ||
| const raw = await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}`, { | ||
| headers: authHeaders(credentials) | ||
| }); | ||
| return normalizeSnapshot4(raw); | ||
| }; | ||
| async function* results3(id, credentials) { | ||
| const snapshot = await retrieve3(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const outputFileId = asString(raw.output_file); | ||
| const errorFileId = asString(raw.error_file); | ||
| const headers3 = authHeaders(credentials); | ||
| if (outputFileId) { | ||
| yield* streamResultFile(assertSimpleProviderId("Mistral output file id", outputFileId), baseUrl3(credentials), headers3); | ||
| } | ||
| if (errorFileId) { | ||
| yield* streamResultFile(assertSimpleProviderId("Mistral error file id", errorFileId), baseUrl3(credentials), headers3); | ||
| } | ||
| if (!(outputFileId || errorFileId)) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| } | ||
| var cancel3 = async (id, credentials) => { | ||
| const jobId = assertSimpleProviderId("Mistral job id", id); | ||
| await requestJson(`${baseUrl3(credentials)}/batch/jobs/${jobId}/cancel`, { | ||
| headers: authHeaders(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var mistralAdapter = { | ||
| cancel: cancel3, | ||
| id: "mistral", | ||
| results: results3, | ||
| retrieve: retrieve3, | ||
| submit: submit3 | ||
| }; | ||
| // src/providers/openai.ts | ||
| var openaiAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "OPENAI_API_KEY", | ||
| apiKeyLabel: "OpenAI", | ||
| baseUrl: "https://api.openai.com/v1", | ||
| id: "openai", | ||
| lineFormat: "method-url" | ||
| }); | ||
| // src/providers/together.ts | ||
| var INPUT_FILE_NAME = "batchwork.jsonl"; | ||
| var HTTP_FOUND = 302; | ||
| var parseIpv4 = (host) => { | ||
| if (!/^\d{1,3}(?:\.\d{1,3}){3}$/u.test(host)) { | ||
| return; | ||
| } | ||
| const parts = host.split(".").map(Number); | ||
| const valid = parts.every((part) => Number.isInteger(part) && part >= 0 && part <= 255); | ||
| return valid ? parts : undefined; | ||
| }; | ||
| var isPrivateIpv4 = (parts) => { | ||
| const [a = 0, b = 0] = parts; | ||
| return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224; | ||
| }; | ||
| var isPrivateIpv6 = (host) => { | ||
| const normalized = host.replace(/^\[/u, "").replace(/\]$/u, "").toLowerCase(); | ||
| return normalized === "::" || normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80:"); | ||
| }; | ||
| var validateUploadLocation = (location) => { | ||
| let url; | ||
| try { | ||
| url = new URL(location); | ||
| } catch (error) { | ||
| throw new BatchworkError("batchwork: Together upload Location must be a valid URL.", { cause: error }); | ||
| } | ||
| if (url.protocol !== "https:") { | ||
| throw new BatchworkError("batchwork: Together upload Location must use https."); | ||
| } | ||
| if (url.username || url.password) { | ||
| throw new BatchworkError("batchwork: Together upload Location must not include credentials."); | ||
| } | ||
| const host = url.hostname.toLowerCase(); | ||
| const ipv4 = parseIpv4(host); | ||
| if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || ipv4 && isPrivateIpv4(ipv4) || isPrivateIpv6(host)) { | ||
| throw new BatchworkError("batchwork: Together upload Location must not target localhost or private networks."); | ||
| } | ||
| return url.toString(); | ||
| }; | ||
| var uploadTogetherFile = async (args) => { | ||
| const metadata = new FormData; | ||
| metadata.append("purpose", args.purpose); | ||
| metadata.append("file_name", INPUT_FILE_NAME); | ||
| metadata.append("file_type", "jsonl"); | ||
| const init = await fetch(`${args.baseUrl}/files`, { | ||
| body: metadata, | ||
| headers: args.headers, | ||
| method: "POST", | ||
| redirect: "manual" | ||
| }); | ||
| const location = init.headers.get("location"); | ||
| const fileId = init.headers.get("x-together-file-id"); | ||
| if (init.status !== HTTP_FOUND || !(location && fileId)) { | ||
| throw new BatchworkError(`batchwork: Together upload could not be initiated (${init.status}).`); | ||
| } | ||
| const uploadLocation = validateUploadLocation(location); | ||
| const upload = await fetch(uploadLocation, { | ||
| body: args.jsonl, | ||
| method: "PUT" | ||
| }); | ||
| if (!upload.ok) { | ||
| throw new BatchworkError(`batchwork: Together file upload failed (${upload.status}).`); | ||
| } | ||
| const safeFileId = assertSimpleProviderId("Together file id", fileId); | ||
| await requestJson(`${args.baseUrl}/files/${safeFileId}/preprocess`, { | ||
| headers: args.headers, | ||
| method: "POST" | ||
| }); | ||
| return safeFileId; | ||
| }; | ||
| var togetherAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "TOGETHER_API_KEY", | ||
| apiKeyLabel: "Together AI", | ||
| baseUrl: "https://api.together.xyz/v1", | ||
| filePurpose: "batch-api", | ||
| id: "together", | ||
| lineFormat: "body-only", | ||
| uploadFile: uploadTogetherFile | ||
| }); | ||
| // src/providers/xai.ts | ||
| var XAI_BASE = "https://api.x.ai/v1"; | ||
| var RESULTS_PAGE_SIZE = 100; | ||
| var apiKey4 = (credentials) => resolveApiKey(credentials, "XAI_API_KEY", "xAI"); | ||
| var baseUrl4 = (credentials) => credentials.baseURL ?? XAI_BASE; | ||
| var authHeaders2 = (credentials) => ({ | ||
| Authorization: `Bearer ${apiKey4(credentials)}`, | ||
| ...credentials.headers | ||
| }); | ||
| var deriveStatus = (state) => { | ||
| const pending = asNumber(state.num_pending); | ||
| if (pending === undefined) { | ||
| return "in_progress"; | ||
| } | ||
| const total = asNumber(state.num_requests) ?? 0; | ||
| const cancelled = asNumber(state.num_cancelled) ?? 0; | ||
| if (total === 0) { | ||
| return "in_progress"; | ||
| } | ||
| if (pending > 0) { | ||
| return "in_progress"; | ||
| } | ||
| if (cancelled > 0 && cancelled === total) { | ||
| return "cancelled"; | ||
| } | ||
| return "completed"; | ||
| }; | ||
| var normalizeSnapshot5 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const state = asRecord(obj.state); | ||
| const id = asString(obj.batch_id) ?? asString(obj.id) ?? ""; | ||
| return { | ||
| completedAt: toDate(obj.cancel_time), | ||
| createdAt: toDate(obj.create_time), | ||
| expiresAt: toDate(obj.expire_time ?? obj.expires_at), | ||
| id: id ? assertSimpleProviderId("xAI batch id", id) : "", | ||
| provider: "xai", | ||
| raw, | ||
| requestCounts: { | ||
| canceled: asNumber(state.num_cancelled) ?? 0, | ||
| completed: asNumber(state.num_success) ?? 0, | ||
| failed: asNumber(state.num_error) ?? 0, | ||
| processing: asNumber(state.num_pending) ?? 0, | ||
| total: asNumber(state.num_requests) ?? 0 | ||
| }, | ||
| status: deriveStatus(state) | ||
| }; | ||
| }; | ||
| var normalizeResult3 = (item) => { | ||
| const obj = asRecord(item); | ||
| const customId = asString(obj.batch_request_id) ?? ""; | ||
| const batchResult = asRecord(obj.batch_result); | ||
| const resultError = batchResult.error; | ||
| const errorMessage = asString(obj.error_message) ?? asString(resultError) ?? asString(asRecord(resultError).message); | ||
| if (errorMessage) { | ||
| return { | ||
| customId, | ||
| error: { | ||
| message: errorMessage, | ||
| type: asString(asRecord(resultError).type) | ||
| }, | ||
| response: obj, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| const response = asRecord(batchResult.response); | ||
| const completion = response.chat_get_completion ?? Object.values(response)[0]; | ||
| return { | ||
| customId, | ||
| response: completion, | ||
| status: "succeeded", | ||
| text: textFromBody(completion), | ||
| usage: usageFromBody(completion) | ||
| }; | ||
| }; | ||
| var submit4 = async (input) => { | ||
| const limits = resolveBatchLimits(input.limits); | ||
| const jsonl = encodeJsonl(input.built.map((item) => ({ | ||
| body: omit(item.body, "stream"), | ||
| custom_id: item.customId, | ||
| method: "POST", | ||
| url: input.endpoint | ||
| }))); | ||
| assertByteLength("batch upload JSONL", jsonl, limits.maxUploadBytes); | ||
| const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials), { purpose: null }); | ||
| const raw = await requestJson(`${baseUrl4(input.credentials)}/batches`, { | ||
| body: JSON.stringify({ input_file_id: inputFileId, name: "batchwork" }), | ||
| headers: { | ||
| ...authHeaders2(input.credentials), | ||
| "content-type": "application/json" | ||
| }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot5(raw); | ||
| }; | ||
| var retrieve4 = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId("xAI batch id", id); | ||
| const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}`, { | ||
| headers: authHeaders2(credentials) | ||
| }); | ||
| return normalizeSnapshot5(raw); | ||
| }; | ||
| async function* results4(id, credentials) { | ||
| const batchId = assertSimpleProviderId("xAI batch id", id); | ||
| const headers3 = authHeaders2(credentials); | ||
| let token; | ||
| do { | ||
| const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) }); | ||
| if (token) { | ||
| query.set("pagination_token", token); | ||
| } | ||
| const raw = await requestJson(`${baseUrl4(credentials)}/batches/${batchId}/results?${query.toString()}`, { headers: headers3 }); | ||
| const page = asRecord(raw); | ||
| for (const item of Array.isArray(page.results) ? page.results : []) { | ||
| yield normalizeResult3(item); | ||
| } | ||
| token = asString(page.pagination_token); | ||
| } while (token); | ||
| } | ||
| var cancel4 = async (id, credentials) => { | ||
| const batchId = assertSimpleProviderId("xAI batch id", id); | ||
| await requestJson(`${baseUrl4(credentials)}/batches/${batchId}:cancel`, { | ||
| headers: authHeaders2(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var xaiAdapter = { | ||
| cancel: cancel4, | ||
| id: "xai", | ||
| results: results4, | ||
| retrieve: retrieve4, | ||
| submit: submit4 | ||
| }; | ||
| // src/providers/index.ts | ||
| var adapters = { | ||
| anthropic: anthropicAdapter, | ||
| google: googleAdapter, | ||
| groq: groqAdapter, | ||
| mistral: mistralAdapter, | ||
| openai: openaiAdapter, | ||
| together: togetherAdapter, | ||
| xai: xaiAdapter | ||
| }; | ||
| var getAdapter = (provider) => adapters[provider]; | ||
| export { BatchworkError, UnsupportedProviderError, MissingDependencyError, resolveBatchLimits, assertByteLength, mapWithConcurrency, isTerminalStatus, BatchJob, getAdapter }; | ||
| //# debugId=A38719FEA6433ECA64756E2164756E21 | ||
| //# sourceMappingURL=chunk-g481f961.js.map |
Sorry, the diff of this file is too big to display
| import { | ||
| BatchJob, | ||
| BatchworkError, | ||
| MissingDependencyError, | ||
| UnsupportedProviderError, | ||
| assertByteLength, | ||
| getAdapter, | ||
| mapWithConcurrency, | ||
| resolveBatchLimits | ||
| } from "./chunk-g481f961.js"; | ||
| import { | ||
| __require | ||
| } from "./chunk-v0bahtg2.js"; | ||
| // src/model.ts | ||
| var CAPTURE_API_KEY = "batchwork-capture"; | ||
| var PACKAGE_BY_PROVIDER = { | ||
| anthropic: { label: "Anthropic", specifier: "@ai-sdk/anthropic" }, | ||
| google: { label: "Google Gemini", specifier: "@ai-sdk/google" }, | ||
| groq: { label: "Groq", specifier: "@ai-sdk/groq" }, | ||
| mistral: { label: "Mistral", specifier: "@ai-sdk/mistral" }, | ||
| openai: { label: "OpenAI", specifier: "@ai-sdk/openai" }, | ||
| together: { label: "Together AI", specifier: "@ai-sdk/togetherai" }, | ||
| xai: { label: "xAI", specifier: "@ai-sdk/xai" } | ||
| }; | ||
| var PROVIDER_BY_FAMILY = { | ||
| anthropic: "anthropic", | ||
| google: "google", | ||
| groq: "groq", | ||
| mistral: "mistral", | ||
| openai: "openai", | ||
| together: "together", | ||
| togetherai: "together", | ||
| xai: "xai" | ||
| }; | ||
| var PROVIDER_BY_ALIAS = { | ||
| ...PROVIDER_BY_FAMILY, | ||
| gemini: "google" | ||
| }; | ||
| var splitOnce = (value, separator) => { | ||
| const index = value.indexOf(separator); | ||
| if (index === -1) { | ||
| return [value, ""]; | ||
| } | ||
| return [value.slice(0, index), value.slice(index + separator.length)]; | ||
| }; | ||
| var openaiKind = (suffix) => { | ||
| if (suffix === "responses") { | ||
| return "responses"; | ||
| } | ||
| if (suffix === "completion") { | ||
| return "completion"; | ||
| } | ||
| return "chat"; | ||
| }; | ||
| var resolveModelString = (value) => { | ||
| const [providerId, modelId] = splitOnce(value, "/"); | ||
| if (modelId === "") { | ||
| throw new UnsupportedProviderError(value); | ||
| } | ||
| const provider = PROVIDER_BY_ALIAS[providerId]; | ||
| if (!provider) { | ||
| throw new UnsupportedProviderError(providerId); | ||
| } | ||
| return { kind: "chat", modelId, provider }; | ||
| }; | ||
| var resolveModel = (model) => { | ||
| if (typeof model === "string") { | ||
| return resolveModelString(model); | ||
| } | ||
| const [family, suffix] = splitOnce(model.provider, "."); | ||
| const provider = PROVIDER_BY_FAMILY[family]; | ||
| if (provider === "openai") { | ||
| return { kind: openaiKind(suffix), modelId: model.modelId, provider }; | ||
| } | ||
| if (provider) { | ||
| return { kind: "chat", modelId: model.modelId, provider }; | ||
| } | ||
| if (model.modelId.includes("/")) { | ||
| return resolveModelString(model.modelId); | ||
| } | ||
| throw new UnsupportedProviderError(model.provider); | ||
| }; | ||
| var importProvider = (provider) => { | ||
| switch (provider) { | ||
| case "anthropic": { | ||
| return import("@ai-sdk/anthropic"); | ||
| } | ||
| case "google": { | ||
| return import("@ai-sdk/google"); | ||
| } | ||
| case "groq": { | ||
| return import("@ai-sdk/groq"); | ||
| } | ||
| case "mistral": { | ||
| return import("@ai-sdk/mistral"); | ||
| } | ||
| case "openai": { | ||
| return import("@ai-sdk/openai"); | ||
| } | ||
| case "together": { | ||
| return import("@ai-sdk/togetherai"); | ||
| } | ||
| case "xai": { | ||
| return import("@ai-sdk/xai"); | ||
| } | ||
| default: { | ||
| return Promise.reject(new UnsupportedProviderError(provider)); | ||
| } | ||
| } | ||
| }; | ||
| var loadProvider = async (provider, load = importProvider) => { | ||
| try { | ||
| return await load(provider); | ||
| } catch (error) { | ||
| if (error instanceof UnsupportedProviderError) { | ||
| throw error; | ||
| } | ||
| const { specifier, label } = PACKAGE_BY_PROVIDER[provider]; | ||
| throw new MissingDependencyError(specifier, label); | ||
| } | ||
| }; | ||
| var createCaptureModel = async (resolved, credentials, fetchImpl) => { | ||
| const settings = { | ||
| apiKey: credentials.apiKey ?? CAPTURE_API_KEY, | ||
| baseURL: credentials.baseURL, | ||
| fetch: fetchImpl, | ||
| headers: credentials.headers | ||
| }; | ||
| switch (resolved.provider) { | ||
| case "openai": { | ||
| const { createOpenAI } = await loadProvider("openai"); | ||
| const provider = createOpenAI(settings); | ||
| if (resolved.kind === "responses") { | ||
| return provider.responses(resolved.modelId); | ||
| } | ||
| if (resolved.kind === "completion") { | ||
| return provider.completion(resolved.modelId); | ||
| } | ||
| return provider.chat(resolved.modelId); | ||
| } | ||
| case "anthropic": { | ||
| const { createAnthropic } = await loadProvider("anthropic"); | ||
| return createAnthropic(settings).messages(resolved.modelId); | ||
| } | ||
| case "groq": { | ||
| const { createGroq } = await loadProvider("groq"); | ||
| return createGroq(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "mistral": { | ||
| const { createMistral } = await loadProvider("mistral"); | ||
| return createMistral(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "google": { | ||
| const { createGoogleGenerativeAI } = await loadProvider("google"); | ||
| return createGoogleGenerativeAI(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "xai": { | ||
| const { createXai } = await loadProvider("xai"); | ||
| return createXai(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "together": { | ||
| const { createTogetherAI } = await loadProvider("together"); | ||
| return createTogetherAI(settings).languageModel(resolved.modelId); | ||
| } | ||
| default: { | ||
| throw new UnsupportedProviderError(resolved.provider); | ||
| } | ||
| } | ||
| }; | ||
| // src/body.ts | ||
| import { generateText } from "ai"; | ||
| var MAX_CAUSE_DEPTH = 10; | ||
| class CaptureSignalError extends Error { | ||
| url; | ||
| rawBody; | ||
| constructor(url, rawBody) { | ||
| super("batchwork:capture"); | ||
| this.name = "CaptureSignalError"; | ||
| this.url = url; | ||
| this.rawBody = rawBody; | ||
| } | ||
| } | ||
| var resolveUrl = (input) => { | ||
| if (typeof input === "string") { | ||
| return input; | ||
| } | ||
| if (input instanceof URL) { | ||
| return input.toString(); | ||
| } | ||
| return input.url; | ||
| }; | ||
| var extractBody = (init) => { | ||
| const body = init?.body; | ||
| if (typeof body === "string") { | ||
| return body; | ||
| } | ||
| if (body instanceof Uint8Array) { | ||
| return new TextDecoder().decode(body); | ||
| } | ||
| throw new BatchworkError("batchwork: unable to read the provider request body during capture."); | ||
| }; | ||
| var captureFetch = (input, init) => Promise.reject(new CaptureSignalError(resolveUrl(input), extractBody(init))); | ||
| var findCapture = (error) => { | ||
| let current = error; | ||
| let depth = 0; | ||
| while (current && depth < MAX_CAUSE_DEPTH) { | ||
| if (current instanceof CaptureSignalError) { | ||
| return current; | ||
| } | ||
| current = current.cause; | ||
| depth += 1; | ||
| } | ||
| }; | ||
| var endpointFromUrl = (url) => { | ||
| try { | ||
| return new URL(url).pathname; | ||
| } catch { | ||
| return url; | ||
| } | ||
| }; | ||
| var mergeDefaults = (request, defaults) => { | ||
| if (!defaults) { | ||
| return request; | ||
| } | ||
| return { ...defaults, ...request }; | ||
| }; | ||
| var toGenerateInput = (model, request) => ({ | ||
| frequencyPenalty: request.frequencyPenalty, | ||
| maxOutputTokens: request.maxOutputTokens, | ||
| maxRetries: 0, | ||
| messages: request.messages, | ||
| model, | ||
| presencePenalty: request.presencePenalty, | ||
| prompt: request.prompt, | ||
| providerOptions: request.providerOptions, | ||
| seed: request.seed, | ||
| stopSequences: request.stopSequences, | ||
| system: request.system, | ||
| temperature: request.temperature, | ||
| toolChoice: request.toolChoice, | ||
| tools: request.tools, | ||
| topK: request.topK, | ||
| topP: request.topP | ||
| }); | ||
| var captureOne = async (model, request, customId) => { | ||
| try { | ||
| await generateText(toGenerateInput(model, request)); | ||
| } catch (error) { | ||
| const capture = findCapture(error); | ||
| if (capture) { | ||
| return { | ||
| body: JSON.parse(capture.rawBody), | ||
| customId, | ||
| endpoint: endpointFromUrl(capture.url) | ||
| }; | ||
| } | ||
| throw error; | ||
| } | ||
| throw new BatchworkError("batchwork: the request was not intercepted while building the batch body."); | ||
| }; | ||
| var buildRequestBodies = async (resolved, requests, defaults, credentials, rawLimits) => { | ||
| const limits = resolveBatchLimits(rawLimits); | ||
| if (requests.length > limits.maxRequests) { | ||
| throw new BatchworkError(`batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`); | ||
| } | ||
| const model = await createCaptureModel(resolved, credentials, captureFetch); | ||
| const seen = new Set; | ||
| const items = requests.map((request, index) => { | ||
| const customId = request.customId ?? `request-${index}`; | ||
| if (seen.has(customId)) { | ||
| throw new BatchworkError(`batchwork: duplicate customId "${customId}". customId values must be unique within a batch.`); | ||
| } | ||
| seen.add(customId); | ||
| return { customId, request }; | ||
| }); | ||
| return await mapWithConcurrency(items, limits.captureConcurrency, async (item) => { | ||
| const built = await captureOne(model, mergeDefaults(item.request, defaults), item.customId); | ||
| assertByteLength(`request "${item.customId}"`, JSON.stringify(built.body), limits.maxRequestBytes); | ||
| return built; | ||
| }); | ||
| }; | ||
| // src/batch.ts | ||
| var pickCredentials = (source) => ({ | ||
| apiKey: source.apiKey, | ||
| baseURL: source.baseURL, | ||
| headers: source.headers | ||
| }); | ||
| var providerFromRef = (ref) => { | ||
| if (ref.provider) { | ||
| return ref.provider; | ||
| } | ||
| if (ref.model !== undefined) { | ||
| return resolveModel(ref.model).provider; | ||
| } | ||
| throw new BatchworkError("batchwork: provide `provider` or `model` to identify the batch."); | ||
| }; | ||
| var batch = async (options) => { | ||
| if (options.requests.length === 0) { | ||
| throw new BatchworkError("batchwork: `requests` must not be empty."); | ||
| } | ||
| const resolved = resolveModel(options.model); | ||
| const credentials = pickCredentials(options); | ||
| const limits = resolveBatchLimits(options.limits); | ||
| const adapter = getAdapter(resolved.provider); | ||
| const built = await buildRequestBodies(resolved, options.requests, options.defaults, credentials, limits); | ||
| const snapshot = await adapter.submit({ | ||
| built, | ||
| credentials, | ||
| endpoint: built[0]?.endpoint ?? "", | ||
| limits, | ||
| metadata: options.metadata, | ||
| modelId: resolved.modelId | ||
| }); | ||
| return new BatchJob(adapter, credentials, snapshot); | ||
| }; | ||
| var getBatch = async (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| const credentials = pickCredentials(ref); | ||
| const snapshot = await adapter.retrieve(ref.id, credentials); | ||
| return new BatchJob(adapter, credentials, snapshot); | ||
| }; | ||
| var getBatchResults = (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| return adapter.results(ref.id, pickCredentials(ref)); | ||
| }; | ||
| var cancelBatch = async (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| await adapter.cancel(ref.id, pickCredentials(ref)); | ||
| }; | ||
| export { resolveModel, batch, getBatch, getBatchResults, cancelBatch }; | ||
| //# debugId=C7BCC311BBAF05B464756E2164756E21 | ||
| //# sourceMappingURL=chunk-m4n610nm.js.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/model.ts", "../src/body.ts", "../src/batch.ts"], | ||
| "sourcesContent": [ | ||
| "import type * as AnthropicModule from \"@ai-sdk/anthropic\";\nimport type * as GoogleModule from \"@ai-sdk/google\";\nimport type * as GroqModule from \"@ai-sdk/groq\";\nimport type * as MistralModule from \"@ai-sdk/mistral\";\nimport type * as OpenAIModule from \"@ai-sdk/openai\";\nimport type * as TogetherModule from \"@ai-sdk/togetherai\";\nimport type * as XaiModule from \"@ai-sdk/xai\";\nimport type { LanguageModel } from \"ai\";\n\nimport { MissingDependencyError, UnsupportedProviderError } from \"./errors\";\nimport type { BatchProvider, ProviderCredentials } from \"./types\";\n\n/** A fetch implementation compatible with the AI SDK provider `fetch` option. */\nexport type CapturingFetch = typeof globalThis.fetch;\n\n/** OpenAI exposes several request shapes; we mirror the one the model implies. */\nexport type OpenAIModelKind = \"chat\" | \"responses\" | \"completion\";\n\nexport interface ResolvedModel {\n /** Relevant for OpenAI; other providers always use a single chat endpoint. */\n kind: OpenAIModelKind;\n modelId: string;\n provider: BatchProvider;\n}\n\n/**\n * Placeholder API key used only when building request bodies. Body building\n * intercepts the request before it is sent, so no real credential is needed —\n * but the provider refuses to construct a model without one.\n */\nconst CAPTURE_API_KEY = \"batchwork-capture\";\n\n/** The optional `@ai-sdk/*` package backing each provider. */\nconst PACKAGE_BY_PROVIDER: Record<\n BatchProvider,\n { label: string; specifier: string }\n> = {\n anthropic: { label: \"Anthropic\", specifier: \"@ai-sdk/anthropic\" },\n google: { label: \"Google Gemini\", specifier: \"@ai-sdk/google\" },\n groq: { label: \"Groq\", specifier: \"@ai-sdk/groq\" },\n mistral: { label: \"Mistral\", specifier: \"@ai-sdk/mistral\" },\n openai: { label: \"OpenAI\", specifier: \"@ai-sdk/openai\" },\n together: { label: \"Together AI\", specifier: \"@ai-sdk/togetherai\" },\n xai: { label: \"xAI\", specifier: \"@ai-sdk/xai\" },\n};\n\n/**\n * AI SDK provider id prefixes (the part before the first `.` in `model.provider`)\n * mapped to batch providers.\n */\nconst PROVIDER_BY_FAMILY: Record<string, BatchProvider> = {\n anthropic: \"anthropic\",\n google: \"google\",\n groq: \"groq\",\n mistral: \"mistral\",\n openai: \"openai\",\n together: \"together\",\n togetherai: \"together\",\n xai: \"xai\",\n};\n\n/** Aliases accepted in the `\"provider/model\"` string form. */\nconst PROVIDER_BY_ALIAS: Record<string, BatchProvider> = {\n ...PROVIDER_BY_FAMILY,\n gemini: \"google\",\n};\n\nconst splitOnce = (value: string, separator: string): [string, string] => {\n const index = value.indexOf(separator);\n if (index === -1) {\n return [value, \"\"];\n }\n return [value.slice(0, index), value.slice(index + separator.length)];\n};\n\nconst openaiKind = (suffix: string): OpenAIModelKind => {\n if (suffix === \"responses\") {\n return \"responses\";\n }\n if (suffix === \"completion\") {\n return \"completion\";\n }\n // Default to chat completions: the most widely supported batch endpoint.\n return \"chat\";\n};\n\n/** Resolve a `\"provider/model\"` string into a provider + model id. */\nconst resolveModelString = (value: string): ResolvedModel => {\n const [providerId, modelId] = splitOnce(value, \"/\");\n if (modelId === \"\") {\n throw new UnsupportedProviderError(value);\n }\n const provider = PROVIDER_BY_ALIAS[providerId];\n if (!provider) {\n throw new UnsupportedProviderError(providerId);\n }\n return { kind: \"chat\", modelId, provider };\n};\n\n/**\n * Resolve any AI SDK `model` (a `\"provider/model\"` string or a provider model\n * object such as `openai(\"gpt-5.5\")`) to a provider + model id + request\n * shape. Gateway/registry model objects whose `modelId` is itself\n * `\"provider/model\"` are also handled.\n */\nexport const resolveModel = (model: LanguageModel): ResolvedModel => {\n if (typeof model === \"string\") {\n return resolveModelString(model);\n }\n\n const [family, suffix] = splitOnce(model.provider, \".\");\n const provider = PROVIDER_BY_FAMILY[family];\n if (provider === \"openai\") {\n return { kind: openaiKind(suffix), modelId: model.modelId, provider };\n }\n if (provider) {\n return { kind: \"chat\", modelId: model.modelId, provider };\n }\n // Gateway/registry providers carry the real target in the model id.\n if (model.modelId.includes(\"/\")) {\n return resolveModelString(model.modelId);\n }\n throw new UnsupportedProviderError(model.provider);\n};\n\nconst importProvider = (provider: BatchProvider): Promise<unknown> => {\n switch (provider) {\n case \"anthropic\": {\n return import(\"@ai-sdk/anthropic\");\n }\n case \"google\": {\n return import(\"@ai-sdk/google\");\n }\n case \"groq\": {\n return import(\"@ai-sdk/groq\");\n }\n case \"mistral\": {\n return import(\"@ai-sdk/mistral\");\n }\n case \"openai\": {\n return import(\"@ai-sdk/openai\");\n }\n case \"together\": {\n return import(\"@ai-sdk/togetherai\");\n }\n case \"xai\": {\n return import(\"@ai-sdk/xai\");\n }\n default: {\n return Promise.reject(new UnsupportedProviderError(provider));\n }\n }\n};\n\n/**\n * Import the `@ai-sdk/*` package for a provider, translating a missing optional\n * dependency into a `MissingDependencyError`. The importer is injectable (like\n * the capturing `fetch`) so tests can drive the failure paths without\n * uninstalling a package. Exported for testing; not part of the public API.\n */\nexport const loadProvider = async <T>(\n provider: BatchProvider,\n load: (target: BatchProvider) => Promise<unknown> = importProvider\n): Promise<T> => {\n try {\n return (await load(provider)) as T;\n } catch (error) {\n if (error instanceof UnsupportedProviderError) {\n throw error;\n }\n const { specifier, label } = PACKAGE_BY_PROVIDER[provider];\n throw new MissingDependencyError(specifier, label);\n }\n};\n\n/**\n * Construct an AI SDK model wired to a capturing `fetch`, used to derive the\n * provider request body for each batch item without making a network call.\n */\nexport const createCaptureModel = async (\n resolved: ResolvedModel,\n credentials: ProviderCredentials,\n fetchImpl: CapturingFetch\n): Promise<LanguageModel> => {\n const settings = {\n apiKey: credentials.apiKey ?? CAPTURE_API_KEY,\n baseURL: credentials.baseURL,\n fetch: fetchImpl,\n headers: credentials.headers,\n };\n\n switch (resolved.provider) {\n case \"openai\": {\n const { createOpenAI } =\n await loadProvider<typeof OpenAIModule>(\"openai\");\n const provider = createOpenAI(settings);\n if (resolved.kind === \"responses\") {\n return provider.responses(resolved.modelId);\n }\n if (resolved.kind === \"completion\") {\n return provider.completion(resolved.modelId);\n }\n return provider.chat(resolved.modelId);\n }\n case \"anthropic\": {\n const { createAnthropic } =\n await loadProvider<typeof AnthropicModule>(\"anthropic\");\n return createAnthropic(settings).messages(resolved.modelId);\n }\n case \"groq\": {\n const { createGroq } = await loadProvider<typeof GroqModule>(\"groq\");\n return createGroq(settings).languageModel(resolved.modelId);\n }\n case \"mistral\": {\n const { createMistral } =\n await loadProvider<typeof MistralModule>(\"mistral\");\n return createMistral(settings).languageModel(resolved.modelId);\n }\n case \"google\": {\n const { createGoogleGenerativeAI } =\n await loadProvider<typeof GoogleModule>(\"google\");\n return createGoogleGenerativeAI(settings).languageModel(resolved.modelId);\n }\n case \"xai\": {\n const { createXai } = await loadProvider<typeof XaiModule>(\"xai\");\n return createXai(settings).languageModel(resolved.modelId);\n }\n case \"together\": {\n const { createTogetherAI } =\n await loadProvider<typeof TogetherModule>(\"together\");\n return createTogetherAI(settings).languageModel(resolved.modelId);\n }\n default: {\n throw new UnsupportedProviderError(resolved.provider);\n }\n }\n};\n", | ||
| "import { generateText } from \"ai\";\nimport type { LanguageModel } from \"ai\";\n\nimport { BatchworkError } from \"./errors\";\nimport {\n assertByteLength,\n mapWithConcurrency,\n resolveBatchLimits,\n} from \"./limits\";\nimport type { ResolvedBatchLimits } from \"./limits\";\nimport { createCaptureModel } from \"./model\";\nimport type { CapturingFetch, ResolvedModel } from \"./model\";\nimport type {\n BatchDefaults,\n BatchLimits,\n BatchRequest,\n ProviderCredentials,\n} from \"./types\";\n\ntype GenerateTextInput = Parameters<typeof generateText>[0];\n\n/** A provider request body derived from a single batch item. */\nexport interface BuiltRequest {\n /** The serialized provider request body (becomes the batch line). */\n body: Record<string, unknown>;\n customId: string;\n /** API endpoint path the model targets, e.g. `/v1/chat/completions`. */\n endpoint: string;\n}\n\nconst MAX_CAUSE_DEPTH = 10;\n\n/**\n * Thrown by the capturing `fetch` to abort the request after its body has been\n * serialized. The body travels inside the error (not shared state), so capture\n * is correct even under concurrency.\n */\nclass CaptureSignalError extends Error {\n readonly url: string;\n readonly rawBody: string;\n\n constructor(url: string, rawBody: string) {\n super(\"batchwork:capture\");\n this.name = \"CaptureSignalError\";\n this.url = url;\n this.rawBody = rawBody;\n }\n}\n\nconst resolveUrl = (input: string | URL | Request): string => {\n if (typeof input === \"string\") {\n return input;\n }\n if (input instanceof URL) {\n return input.toString();\n }\n return input.url;\n};\n\nconst extractBody = (init?: RequestInit): string => {\n const body = init?.body;\n if (typeof body === \"string\") {\n return body;\n }\n if (body instanceof Uint8Array) {\n return new TextDecoder().decode(body);\n }\n throw new BatchworkError(\n \"batchwork: unable to read the provider request body during capture.\"\n );\n};\n\n// `CapturingFetch` is `typeof fetch`, whose shape varies by runtime types (e.g.\n// Bun adds a required `preconnect` method). We only ever call it as a plain\n// fetch, so cast the bare implementation rather than stub the extra members.\nconst captureFetch = ((input: string | URL | Request, init?: RequestInit) =>\n Promise.reject(\n new CaptureSignalError(resolveUrl(input), extractBody(init))\n )) as unknown as CapturingFetch;\n\nconst findCapture = (error: unknown): CaptureSignalError | undefined => {\n let current: unknown = error;\n let depth = 0;\n while (current && depth < MAX_CAUSE_DEPTH) {\n if (current instanceof CaptureSignalError) {\n return current;\n }\n current = (current as { cause?: unknown }).cause;\n depth += 1;\n }\n};\n\nconst endpointFromUrl = (url: string): string => {\n try {\n return new URL(url).pathname;\n } catch {\n return url;\n }\n};\n\nconst mergeDefaults = (\n request: BatchRequest,\n defaults: BatchDefaults | undefined\n): BatchRequest => {\n if (!defaults) {\n return request;\n }\n return { ...defaults, ...request };\n};\n\n/**\n * Map a batch request to AI SDK `generateText` input. Fields are listed\n * explicitly so `customId` never leaks into the provider request.\n */\nconst toGenerateInput = (\n model: LanguageModel,\n request: BatchRequest\n): GenerateTextInput =>\n // `prompt`/`messages` form a discriminated union in the AI SDK types; we\n // pass both keys and let `generateText` validate the XOR at runtime.\n ({\n frequencyPenalty: request.frequencyPenalty,\n maxOutputTokens: request.maxOutputTokens,\n maxRetries: 0,\n messages: request.messages,\n model,\n presencePenalty: request.presencePenalty,\n prompt: request.prompt,\n providerOptions: request.providerOptions,\n seed: request.seed,\n stopSequences: request.stopSequences,\n system: request.system,\n temperature: request.temperature,\n toolChoice: request.toolChoice,\n tools: request.tools,\n topK: request.topK,\n topP: request.topP,\n }) as GenerateTextInput;\n\nconst captureOne = async (\n model: LanguageModel,\n request: BatchRequest,\n customId: string\n): Promise<BuiltRequest> => {\n try {\n await generateText(toGenerateInput(model, request));\n } catch (error) {\n const capture = findCapture(error);\n if (capture) {\n return {\n body: JSON.parse(capture.rawBody) as Record<string, unknown>,\n customId,\n endpoint: endpointFromUrl(capture.url),\n };\n }\n // A genuine failure (e.g. invalid prompt) — surface it to the caller.\n throw error;\n }\n throw new BatchworkError(\n \"batchwork: the request was not intercepted while building the batch body.\"\n );\n};\n\n/**\n * Derive provider request bodies for every batch item by running each through\n * the AI SDK with a capturing `fetch`. This reuses the AI SDK's full message,\n * tool, and multimodal conversion, so the body matches what `generateText`\n * would send — minus the network call.\n */\nexport const buildRequestBodies = async (\n resolved: ResolvedModel,\n requests: readonly BatchRequest[],\n defaults: BatchDefaults | undefined,\n credentials: ProviderCredentials,\n rawLimits?: BatchLimits | ResolvedBatchLimits\n): Promise<BuiltRequest[]> => {\n const limits = resolveBatchLimits(rawLimits);\n if (requests.length > limits.maxRequests) {\n throw new BatchworkError(\n `batchwork: requests length ${requests.length} exceeds the ${limits.maxRequests} request limit.`\n );\n }\n const model = await createCaptureModel(resolved, credentials, captureFetch);\n const seen = new Set<string>();\n\n // Assign and validate customIds up front (sequentially, so duplicates are\n // reported deterministically) before capturing bodies in parallel.\n const items = requests.map((request, index) => {\n const customId = request.customId ?? `request-${index}`;\n if (seen.has(customId)) {\n throw new BatchworkError(\n `batchwork: duplicate customId \"${customId}\". customId values must be unique within a batch.`\n );\n }\n seen.add(customId);\n return { customId, request };\n });\n\n return await mapWithConcurrency(\n items,\n limits.captureConcurrency,\n async (item) => {\n const built = await captureOne(\n model,\n mergeDefaults(item.request, defaults),\n item.customId\n );\n assertByteLength(\n `request \"${item.customId}\"`,\n JSON.stringify(built.body),\n limits.maxRequestBytes\n );\n return built;\n }\n );\n};\n", | ||
| "import { buildRequestBodies } from \"./body\";\nimport { BatchworkError } from \"./errors\";\nimport { BatchJob } from \"./job\";\nimport { resolveBatchLimits } from \"./limits\";\nimport { resolveModel } from \"./model\";\nimport { getAdapter } from \"./providers\";\nimport type {\n BatchOptions,\n BatchProvider,\n BatchRef,\n BatchResult,\n ProviderCredentials,\n} from \"./types\";\n\nconst pickCredentials = (source: ProviderCredentials): ProviderCredentials => ({\n apiKey: source.apiKey,\n baseURL: source.baseURL,\n headers: source.headers,\n});\n\nconst providerFromRef = (ref: BatchRef): BatchProvider => {\n if (ref.provider) {\n return ref.provider;\n }\n if (ref.model !== undefined) {\n return resolveModel(ref.model).provider;\n }\n throw new BatchworkError(\n \"batchwork: provide `provider` or `model` to identify the batch.\"\n );\n};\n\n/**\n * Submit a batch of requests to the model's provider and return a handle.\n *\n * Resolves immediately once the batch is accepted — it does not wait for\n * processing. Use the returned {@link BatchJob} to poll, wait, or stream\n * results.\n *\n * @example\n * const job = await batch({\n * model: openai(\"gpt-5.5\"),\n * requests: [{ customId: \"a\", prompt: \"Say hi\" }],\n * });\n * const results = await job.wait().then(() => job.collect());\n */\nexport const batch = async (options: BatchOptions): Promise<BatchJob> => {\n if (options.requests.length === 0) {\n throw new BatchworkError(\"batchwork: `requests` must not be empty.\");\n }\n\n const resolved = resolveModel(options.model);\n const credentials = pickCredentials(options);\n const limits = resolveBatchLimits(options.limits);\n const adapter = getAdapter(resolved.provider);\n\n const built = await buildRequestBodies(\n resolved,\n options.requests,\n options.defaults,\n credentials,\n limits\n );\n const snapshot = await adapter.submit({\n built,\n credentials,\n endpoint: built[0]?.endpoint ?? \"\",\n limits,\n metadata: options.metadata,\n modelId: resolved.modelId,\n });\n\n return new BatchJob(adapter, credentials, snapshot);\n};\n\n/**\n * Rehydrate a {@link BatchJob} for an existing batch id (e.g. one persisted\n * after submission). Identify the provider with `provider` or `model`.\n */\nexport const getBatch = async (ref: BatchRef): Promise<BatchJob> => {\n const adapter = getAdapter(providerFromRef(ref));\n const credentials = pickCredentials(ref);\n const snapshot = await adapter.retrieve(ref.id, credentials);\n return new BatchJob(adapter, credentials, snapshot);\n};\n\n/** Stream the results of an existing batch by id, without a handle. */\nexport const getBatchResults = (ref: BatchRef): AsyncGenerator<BatchResult> => {\n const adapter = getAdapter(providerFromRef(ref));\n return adapter.results(ref.id, pickCredentials(ref));\n};\n\n/** Request cancellation of an existing batch by id. */\nexport const cancelBatch = async (ref: BatchRef): Promise<void> => {\n const adapter = getAdapter(providerFromRef(ref));\n await adapter.cancel(ref.id, pickCredentials(ref));\n};\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "C7BCC311BBAF05B464756E2164756E21", | ||
| "names": [] | ||
| } |
| import type { BatchLimits } from "./types"; | ||
| export interface ResolvedBatchLimits { | ||
| captureConcurrency: number; | ||
| maxRequests: number; | ||
| maxRequestBytes: number; | ||
| maxUploadBytes: number; | ||
| } | ||
| export declare const resolveBatchLimits: (limits: BatchLimits | undefined) => ResolvedBatchLimits; | ||
| export declare const byteLength: (value: string) => number; | ||
| export declare const assertByteLength: (label: string, value: string, maxBytes: number) => void; | ||
| export declare const mapWithConcurrency: <Input, Output>(items: readonly Input[], concurrency: number, mapper: (item: Input) => Promise<Output>) => Promise<Output[]>; | ||
| //# sourceMappingURL=limits.d.ts.map |
| {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAoBD,eAAO,MAAM,kBAAkB,WACrB,WAAW,GAAG,SAAS,KAC9B,mBAiBD,CAAC;AAEH,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MACb,CAAC;AAE/B,eAAO,MAAM,gBAAgB,UACpB,MAAM,SACN,MAAM,YACH,MAAM,KACf,IAOF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,KAAK,EAAE,MAAM,SAC7C,SAAS,KAAK,EAAE,eACV,MAAM,UACX,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,KACvC,OAAO,CAAC,MAAM,EAAE,CAkBlB,CAAC"} |
| export declare const assertSimpleProviderId: (label: string, id: string) => string; | ||
| export declare const assertPrefixedProviderId: (label: string, id: string, prefix: string) => string; | ||
| //# sourceMappingURL=ids.d.ts.map |
| {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/providers/ids.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,UAAW,MAAM,MAAM,MAAM,KAAG,MAKlE,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAC5B,MAAM,MACT,MAAM,UACF,MAAM,KACb,MAWF,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGjC,OAAO,KAAK,EACV,YAAY,EAEZ,QAAQ,EACR,WAAW,EAEZ,MAAM,SAAS,CAAC;AAoBjB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,KAAK,YAAmB,YAAY,KAAG,OAAO,CAAC,QAAQ,CAwBnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,QAAe,QAAQ,KAAG,OAAO,CAAC,QAAQ,CAK9D,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,eAAe,QAAS,QAAQ,KAAG,cAAc,CAAC,WAAW,CAGzE,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,QAAe,QAAQ,KAAG,OAAO,CAAC,IAAI,CAG7D,CAAC"} | ||
| {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAIjC,OAAO,KAAK,EACV,YAAY,EAEZ,QAAQ,EACR,WAAW,EAEZ,MAAM,SAAS,CAAC;AAoBjB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,KAAK,YAAmB,YAAY,KAAG,OAAO,CAAC,QAAQ,CA2BnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,QAAe,QAAQ,KAAG,OAAO,CAAC,QAAQ,CAK9D,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,eAAe,QAAS,QAAQ,KAAG,cAAc,CAAC,WAAW,CAGzE,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,WAAW,QAAe,QAAQ,KAAG,OAAO,CAAC,IAAI,CAG7D,CAAC"} |
+3
-2
@@ -0,3 +1,4 @@ | ||
| import type { ResolvedBatchLimits } from "./limits"; | ||
| import type { ResolvedModel } from "./model"; | ||
| import type { BatchDefaults, BatchRequest, ProviderCredentials } from "./types"; | ||
| import type { BatchDefaults, BatchLimits, BatchRequest, ProviderCredentials } from "./types"; | ||
| /** A provider request body derived from a single batch item. */ | ||
@@ -17,3 +18,3 @@ export interface BuiltRequest { | ||
| */ | ||
| export declare const buildRequestBodies: (resolved: ResolvedModel, requests: readonly BatchRequest[], defaults: BatchDefaults | undefined, credentials: ProviderCredentials) => Promise<BuiltRequest[]>; | ||
| export declare const buildRequestBodies: (resolved: ResolvedModel, requests: readonly BatchRequest[], defaults: BatchDefaults | undefined, credentials: ProviderCredentials, rawLimits?: BatchLimits | ResolvedBatchLimits) => Promise<BuiltRequest[]>; | ||
| //# sourceMappingURL=body.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAIhF,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuID;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,aACnB,aAAa,YACb,SAAS,YAAY,EAAE,YACvB,aAAa,GAAG,SAAS,eACtB,mBAAmB,KAC/B,OAAO,CAAC,YAAY,EAAE,CAsBxB,CAAC"} | ||
| {"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAIjB,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuID;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,aACnB,aAAa,YACb,SAAS,YAAY,EAAE,YACvB,aAAa,GAAG,SAAS,eACtB,mBAAmB,cACpB,WAAW,GAAG,mBAAmB,KAC5C,OAAO,CAAC,YAAY,EAAE,CAwCxB,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAmBA,qEAAqE;AACrE,eAAO,MAAM,WAAW,GAAU,CAAC,OAC5B,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,CAAC,CAIX,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,aAAa,QACnB,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAOpC,CAAC"} | ||
| {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAUA,qEAAqE;AACrE,eAAO,MAAM,WAAW,GAAU,CAAC,OAC5B,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,CAAC,CAIX,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,aAAa,QACnB,MAAM,QACL,WAAW,KAChB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAOpC,CAAC"} |
+1
-1
@@ -5,3 +5,3 @@ export { batch, cancelBatch, getBatch, getBatchResults } from "./batch"; | ||
| export { resolveModel } from "./model"; | ||
| export type { BatchDefaults, BatchOptions, BatchProvider, BatchRef, BatchRequest, BatchRequestCounts, BatchRequestSettings, BatchResult, BatchResultError, BatchResultStatus, BatchSnapshot, BatchStatus, BatchUsage, ProviderCredentials, ProviderOptions, WaitOptions, } from "./types"; | ||
| export type { BatchDefaults, BatchLimits, BatchOptions, BatchProvider, BatchRef, BatchRequest, BatchRequestCounts, BatchRequestSettings, BatchResult, BatchResultError, BatchResultStatus, BatchSnapshot, BatchStatus, BatchUsage, ProviderCredentials, ProviderOptions, WaitOptions, } from "./types"; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,eAAe,EACf,WAAW,GACZ,MAAM,SAAS,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,YAAY,EACV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,eAAe,EACf,WAAW,GACZ,MAAM,SAAS,CAAC"} |
+2
-2
@@ -7,3 +7,3 @@ import { | ||
| resolveModel | ||
| } from "./chunk-ab2d71gk.js"; | ||
| } from "./chunk-m4n610nm.js"; | ||
| import { | ||
@@ -15,3 +15,3 @@ BatchJob, | ||
| isTerminalStatus | ||
| } from "./chunk-kv3847wy.js"; | ||
| } from "./chunk-g481f961.js"; | ||
| import"./chunk-v0bahtg2.js"; | ||
@@ -18,0 +18,0 @@ export { |
+5
-15
@@ -1,17 +0,7 @@ | ||
| /** | ||
| * JSONL (newline-delimited JSON) helpers. OpenAI batch input/output and | ||
| * Anthropic batch results are all JSONL, so batchwork builds and parses it for | ||
| * the user. | ||
| */ | ||
| /** Serialize an array of values to a JSONL string (trailing newline included). */ | ||
| export interface JsonlParseOptions { | ||
| maxLineBytes?: number; | ||
| } | ||
| export declare const encodeJsonl: (items: readonly unknown[]) => string; | ||
| /** Parse a complete JSONL string into an array, skipping blank lines. */ | ||
| export declare const parseJsonl: <T = unknown>(text: string) => T[]; | ||
| /** | ||
| * Stream-parse JSONL from a byte stream, yielding one parsed value per line as | ||
| * it arrives. Memory-efficient for large result files. | ||
| * | ||
| * @yields {T} the parsed value for each non-empty line. | ||
| */ | ||
| export declare function streamJsonl<T = unknown>(source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>): AsyncGenerator<T>; | ||
| export declare const parseJsonl: <T = unknown>(text: string, options?: JsonlParseOptions) => T[]; | ||
| export declare function streamJsonl<T = unknown>(source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>, options?: JsonlParseOptions): AsyncGenerator<T>; | ||
| //# sourceMappingURL=jsonl.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../src/jsonl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,kFAAkF;AAClF,eAAO,MAAM,WAAW,UAAW,SAAS,OAAO,EAAE,KAAG,MAMvD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,OAAO,QAAQ,MAAM,KAAG,CAAC,EASvD,CAAC;AAkCF;;;;;GAKG;AAEH,wBAAuB,WAAW,CAAC,CAAC,GAAG,OAAO,EAC5C,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GAC7D,cAAc,CAAC,CAAC,CAAC,CAsBnB"} | ||
| {"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../src/jsonl.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA6CD,eAAO,MAAM,WAAW,UAAW,SAAS,OAAO,EAAE,KAAG,MAMvD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,OAAO,QAC9B,MAAM,YACF,iBAAiB,KAC1B,CAAC,EAWH,CAAC;AAmCF,wBAAuB,WAAW,CAAC,CAAC,GAAG,OAAO,EAC5C,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,EAC9D,OAAO,CAAC,EAAE,iBAAiB,GAC1B,cAAc,CAAC,CAAC,CAAC,CA2BnB"} |
@@ -24,4 +24,6 @@ import type { CredentialResolver, TrackTarget } from "../server/poller"; | ||
| credentials?: CredentialResolver; | ||
| /** When set, the cron `GET` requires `Authorization: Bearer <cronSecret>`. */ | ||
| /** Requires `Authorization: Bearer <cronSecret>` on the cron `GET`. */ | ||
| cronSecret?: string; | ||
| /** Allow unauthenticated cron ticks. Intended only for private/local routes. */ | ||
| allowUnauthenticatedCron?: boolean; | ||
| /** When set, mounts an OpenAI native-webhook handler on `POST`. */ | ||
@@ -28,0 +30,0 @@ openaiSigningSecret?: string; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,kBAAkB,EAClB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAiB,WAAW,EAAuB,MAAM,UAAU,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,KAChC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,6DAA6D;IAC7D,UAAU,EAAE,eAAe,CAAC;IAC5B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,8EAA8E;IAC9E,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/C,4EAA4E;IAC5E,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACvD;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,YAAa,kBAAkB,KAAG,WAoD/D,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,kBAAkB,EAClB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAiB,WAAW,EAAuB,MAAM,UAAU,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,KAChC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,6DAA6D;IAC7D,UAAU,EAAE,eAAe,CAAC;IAC5B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,8EAA8E;IAC9E,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/C,4EAA4E;IAC5E,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACvD;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,YAAa,kBAAkB,KAAG,WAuD/D,CAAC"} |
| import { | ||
| getBatchResults | ||
| } from "../chunk-ab2d71gk.js"; | ||
| } from "../chunk-m4n610nm.js"; | ||
| import { | ||
@@ -8,4 +8,4 @@ createBatchPoller, | ||
| toEvent | ||
| } from "../chunk-zp2cxkyb.js"; | ||
| import"../chunk-kv3847wy.js"; | ||
| } from "../chunk-e6qn48qa.js"; | ||
| import"../chunk-g481f961.js"; | ||
| import"../chunk-v0bahtg2.js"; | ||
@@ -42,2 +42,5 @@ | ||
| const GET = (request) => { | ||
| if (!(options.cronSecret || options.allowUnauthenticatedCron)) { | ||
| return Promise.resolve(new Response("unauthorized", { status: 401 })); | ||
| } | ||
| if (options.cronSecret && request.headers.get("authorization") !== `Bearer ${options.cronSecret}`) { | ||
@@ -62,3 +65,3 @@ return Promise.resolve(new Response("unauthorized", { status: 401 })); | ||
| //# debugId=2EB8C739158F3A9164756E2164756E21 | ||
| //# debugId=5888D5A3C810FEE564756E2164756E21 | ||
| //# sourceMappingURL=index.js.map |
@@ -5,7 +5,7 @@ { | ||
| "sourcesContent": [ | ||
| "import { getBatchResults } from \"../batch\";\nimport { toEvent } from \"../server/events\";\nimport { createBatchPoller } from \"../server/poller\";\nimport type {\n CompletionSink,\n CredentialResolver,\n TrackTarget,\n} from \"../server/poller\";\nimport { createMemoryStore } from \"../server/store\";\nimport type {\n BatchStore,\n BatchWebhookEvent,\n TrackedBatch,\n} from \"../server/types\";\nimport type { BatchProvider, BatchResult, ProviderCredentials } from \"../types\";\n\nexport { createMemoryStore };\nexport type {\n BatchStore,\n BatchWebhookEvent,\n BatchResult,\n TrackedBatch,\n TrackTarget,\n};\n\n/**\n * Invoked once per batch when it reaches a terminal status. Persist the results\n * to your database here. `results` streams the parsed result lines for a\n * completed batch and is empty for failure events (`batch.failed` /\n * `batch.expired` / `batch.cancelled`) — inspect `event.type`.\n *\n * May fire more than once for the same batch (a cron tick racing the OpenAI\n * native webhook, or a retry after a partial save), so make persistence\n * idempotent — upsert keyed by `(provider, batchId, customId)`.\n */\nexport type OnBatchComplete = (\n event: BatchWebhookEvent,\n results: AsyncIterable<BatchResult>\n) => void | Promise<void>;\n\nexport interface BatchRoutesOptions {\n store: BatchStore;\n /** Called when each batch finishes; persist results here. */\n onComplete: OnBatchComplete;\n /** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */\n credentials?: CredentialResolver;\n /** When set, the cron `GET` requires `Authorization: Bearer <cronSecret>`. */\n cronSecret?: string;\n /** When set, mounts an OpenAI native-webhook handler on `POST`. */\n openaiSigningSecret?: string;\n /** Observe per-batch processing errors during a tick; the tick continues. */\n onError?: (record: TrackedBatch, error: unknown) => void;\n}\n\nexport interface BatchRoutes {\n /** Cron-triggered poll tick. Wire to Vercel Cron (or any scheduler). */\n GET: (request: Request) => Promise<Response>;\n /** OpenAI native-webhook handler. Present only with `openaiSigningSecret`. */\n POST?: (request: Request) => Promise<Response>;\n /** Register a submitted batch so the cron polls it (a `BatchJob` works). */\n track: (target: TrackTarget) => Promise<TrackedBatch>;\n}\n\n/** An already-exhausted async iterable, for non-completed events. */\nconst EMPTY_RESULTS: AsyncIterable<BatchResult> = {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.resolve({ done: true, value: undefined }),\n }),\n};\n\n/**\n * Build Next.js App Router route handlers that poll your in-flight batches on a\n * cron tick and invoke `onComplete` directly when each finishes — persist\n * results to your DB without round-tripping an HTTP webhook back to your app.\n *\n * @example\n * import { createBatchRoutes, createMemoryStore } from \"batchwork/next\";\n *\n * export const { GET, POST } = createBatchRoutes({\n * store: createMemoryStore(),\n * cronSecret: process.env.CRON_SECRET,\n * openaiSigningSecret: process.env.OPENAI_WEBHOOK_SECRET,\n * onComplete: async (event, results) => {\n * for await (const r of results) {\n * await db.insert({ id: r.customId, status: r.status, text: r.text });\n * }\n * },\n * });\n */\nexport const createBatchRoutes = (options: BatchRoutesOptions): BatchRoutes => {\n const resolveCredentials = (provider: BatchProvider): ProviderCredentials => {\n if (typeof options.credentials === \"function\") {\n return options.credentials(provider);\n }\n return options.credentials ?? {};\n };\n\n const sink: CompletionSink = async (record, snapshot) => {\n const event = toEvent(record.provider, snapshot);\n // Only completed batches have results to fetch — the adapter throws when a\n // terminal batch has no output/error file (failed/expired/cancelled).\n const results =\n event.type === \"batch.completed\"\n ? getBatchResults({\n id: record.id,\n provider: record.provider,\n ...resolveCredentials(record.provider),\n })\n : EMPTY_RESULTS;\n await options.onComplete(event, results);\n };\n\n const poller = createBatchPoller({\n credentials: options.credentials,\n onComplete: sink,\n // Always supply an `onError` so one failing batch can't abort the whole\n // tick; forward to the caller's handler when they provided one.\n onError: (record, error) => options.onError?.(record, error),\n store: options.store,\n });\n\n const GET = (request: Request): Promise<Response> => {\n if (\n options.cronSecret &&\n request.headers.get(\"authorization\") !== `Bearer ${options.cronSecret}`\n ) {\n return Promise.resolve(new Response(\"unauthorized\", { status: 401 }));\n }\n return poller.tick().then((result) => Response.json(result));\n };\n\n const track = (target: TrackTarget): Promise<TrackedBatch> =>\n poller.track(target, {});\n\n const routes: BatchRoutes = { GET, track };\n if (options.openaiSigningSecret) {\n routes.POST = poller.openaiWebhookHandler({\n signingSecret: options.openaiSigningSecret,\n });\n }\n return routes;\n};\n" | ||
| "import { getBatchResults } from \"../batch\";\nimport { toEvent } from \"../server/events\";\nimport { createBatchPoller } from \"../server/poller\";\nimport type {\n CompletionSink,\n CredentialResolver,\n TrackTarget,\n} from \"../server/poller\";\nimport { createMemoryStore } from \"../server/store\";\nimport type {\n BatchStore,\n BatchWebhookEvent,\n TrackedBatch,\n} from \"../server/types\";\nimport type { BatchProvider, BatchResult, ProviderCredentials } from \"../types\";\n\nexport { createMemoryStore };\nexport type {\n BatchStore,\n BatchWebhookEvent,\n BatchResult,\n TrackedBatch,\n TrackTarget,\n};\n\n/**\n * Invoked once per batch when it reaches a terminal status. Persist the results\n * to your database here. `results` streams the parsed result lines for a\n * completed batch and is empty for failure events (`batch.failed` /\n * `batch.expired` / `batch.cancelled`) — inspect `event.type`.\n *\n * May fire more than once for the same batch (a cron tick racing the OpenAI\n * native webhook, or a retry after a partial save), so make persistence\n * idempotent — upsert keyed by `(provider, batchId, customId)`.\n */\nexport type OnBatchComplete = (\n event: BatchWebhookEvent,\n results: AsyncIterable<BatchResult>\n) => void | Promise<void>;\n\nexport interface BatchRoutesOptions {\n store: BatchStore;\n /** Called when each batch finishes; persist results here. */\n onComplete: OnBatchComplete;\n /** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */\n credentials?: CredentialResolver;\n /** Requires `Authorization: Bearer <cronSecret>` on the cron `GET`. */\n cronSecret?: string;\n /** Allow unauthenticated cron ticks. Intended only for private/local routes. */\n allowUnauthenticatedCron?: boolean;\n /** When set, mounts an OpenAI native-webhook handler on `POST`. */\n openaiSigningSecret?: string;\n /** Observe per-batch processing errors during a tick; the tick continues. */\n onError?: (record: TrackedBatch, error: unknown) => void;\n}\n\nexport interface BatchRoutes {\n /** Cron-triggered poll tick. Wire to Vercel Cron (or any scheduler). */\n GET: (request: Request) => Promise<Response>;\n /** OpenAI native-webhook handler. Present only with `openaiSigningSecret`. */\n POST?: (request: Request) => Promise<Response>;\n /** Register a submitted batch so the cron polls it (a `BatchJob` works). */\n track: (target: TrackTarget) => Promise<TrackedBatch>;\n}\n\n/** An already-exhausted async iterable, for non-completed events. */\nconst EMPTY_RESULTS: AsyncIterable<BatchResult> = {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.resolve({ done: true, value: undefined }),\n }),\n};\n\n/**\n * Build Next.js App Router route handlers that poll your in-flight batches on a\n * cron tick and invoke `onComplete` directly when each finishes — persist\n * results to your DB without round-tripping an HTTP webhook back to your app.\n *\n * @example\n * import { createBatchRoutes, createMemoryStore } from \"batchwork/next\";\n *\n * export const { GET, POST } = createBatchRoutes({\n * store: createMemoryStore(),\n * cronSecret: process.env.CRON_SECRET,\n * openaiSigningSecret: process.env.OPENAI_WEBHOOK_SECRET,\n * onComplete: async (event, results) => {\n * for await (const r of results) {\n * await db.insert({ id: r.customId, status: r.status, text: r.text });\n * }\n * },\n * });\n */\nexport const createBatchRoutes = (options: BatchRoutesOptions): BatchRoutes => {\n const resolveCredentials = (provider: BatchProvider): ProviderCredentials => {\n if (typeof options.credentials === \"function\") {\n return options.credentials(provider);\n }\n return options.credentials ?? {};\n };\n\n const sink: CompletionSink = async (record, snapshot) => {\n const event = toEvent(record.provider, snapshot);\n // Only completed batches have results to fetch — the adapter throws when a\n // terminal batch has no output/error file (failed/expired/cancelled).\n const results =\n event.type === \"batch.completed\"\n ? getBatchResults({\n id: record.id,\n provider: record.provider,\n ...resolveCredentials(record.provider),\n })\n : EMPTY_RESULTS;\n await options.onComplete(event, results);\n };\n\n const poller = createBatchPoller({\n credentials: options.credentials,\n onComplete: sink,\n // Always supply an `onError` so one failing batch can't abort the whole\n // tick; forward to the caller's handler when they provided one.\n onError: (record, error) => options.onError?.(record, error),\n store: options.store,\n });\n\n const GET = (request: Request): Promise<Response> => {\n if (!(options.cronSecret || options.allowUnauthenticatedCron)) {\n return Promise.resolve(new Response(\"unauthorized\", { status: 401 }));\n }\n if (\n options.cronSecret &&\n request.headers.get(\"authorization\") !== `Bearer ${options.cronSecret}`\n ) {\n return Promise.resolve(new Response(\"unauthorized\", { status: 401 }));\n }\n return poller.tick().then((result) => Response.json(result));\n };\n\n const track = (target: TrackTarget): Promise<TrackedBatch> =>\n poller.track(target, {});\n\n const routes: BatchRoutes = { GET, track };\n if (options.openaiSigningSecret) {\n routes.POST = poller.openaiWebhookHandler({\n signingSecret: options.openaiSigningSecret,\n });\n }\n return routes;\n};\n" | ||
| ], | ||
| "mappings": ";;;;;;;;;;;;AAgEA,IAAM,gBAA4C;AAAA,GAC/C,OAAO,gBAAgB,OAAO;AAAA,IAC7B,MAAM,MAAM,QAAQ,QAAQ,EAAE,MAAM,MAAM,OAAO,UAAU,CAAC;AAAA,EAC9D;AACF;AAqBO,IAAM,oBAAoB,CAAC,YAA6C;AAAA,EAC7E,MAAM,qBAAqB,CAAC,aAAiD;AAAA,IAC3E,IAAI,OAAO,QAAQ,gBAAgB,YAAY;AAAA,MAC7C,OAAO,QAAQ,YAAY,QAAQ;AAAA,IACrC;AAAA,IACA,OAAO,QAAQ,eAAe,CAAC;AAAA;AAAA,EAGjC,MAAM,OAAuB,OAAO,QAAQ,aAAa;AAAA,IACvD,MAAM,QAAQ,QAAQ,OAAO,UAAU,QAAQ;AAAA,IAG/C,MAAM,UACJ,MAAM,SAAS,oBACX,gBAAgB;AAAA,MACd,IAAI,OAAO;AAAA,MACX,UAAU,OAAO;AAAA,SACd,mBAAmB,OAAO,QAAQ;AAAA,IACvC,CAAC,IACD;AAAA,IACN,MAAM,QAAQ,WAAW,OAAO,OAAO;AAAA;AAAA,EAGzC,MAAM,SAAS,kBAAkB;AAAA,IAC/B,aAAa,QAAQ;AAAA,IACrB,YAAY;AAAA,IAGZ,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,QAAQ,KAAK;AAAA,IAC3D,OAAO,QAAQ;AAAA,EACjB,CAAC;AAAA,EAED,MAAM,MAAM,CAAC,YAAwC;AAAA,IACnD,IACE,QAAQ,cACR,QAAQ,QAAQ,IAAI,eAAe,MAAM,UAAU,QAAQ,cAC3D;AAAA,MACA,OAAO,QAAQ,QAAQ,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC,CAAC;AAAA,IACtE;AAAA,IACA,OAAO,OAAO,KAAK,EAAE,KAAK,CAAC,WAAW,SAAS,KAAK,MAAM,CAAC;AAAA;AAAA,EAG7D,MAAM,QAAQ,CAAC,WACb,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,EAEzB,MAAM,SAAsB,EAAE,KAAK,MAAM;AAAA,EACzC,IAAI,QAAQ,qBAAqB;AAAA,IAC/B,OAAO,OAAO,OAAO,qBAAqB;AAAA,MACxC,eAAe,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA;", | ||
| "debugId": "2EB8C739158F3A9164756E2164756E21", | ||
| "mappings": ";;;;;;;;;;;;AAkEA,IAAM,gBAA4C;AAAA,GAC/C,OAAO,gBAAgB,OAAO;AAAA,IAC7B,MAAM,MAAM,QAAQ,QAAQ,EAAE,MAAM,MAAM,OAAO,UAAU,CAAC;AAAA,EAC9D;AACF;AAqBO,IAAM,oBAAoB,CAAC,YAA6C;AAAA,EAC7E,MAAM,qBAAqB,CAAC,aAAiD;AAAA,IAC3E,IAAI,OAAO,QAAQ,gBAAgB,YAAY;AAAA,MAC7C,OAAO,QAAQ,YAAY,QAAQ;AAAA,IACrC;AAAA,IACA,OAAO,QAAQ,eAAe,CAAC;AAAA;AAAA,EAGjC,MAAM,OAAuB,OAAO,QAAQ,aAAa;AAAA,IACvD,MAAM,QAAQ,QAAQ,OAAO,UAAU,QAAQ;AAAA,IAG/C,MAAM,UACJ,MAAM,SAAS,oBACX,gBAAgB;AAAA,MACd,IAAI,OAAO;AAAA,MACX,UAAU,OAAO;AAAA,SACd,mBAAmB,OAAO,QAAQ;AAAA,IACvC,CAAC,IACD;AAAA,IACN,MAAM,QAAQ,WAAW,OAAO,OAAO;AAAA;AAAA,EAGzC,MAAM,SAAS,kBAAkB;AAAA,IAC/B,aAAa,QAAQ;AAAA,IACrB,YAAY;AAAA,IAGZ,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,QAAQ,KAAK;AAAA,IAC3D,OAAO,QAAQ;AAAA,EACjB,CAAC;AAAA,EAED,MAAM,MAAM,CAAC,YAAwC;AAAA,IACnD,IAAI,EAAE,QAAQ,cAAc,QAAQ,2BAA2B;AAAA,MAC7D,OAAO,QAAQ,QAAQ,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC,CAAC;AAAA,IACtE;AAAA,IACA,IACE,QAAQ,cACR,QAAQ,QAAQ,IAAI,eAAe,MAAM,UAAU,QAAQ,cAC3D;AAAA,MACA,OAAO,QAAQ,QAAQ,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC,CAAC;AAAA,IACtE;AAAA,IACA,OAAO,OAAO,KAAK,EAAE,KAAK,CAAC,WAAW,SAAS,KAAK,MAAM,CAAC;AAAA;AAAA,EAG7D,MAAM,QAAQ,CAAC,WACb,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,EAEzB,MAAM,SAAsB,EAAE,KAAK,MAAM;AAAA,EACzC,IAAI,QAAQ,qBAAqB;AAAA,IAC/B,OAAO,OAAO,OAAO,qBAAqB;AAAA,MACxC,eAAe,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA;", | ||
| "debugId": "5888D5A3C810FEE564756E2164756E21", | ||
| "names": [] | ||
| } |
| import type { BuiltRequest } from "../body"; | ||
| import type { BatchProvider, BatchResult, BatchSnapshot, ProviderCredentials } from "../types"; | ||
| import type { BatchLimits, BatchProvider, BatchResult, BatchSnapshot, ProviderCredentials } from "../types"; | ||
| export interface SubmitInput { | ||
@@ -8,2 +8,3 @@ built: BuiltRequest[]; | ||
| endpoint: string; | ||
| limits?: BatchLimits; | ||
| metadata?: Record<string, string>; | ||
@@ -10,0 +11,0 @@ /** |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/providers/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,mBAAmB,CAAC;IACjC,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IAC3B,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,mBAAmB,KAC7B,cAAc,CAAC,WAAW,CAAC,CAAC;IACjC,QAAQ,EAAE,CACR,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,mBAAmB,KAC7B,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACxD"} | ||
| {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/providers/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,mBAAmB,CAAC;IACjC,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IAC3B,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,mBAAmB,KAC7B,cAAc,CAAC,WAAW,CAAC,CAAC;IACjC,QAAQ,EAAE,CACR,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,mBAAmB,KAC7B,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACxD"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAqL3D,eAAO,MAAM,gBAAgB,EAAE,YAM9B,CAAC"} | ||
| {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAyN3D,eAAO,MAAM,gBAAgB,EAAE,YAM9B,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AA6N3D;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,YAM3B,CAAC"} | ||
| {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AA+O3D;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,YAM3B,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mistral.d.ts","sourceRoot":"","sources":["../../src/providers/mistral.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAuI3D;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,YAM5B,CAAC"} | ||
| {"version":3,"file":"mistral.d.ts","sourceRoot":"","sources":["../../src/providers/mistral.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAqJ3D;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,YAM5B,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"openai-compatible.d.ts","sourceRoot":"","sources":["../../src/providers/openai-compatible.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAI3D,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,YAAY,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAEhB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,aAAa,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAClD;AAgDD,eAAO,MAAM,6BAA6B,WAChC,sBAAsB,KAC7B,YAgGF,CAAC"} | ||
| {"version":3,"file":"openai-compatible.d.ts","sourceRoot":"","sources":["../../src/providers/openai-compatible.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAK3D,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,YAAY,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAEhB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,aAAa,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAClD;AAgDD,eAAO,MAAM,6BAA6B,WAChC,sBAAsB,KAC7B,YA+GF,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"together.d.ts","sourceRoot":"","sources":["../../src/providers/together.ts"],"names":[],"mappings":"AAkEA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kCAQ1B,CAAC"} | ||
| {"version":3,"file":"together.d.ts","sourceRoot":"","sources":["../../src/providers/together.ts"],"names":[],"mappings":"AAyIA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kCAQ1B,CAAC"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../src/providers/xai.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAyK3D;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,YAMxB,CAAC"} | ||
| {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../src/providers/xai.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAgL3D;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,YAMxB,CAAC"} |
| export { createBatchPoller } from "./poller"; | ||
| export type { BatchPoller, BatchPollerOptions, CompletionSink, CredentialResolver, OpenAIWebhookOptions, TickResult, TrackOptions, TrackTarget, } from "./poller"; | ||
| export type { BatchPoller, BatchPollerOptions, CompletionSink, CredentialResolver, OpenAIWebhookOptions, TickResult, TrackOptions, TrackTarget, WebhookUrlValidator, } from "./poller"; | ||
| export { signWebhook, verifyBatchWebhook, verifyWebhook } from "./signing"; | ||
| export type { VerifiedWebhook } from "./signing"; | ||
| export type { VerifiedWebhook, VerifyWebhookOptions, WebhookReplayStore, } from "./signing"; | ||
| export { createMemoryStore } from "./store"; | ||
| export type { BatchStore, BatchWebhookEvent, BatchWebhookEventType, TrackedBatch, } from "./types"; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,GACb,MAAM,SAAS,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,GACb,MAAM,SAAS,CAAC"} |
@@ -7,4 +7,4 @@ import { | ||
| verifyWebhook | ||
| } from "../chunk-zp2cxkyb.js"; | ||
| import"../chunk-kv3847wy.js"; | ||
| } from "../chunk-e6qn48qa.js"; | ||
| import"../chunk-g481f961.js"; | ||
| import"../chunk-v0bahtg2.js"; | ||
@@ -11,0 +11,0 @@ export { |
@@ -10,2 +10,3 @@ import type { BatchProvider, BatchSnapshot, BatchStatus, ProviderCredentials } from "../types"; | ||
| export type CompletionSink = (record: TrackedBatch, snapshot: BatchSnapshot) => Promise<void>; | ||
| export type WebhookUrlValidator = (url: URL) => void | Promise<void>; | ||
| export interface BatchPollerOptions { | ||
@@ -17,2 +18,4 @@ store: BatchStore; | ||
| onComplete?: CompletionSink; | ||
| /** Override the default webhook URL policy with an application allowlist. */ | ||
| validateWebhookUrl?: WebhookUrlValidator; | ||
| /** | ||
@@ -19,0 +22,0 @@ * Called when processing a single batch throws during `tick`. When provided, |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/server/poller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACX,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAExD,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,CAAC,CAAC,QAAQ,EAAE,aAAa,KAAK,mBAAmB,CAAC,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7E,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,oBAAoB,EAAE,CACpB,OAAO,EAAE,oBAAoB,KAC1B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9C;AAoCD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,YAAa,kBAAkB,KAAG,WA2H/D,CAAC"} | ||
| {"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/server/poller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACX,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAExD,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,CAAC,CAAC,QAAQ,EAAE,aAAa,KAAK,mBAAmB,CAAC,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;IACzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7E,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,oBAAoB,EAAE,CACpB,OAAO,EAAE,oBAAoB,KAC1B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9C;AA8HD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,YAAa,kBAAkB,KAAG,WAiI/D,CAAC"} |
| import type { BatchWebhookEvent } from "./types"; | ||
| export interface WebhookReplayStore { | ||
| get: (id: string) => number | Promise<number | undefined> | undefined; | ||
| set: (id: string, expiresAt: number) => Promise<void> | void; | ||
| } | ||
| export interface VerifyWebhookOptions { | ||
| replayStore?: WebhookReplayStore; | ||
| } | ||
| /** Build Standard Webhooks signature headers for an outbound delivery. */ | ||
@@ -14,3 +21,3 @@ export declare const signWebhook: (secret: string, id: string, body: string, timestampSeconds: number) => Promise<Record<string, string>>; | ||
| */ | ||
| export declare const verifyWebhook: (request: Request, secret: string) => Promise<VerifiedWebhook>; | ||
| export declare const verifyWebhook: (request: Request, secret: string, options?: VerifyWebhookOptions) => Promise<VerifiedWebhook>; | ||
| /** | ||
@@ -20,3 +27,3 @@ * Verify and parse a batchwork webhook on your receiving endpoint. Returns the | ||
| */ | ||
| export declare const verifyBatchWebhook: (request: Request, secret: string) => Promise<BatchWebhookEvent>; | ||
| export declare const verifyBatchWebhook: (request: Request, secret: string, options?: VerifyWebhookOptions) => Promise<BatchWebhookEvent>; | ||
| //# sourceMappingURL=signing.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/server/signing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AA8DjD,0EAA0E;AAC1E,eAAO,MAAM,WAAW,WACd,MAAM,MACV,MAAM,QACJ,MAAM,oBACM,MAAM,KACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQhC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,YACf,OAAO,UACR,MAAM,KACb,OAAO,CAAC,eAAe,CAsCzB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,YACpB,OAAO,UACR,MAAM,KACb,OAAO,CAAC,iBAAiB,CAG3B,CAAC"} | ||
| {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/server/signing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAWjD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACtE,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAsFD,0EAA0E;AAC1E,eAAO,MAAM,WAAW,WACd,MAAM,MACV,MAAM,QACJ,MAAM,oBACM,MAAM,KACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQhC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,YACf,OAAO,UACR,MAAM,YACJ,oBAAoB,KAC7B,OAAO,CAAC,eAAe,CA8CzB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,YACpB,OAAO,UACR,MAAM,YACJ,oBAAoB,KAC7B,OAAO,CAAC,iBAAiB,CAG3B,CAAC"} |
+7
-0
@@ -93,5 +93,12 @@ import type { JSONValue, LanguageModel, ModelMessage, ToolChoice, ToolSet } from "ai"; | ||
| } | ||
| export interface BatchLimits { | ||
| captureConcurrency?: number; | ||
| maxRequests?: number; | ||
| maxRequestBytes?: number; | ||
| maxUploadBytes?: number; | ||
| } | ||
| /** Input to `batch()`. */ | ||
| export interface BatchOptions extends ProviderCredentials { | ||
| defaults?: BatchDefaults; | ||
| limits?: BatchLimits; | ||
| metadata?: Record<string, string>; | ||
@@ -98,0 +105,0 @@ model: LanguageModel; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} |
+1
-1
| { | ||
| "name": "batchwork", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "description": "Unified batch API for AI providers — low-cost LLM batch processing at scale.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
| import { | ||
| BatchJob, | ||
| BatchworkError, | ||
| MissingDependencyError, | ||
| UnsupportedProviderError, | ||
| getAdapter | ||
| } from "./chunk-kv3847wy.js"; | ||
| import { | ||
| __require | ||
| } from "./chunk-v0bahtg2.js"; | ||
| // src/model.ts | ||
| var CAPTURE_API_KEY = "batchwork-capture"; | ||
| var PACKAGE_BY_PROVIDER = { | ||
| anthropic: { label: "Anthropic", specifier: "@ai-sdk/anthropic" }, | ||
| google: { label: "Google Gemini", specifier: "@ai-sdk/google" }, | ||
| groq: { label: "Groq", specifier: "@ai-sdk/groq" }, | ||
| mistral: { label: "Mistral", specifier: "@ai-sdk/mistral" }, | ||
| openai: { label: "OpenAI", specifier: "@ai-sdk/openai" }, | ||
| together: { label: "Together AI", specifier: "@ai-sdk/togetherai" }, | ||
| xai: { label: "xAI", specifier: "@ai-sdk/xai" } | ||
| }; | ||
| var PROVIDER_BY_FAMILY = { | ||
| anthropic: "anthropic", | ||
| google: "google", | ||
| groq: "groq", | ||
| mistral: "mistral", | ||
| openai: "openai", | ||
| together: "together", | ||
| togetherai: "together", | ||
| xai: "xai" | ||
| }; | ||
| var PROVIDER_BY_ALIAS = { | ||
| ...PROVIDER_BY_FAMILY, | ||
| gemini: "google" | ||
| }; | ||
| var splitOnce = (value, separator) => { | ||
| const index = value.indexOf(separator); | ||
| if (index === -1) { | ||
| return [value, ""]; | ||
| } | ||
| return [value.slice(0, index), value.slice(index + separator.length)]; | ||
| }; | ||
| var openaiKind = (suffix) => { | ||
| if (suffix === "responses") { | ||
| return "responses"; | ||
| } | ||
| if (suffix === "completion") { | ||
| return "completion"; | ||
| } | ||
| return "chat"; | ||
| }; | ||
| var resolveModelString = (value) => { | ||
| const [providerId, modelId] = splitOnce(value, "/"); | ||
| if (modelId === "") { | ||
| throw new UnsupportedProviderError(value); | ||
| } | ||
| const provider = PROVIDER_BY_ALIAS[providerId]; | ||
| if (!provider) { | ||
| throw new UnsupportedProviderError(providerId); | ||
| } | ||
| return { kind: "chat", modelId, provider }; | ||
| }; | ||
| var resolveModel = (model) => { | ||
| if (typeof model === "string") { | ||
| return resolveModelString(model); | ||
| } | ||
| const [family, suffix] = splitOnce(model.provider, "."); | ||
| const provider = PROVIDER_BY_FAMILY[family]; | ||
| if (provider === "openai") { | ||
| return { kind: openaiKind(suffix), modelId: model.modelId, provider }; | ||
| } | ||
| if (provider) { | ||
| return { kind: "chat", modelId: model.modelId, provider }; | ||
| } | ||
| if (model.modelId.includes("/")) { | ||
| return resolveModelString(model.modelId); | ||
| } | ||
| throw new UnsupportedProviderError(model.provider); | ||
| }; | ||
| var importProvider = (provider) => { | ||
| switch (provider) { | ||
| case "anthropic": { | ||
| return import("@ai-sdk/anthropic"); | ||
| } | ||
| case "google": { | ||
| return import("@ai-sdk/google"); | ||
| } | ||
| case "groq": { | ||
| return import("@ai-sdk/groq"); | ||
| } | ||
| case "mistral": { | ||
| return import("@ai-sdk/mistral"); | ||
| } | ||
| case "openai": { | ||
| return import("@ai-sdk/openai"); | ||
| } | ||
| case "together": { | ||
| return import("@ai-sdk/togetherai"); | ||
| } | ||
| case "xai": { | ||
| return import("@ai-sdk/xai"); | ||
| } | ||
| default: { | ||
| return Promise.reject(new UnsupportedProviderError(provider)); | ||
| } | ||
| } | ||
| }; | ||
| var loadProvider = async (provider, load = importProvider) => { | ||
| try { | ||
| return await load(provider); | ||
| } catch (error) { | ||
| if (error instanceof UnsupportedProviderError) { | ||
| throw error; | ||
| } | ||
| const { specifier, label } = PACKAGE_BY_PROVIDER[provider]; | ||
| throw new MissingDependencyError(specifier, label); | ||
| } | ||
| }; | ||
| var createCaptureModel = async (resolved, credentials, fetchImpl) => { | ||
| const settings = { | ||
| apiKey: credentials.apiKey ?? CAPTURE_API_KEY, | ||
| baseURL: credentials.baseURL, | ||
| fetch: fetchImpl, | ||
| headers: credentials.headers | ||
| }; | ||
| switch (resolved.provider) { | ||
| case "openai": { | ||
| const { createOpenAI } = await loadProvider("openai"); | ||
| const provider = createOpenAI(settings); | ||
| if (resolved.kind === "responses") { | ||
| return provider.responses(resolved.modelId); | ||
| } | ||
| if (resolved.kind === "completion") { | ||
| return provider.completion(resolved.modelId); | ||
| } | ||
| return provider.chat(resolved.modelId); | ||
| } | ||
| case "anthropic": { | ||
| const { createAnthropic } = await loadProvider("anthropic"); | ||
| return createAnthropic(settings).messages(resolved.modelId); | ||
| } | ||
| case "groq": { | ||
| const { createGroq } = await loadProvider("groq"); | ||
| return createGroq(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "mistral": { | ||
| const { createMistral } = await loadProvider("mistral"); | ||
| return createMistral(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "google": { | ||
| const { createGoogleGenerativeAI } = await loadProvider("google"); | ||
| return createGoogleGenerativeAI(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "xai": { | ||
| const { createXai } = await loadProvider("xai"); | ||
| return createXai(settings).languageModel(resolved.modelId); | ||
| } | ||
| case "together": { | ||
| const { createTogetherAI } = await loadProvider("together"); | ||
| return createTogetherAI(settings).languageModel(resolved.modelId); | ||
| } | ||
| default: { | ||
| throw new UnsupportedProviderError(resolved.provider); | ||
| } | ||
| } | ||
| }; | ||
| // src/body.ts | ||
| import { generateText } from "ai"; | ||
| var MAX_CAUSE_DEPTH = 10; | ||
| class CaptureSignalError extends Error { | ||
| url; | ||
| rawBody; | ||
| constructor(url, rawBody) { | ||
| super("batchwork:capture"); | ||
| this.name = "CaptureSignalError"; | ||
| this.url = url; | ||
| this.rawBody = rawBody; | ||
| } | ||
| } | ||
| var resolveUrl = (input) => { | ||
| if (typeof input === "string") { | ||
| return input; | ||
| } | ||
| if (input instanceof URL) { | ||
| return input.toString(); | ||
| } | ||
| return input.url; | ||
| }; | ||
| var extractBody = (init) => { | ||
| const body = init?.body; | ||
| if (typeof body === "string") { | ||
| return body; | ||
| } | ||
| if (body instanceof Uint8Array) { | ||
| return new TextDecoder().decode(body); | ||
| } | ||
| throw new BatchworkError("batchwork: unable to read the provider request body during capture."); | ||
| }; | ||
| var captureFetch = (input, init) => Promise.reject(new CaptureSignalError(resolveUrl(input), extractBody(init))); | ||
| var findCapture = (error) => { | ||
| let current = error; | ||
| let depth = 0; | ||
| while (current && depth < MAX_CAUSE_DEPTH) { | ||
| if (current instanceof CaptureSignalError) { | ||
| return current; | ||
| } | ||
| current = current.cause; | ||
| depth += 1; | ||
| } | ||
| }; | ||
| var endpointFromUrl = (url) => { | ||
| try { | ||
| return new URL(url).pathname; | ||
| } catch { | ||
| return url; | ||
| } | ||
| }; | ||
| var mergeDefaults = (request, defaults) => { | ||
| if (!defaults) { | ||
| return request; | ||
| } | ||
| return { ...defaults, ...request }; | ||
| }; | ||
| var toGenerateInput = (model, request) => ({ | ||
| frequencyPenalty: request.frequencyPenalty, | ||
| maxOutputTokens: request.maxOutputTokens, | ||
| maxRetries: 0, | ||
| messages: request.messages, | ||
| model, | ||
| presencePenalty: request.presencePenalty, | ||
| prompt: request.prompt, | ||
| providerOptions: request.providerOptions, | ||
| seed: request.seed, | ||
| stopSequences: request.stopSequences, | ||
| system: request.system, | ||
| temperature: request.temperature, | ||
| toolChoice: request.toolChoice, | ||
| tools: request.tools, | ||
| topK: request.topK, | ||
| topP: request.topP | ||
| }); | ||
| var captureOne = async (model, request, customId) => { | ||
| try { | ||
| await generateText(toGenerateInput(model, request)); | ||
| } catch (error) { | ||
| const capture = findCapture(error); | ||
| if (capture) { | ||
| return { | ||
| body: JSON.parse(capture.rawBody), | ||
| customId, | ||
| endpoint: endpointFromUrl(capture.url) | ||
| }; | ||
| } | ||
| throw error; | ||
| } | ||
| throw new BatchworkError("batchwork: the request was not intercepted while building the batch body."); | ||
| }; | ||
| var buildRequestBodies = async (resolved, requests, defaults, credentials) => { | ||
| const model = await createCaptureModel(resolved, credentials, captureFetch); | ||
| const seen = new Set; | ||
| const items = requests.map((request, index) => { | ||
| const customId = request.customId ?? `request-${index}`; | ||
| if (seen.has(customId)) { | ||
| throw new BatchworkError(`batchwork: duplicate customId "${customId}". customId values must be unique within a batch.`); | ||
| } | ||
| seen.add(customId); | ||
| return { customId, request }; | ||
| }); | ||
| return await Promise.all(items.map((item) => captureOne(model, mergeDefaults(item.request, defaults), item.customId))); | ||
| }; | ||
| // src/batch.ts | ||
| var pickCredentials = (source) => ({ | ||
| apiKey: source.apiKey, | ||
| baseURL: source.baseURL, | ||
| headers: source.headers | ||
| }); | ||
| var providerFromRef = (ref) => { | ||
| if (ref.provider) { | ||
| return ref.provider; | ||
| } | ||
| if (ref.model !== undefined) { | ||
| return resolveModel(ref.model).provider; | ||
| } | ||
| throw new BatchworkError("batchwork: provide `provider` or `model` to identify the batch."); | ||
| }; | ||
| var batch = async (options) => { | ||
| if (options.requests.length === 0) { | ||
| throw new BatchworkError("batchwork: `requests` must not be empty."); | ||
| } | ||
| const resolved = resolveModel(options.model); | ||
| const credentials = pickCredentials(options); | ||
| const adapter = getAdapter(resolved.provider); | ||
| const built = await buildRequestBodies(resolved, options.requests, options.defaults, credentials); | ||
| const snapshot = await adapter.submit({ | ||
| built, | ||
| credentials, | ||
| endpoint: built[0]?.endpoint ?? "", | ||
| metadata: options.metadata, | ||
| modelId: resolved.modelId | ||
| }); | ||
| return new BatchJob(adapter, credentials, snapshot); | ||
| }; | ||
| var getBatch = async (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| const credentials = pickCredentials(ref); | ||
| const snapshot = await adapter.retrieve(ref.id, credentials); | ||
| return new BatchJob(adapter, credentials, snapshot); | ||
| }; | ||
| var getBatchResults = (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| return adapter.results(ref.id, pickCredentials(ref)); | ||
| }; | ||
| var cancelBatch = async (ref) => { | ||
| const adapter = getAdapter(providerFromRef(ref)); | ||
| await adapter.cancel(ref.id, pickCredentials(ref)); | ||
| }; | ||
| export { resolveModel, batch, getBatch, getBatchResults, cancelBatch }; | ||
| //# debugId=A3005BE7078C61CE64756E2164756E21 | ||
| //# sourceMappingURL=chunk-ab2d71gk.js.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/model.ts", "../src/body.ts", "../src/batch.ts"], | ||
| "sourcesContent": [ | ||
| "import type * as AnthropicModule from \"@ai-sdk/anthropic\";\nimport type * as GoogleModule from \"@ai-sdk/google\";\nimport type * as GroqModule from \"@ai-sdk/groq\";\nimport type * as MistralModule from \"@ai-sdk/mistral\";\nimport type * as OpenAIModule from \"@ai-sdk/openai\";\nimport type * as TogetherModule from \"@ai-sdk/togetherai\";\nimport type * as XaiModule from \"@ai-sdk/xai\";\nimport type { LanguageModel } from \"ai\";\n\nimport { MissingDependencyError, UnsupportedProviderError } from \"./errors\";\nimport type { BatchProvider, ProviderCredentials } from \"./types\";\n\n/** A fetch implementation compatible with the AI SDK provider `fetch` option. */\nexport type CapturingFetch = typeof globalThis.fetch;\n\n/** OpenAI exposes several request shapes; we mirror the one the model implies. */\nexport type OpenAIModelKind = \"chat\" | \"responses\" | \"completion\";\n\nexport interface ResolvedModel {\n /** Relevant for OpenAI; other providers always use a single chat endpoint. */\n kind: OpenAIModelKind;\n modelId: string;\n provider: BatchProvider;\n}\n\n/**\n * Placeholder API key used only when building request bodies. Body building\n * intercepts the request before it is sent, so no real credential is needed —\n * but the provider refuses to construct a model without one.\n */\nconst CAPTURE_API_KEY = \"batchwork-capture\";\n\n/** The optional `@ai-sdk/*` package backing each provider. */\nconst PACKAGE_BY_PROVIDER: Record<\n BatchProvider,\n { label: string; specifier: string }\n> = {\n anthropic: { label: \"Anthropic\", specifier: \"@ai-sdk/anthropic\" },\n google: { label: \"Google Gemini\", specifier: \"@ai-sdk/google\" },\n groq: { label: \"Groq\", specifier: \"@ai-sdk/groq\" },\n mistral: { label: \"Mistral\", specifier: \"@ai-sdk/mistral\" },\n openai: { label: \"OpenAI\", specifier: \"@ai-sdk/openai\" },\n together: { label: \"Together AI\", specifier: \"@ai-sdk/togetherai\" },\n xai: { label: \"xAI\", specifier: \"@ai-sdk/xai\" },\n};\n\n/**\n * AI SDK provider id prefixes (the part before the first `.` in `model.provider`)\n * mapped to batch providers.\n */\nconst PROVIDER_BY_FAMILY: Record<string, BatchProvider> = {\n anthropic: \"anthropic\",\n google: \"google\",\n groq: \"groq\",\n mistral: \"mistral\",\n openai: \"openai\",\n together: \"together\",\n togetherai: \"together\",\n xai: \"xai\",\n};\n\n/** Aliases accepted in the `\"provider/model\"` string form. */\nconst PROVIDER_BY_ALIAS: Record<string, BatchProvider> = {\n ...PROVIDER_BY_FAMILY,\n gemini: \"google\",\n};\n\nconst splitOnce = (value: string, separator: string): [string, string] => {\n const index = value.indexOf(separator);\n if (index === -1) {\n return [value, \"\"];\n }\n return [value.slice(0, index), value.slice(index + separator.length)];\n};\n\nconst openaiKind = (suffix: string): OpenAIModelKind => {\n if (suffix === \"responses\") {\n return \"responses\";\n }\n if (suffix === \"completion\") {\n return \"completion\";\n }\n // Default to chat completions: the most widely supported batch endpoint.\n return \"chat\";\n};\n\n/** Resolve a `\"provider/model\"` string into a provider + model id. */\nconst resolveModelString = (value: string): ResolvedModel => {\n const [providerId, modelId] = splitOnce(value, \"/\");\n if (modelId === \"\") {\n throw new UnsupportedProviderError(value);\n }\n const provider = PROVIDER_BY_ALIAS[providerId];\n if (!provider) {\n throw new UnsupportedProviderError(providerId);\n }\n return { kind: \"chat\", modelId, provider };\n};\n\n/**\n * Resolve any AI SDK `model` (a `\"provider/model\"` string or a provider model\n * object such as `openai(\"gpt-5.5\")`) to a provider + model id + request\n * shape. Gateway/registry model objects whose `modelId` is itself\n * `\"provider/model\"` are also handled.\n */\nexport const resolveModel = (model: LanguageModel): ResolvedModel => {\n if (typeof model === \"string\") {\n return resolveModelString(model);\n }\n\n const [family, suffix] = splitOnce(model.provider, \".\");\n const provider = PROVIDER_BY_FAMILY[family];\n if (provider === \"openai\") {\n return { kind: openaiKind(suffix), modelId: model.modelId, provider };\n }\n if (provider) {\n return { kind: \"chat\", modelId: model.modelId, provider };\n }\n // Gateway/registry providers carry the real target in the model id.\n if (model.modelId.includes(\"/\")) {\n return resolveModelString(model.modelId);\n }\n throw new UnsupportedProviderError(model.provider);\n};\n\nconst importProvider = (provider: BatchProvider): Promise<unknown> => {\n switch (provider) {\n case \"anthropic\": {\n return import(\"@ai-sdk/anthropic\");\n }\n case \"google\": {\n return import(\"@ai-sdk/google\");\n }\n case \"groq\": {\n return import(\"@ai-sdk/groq\");\n }\n case \"mistral\": {\n return import(\"@ai-sdk/mistral\");\n }\n case \"openai\": {\n return import(\"@ai-sdk/openai\");\n }\n case \"together\": {\n return import(\"@ai-sdk/togetherai\");\n }\n case \"xai\": {\n return import(\"@ai-sdk/xai\");\n }\n default: {\n return Promise.reject(new UnsupportedProviderError(provider));\n }\n }\n};\n\n/**\n * Import the `@ai-sdk/*` package for a provider, translating a missing optional\n * dependency into a `MissingDependencyError`. The importer is injectable (like\n * the capturing `fetch`) so tests can drive the failure paths without\n * uninstalling a package. Exported for testing; not part of the public API.\n */\nexport const loadProvider = async <T>(\n provider: BatchProvider,\n load: (target: BatchProvider) => Promise<unknown> = importProvider\n): Promise<T> => {\n try {\n return (await load(provider)) as T;\n } catch (error) {\n if (error instanceof UnsupportedProviderError) {\n throw error;\n }\n const { specifier, label } = PACKAGE_BY_PROVIDER[provider];\n throw new MissingDependencyError(specifier, label);\n }\n};\n\n/**\n * Construct an AI SDK model wired to a capturing `fetch`, used to derive the\n * provider request body for each batch item without making a network call.\n */\nexport const createCaptureModel = async (\n resolved: ResolvedModel,\n credentials: ProviderCredentials,\n fetchImpl: CapturingFetch\n): Promise<LanguageModel> => {\n const settings = {\n apiKey: credentials.apiKey ?? CAPTURE_API_KEY,\n baseURL: credentials.baseURL,\n fetch: fetchImpl,\n headers: credentials.headers,\n };\n\n switch (resolved.provider) {\n case \"openai\": {\n const { createOpenAI } =\n await loadProvider<typeof OpenAIModule>(\"openai\");\n const provider = createOpenAI(settings);\n if (resolved.kind === \"responses\") {\n return provider.responses(resolved.modelId);\n }\n if (resolved.kind === \"completion\") {\n return provider.completion(resolved.modelId);\n }\n return provider.chat(resolved.modelId);\n }\n case \"anthropic\": {\n const { createAnthropic } =\n await loadProvider<typeof AnthropicModule>(\"anthropic\");\n return createAnthropic(settings).messages(resolved.modelId);\n }\n case \"groq\": {\n const { createGroq } = await loadProvider<typeof GroqModule>(\"groq\");\n return createGroq(settings).languageModel(resolved.modelId);\n }\n case \"mistral\": {\n const { createMistral } =\n await loadProvider<typeof MistralModule>(\"mistral\");\n return createMistral(settings).languageModel(resolved.modelId);\n }\n case \"google\": {\n const { createGoogleGenerativeAI } =\n await loadProvider<typeof GoogleModule>(\"google\");\n return createGoogleGenerativeAI(settings).languageModel(resolved.modelId);\n }\n case \"xai\": {\n const { createXai } = await loadProvider<typeof XaiModule>(\"xai\");\n return createXai(settings).languageModel(resolved.modelId);\n }\n case \"together\": {\n const { createTogetherAI } =\n await loadProvider<typeof TogetherModule>(\"together\");\n return createTogetherAI(settings).languageModel(resolved.modelId);\n }\n default: {\n throw new UnsupportedProviderError(resolved.provider);\n }\n }\n};\n", | ||
| "import { generateText } from \"ai\";\nimport type { LanguageModel } from \"ai\";\n\nimport { BatchworkError } from \"./errors\";\nimport { createCaptureModel } from \"./model\";\nimport type { CapturingFetch, ResolvedModel } from \"./model\";\nimport type { BatchDefaults, BatchRequest, ProviderCredentials } from \"./types\";\n\ntype GenerateTextInput = Parameters<typeof generateText>[0];\n\n/** A provider request body derived from a single batch item. */\nexport interface BuiltRequest {\n /** The serialized provider request body (becomes the batch line). */\n body: Record<string, unknown>;\n customId: string;\n /** API endpoint path the model targets, e.g. `/v1/chat/completions`. */\n endpoint: string;\n}\n\nconst MAX_CAUSE_DEPTH = 10;\n\n/**\n * Thrown by the capturing `fetch` to abort the request after its body has been\n * serialized. The body travels inside the error (not shared state), so capture\n * is correct even under concurrency.\n */\nclass CaptureSignalError extends Error {\n readonly url: string;\n readonly rawBody: string;\n\n constructor(url: string, rawBody: string) {\n super(\"batchwork:capture\");\n this.name = \"CaptureSignalError\";\n this.url = url;\n this.rawBody = rawBody;\n }\n}\n\nconst resolveUrl = (input: string | URL | Request): string => {\n if (typeof input === \"string\") {\n return input;\n }\n if (input instanceof URL) {\n return input.toString();\n }\n return input.url;\n};\n\nconst extractBody = (init?: RequestInit): string => {\n const body = init?.body;\n if (typeof body === \"string\") {\n return body;\n }\n if (body instanceof Uint8Array) {\n return new TextDecoder().decode(body);\n }\n throw new BatchworkError(\n \"batchwork: unable to read the provider request body during capture.\"\n );\n};\n\n// `CapturingFetch` is `typeof fetch`, whose shape varies by runtime types (e.g.\n// Bun adds a required `preconnect` method). We only ever call it as a plain\n// fetch, so cast the bare implementation rather than stub the extra members.\nconst captureFetch = ((input: string | URL | Request, init?: RequestInit) =>\n Promise.reject(\n new CaptureSignalError(resolveUrl(input), extractBody(init))\n )) as unknown as CapturingFetch;\n\nconst findCapture = (error: unknown): CaptureSignalError | undefined => {\n let current: unknown = error;\n let depth = 0;\n while (current && depth < MAX_CAUSE_DEPTH) {\n if (current instanceof CaptureSignalError) {\n return current;\n }\n current = (current as { cause?: unknown }).cause;\n depth += 1;\n }\n};\n\nconst endpointFromUrl = (url: string): string => {\n try {\n return new URL(url).pathname;\n } catch {\n return url;\n }\n};\n\nconst mergeDefaults = (\n request: BatchRequest,\n defaults: BatchDefaults | undefined\n): BatchRequest => {\n if (!defaults) {\n return request;\n }\n return { ...defaults, ...request };\n};\n\n/**\n * Map a batch request to AI SDK `generateText` input. Fields are listed\n * explicitly so `customId` never leaks into the provider request.\n */\nconst toGenerateInput = (\n model: LanguageModel,\n request: BatchRequest\n): GenerateTextInput =>\n // `prompt`/`messages` form a discriminated union in the AI SDK types; we\n // pass both keys and let `generateText` validate the XOR at runtime.\n ({\n frequencyPenalty: request.frequencyPenalty,\n maxOutputTokens: request.maxOutputTokens,\n maxRetries: 0,\n messages: request.messages,\n model,\n presencePenalty: request.presencePenalty,\n prompt: request.prompt,\n providerOptions: request.providerOptions,\n seed: request.seed,\n stopSequences: request.stopSequences,\n system: request.system,\n temperature: request.temperature,\n toolChoice: request.toolChoice,\n tools: request.tools,\n topK: request.topK,\n topP: request.topP,\n }) as GenerateTextInput;\n\nconst captureOne = async (\n model: LanguageModel,\n request: BatchRequest,\n customId: string\n): Promise<BuiltRequest> => {\n try {\n await generateText(toGenerateInput(model, request));\n } catch (error) {\n const capture = findCapture(error);\n if (capture) {\n return {\n body: JSON.parse(capture.rawBody) as Record<string, unknown>,\n customId,\n endpoint: endpointFromUrl(capture.url),\n };\n }\n // A genuine failure (e.g. invalid prompt) — surface it to the caller.\n throw error;\n }\n throw new BatchworkError(\n \"batchwork: the request was not intercepted while building the batch body.\"\n );\n};\n\n/**\n * Derive provider request bodies for every batch item by running each through\n * the AI SDK with a capturing `fetch`. This reuses the AI SDK's full message,\n * tool, and multimodal conversion, so the body matches what `generateText`\n * would send — minus the network call.\n */\nexport const buildRequestBodies = async (\n resolved: ResolvedModel,\n requests: readonly BatchRequest[],\n defaults: BatchDefaults | undefined,\n credentials: ProviderCredentials\n): Promise<BuiltRequest[]> => {\n const model = await createCaptureModel(resolved, credentials, captureFetch);\n const seen = new Set<string>();\n\n // Assign and validate customIds up front (sequentially, so duplicates are\n // reported deterministically) before capturing bodies in parallel.\n const items = requests.map((request, index) => {\n const customId = request.customId ?? `request-${index}`;\n if (seen.has(customId)) {\n throw new BatchworkError(\n `batchwork: duplicate customId \"${customId}\". customId values must be unique within a batch.`\n );\n }\n seen.add(customId);\n return { customId, request };\n });\n\n return await Promise.all(\n items.map((item) =>\n captureOne(model, mergeDefaults(item.request, defaults), item.customId)\n )\n );\n};\n", | ||
| "import { buildRequestBodies } from \"./body\";\nimport { BatchworkError } from \"./errors\";\nimport { BatchJob } from \"./job\";\nimport { resolveModel } from \"./model\";\nimport { getAdapter } from \"./providers\";\nimport type {\n BatchOptions,\n BatchProvider,\n BatchRef,\n BatchResult,\n ProviderCredentials,\n} from \"./types\";\n\nconst pickCredentials = (source: ProviderCredentials): ProviderCredentials => ({\n apiKey: source.apiKey,\n baseURL: source.baseURL,\n headers: source.headers,\n});\n\nconst providerFromRef = (ref: BatchRef): BatchProvider => {\n if (ref.provider) {\n return ref.provider;\n }\n if (ref.model !== undefined) {\n return resolveModel(ref.model).provider;\n }\n throw new BatchworkError(\n \"batchwork: provide `provider` or `model` to identify the batch.\"\n );\n};\n\n/**\n * Submit a batch of requests to the model's provider and return a handle.\n *\n * Resolves immediately once the batch is accepted — it does not wait for\n * processing. Use the returned {@link BatchJob} to poll, wait, or stream\n * results.\n *\n * @example\n * const job = await batch({\n * model: openai(\"gpt-5.5\"),\n * requests: [{ customId: \"a\", prompt: \"Say hi\" }],\n * });\n * const results = await job.wait().then(() => job.collect());\n */\nexport const batch = async (options: BatchOptions): Promise<BatchJob> => {\n if (options.requests.length === 0) {\n throw new BatchworkError(\"batchwork: `requests` must not be empty.\");\n }\n\n const resolved = resolveModel(options.model);\n const credentials = pickCredentials(options);\n const adapter = getAdapter(resolved.provider);\n\n const built = await buildRequestBodies(\n resolved,\n options.requests,\n options.defaults,\n credentials\n );\n const snapshot = await adapter.submit({\n built,\n credentials,\n endpoint: built[0]?.endpoint ?? \"\",\n metadata: options.metadata,\n modelId: resolved.modelId,\n });\n\n return new BatchJob(adapter, credentials, snapshot);\n};\n\n/**\n * Rehydrate a {@link BatchJob} for an existing batch id (e.g. one persisted\n * after submission). Identify the provider with `provider` or `model`.\n */\nexport const getBatch = async (ref: BatchRef): Promise<BatchJob> => {\n const adapter = getAdapter(providerFromRef(ref));\n const credentials = pickCredentials(ref);\n const snapshot = await adapter.retrieve(ref.id, credentials);\n return new BatchJob(adapter, credentials, snapshot);\n};\n\n/** Stream the results of an existing batch by id, without a handle. */\nexport const getBatchResults = (ref: BatchRef): AsyncGenerator<BatchResult> => {\n const adapter = getAdapter(providerFromRef(ref));\n return adapter.results(ref.id, pickCredentials(ref));\n};\n\n/** Request cancellation of an existing batch by id. */\nexport const cancelBatch = async (ref: BatchRef): Promise<void> => {\n const adapter = getAdapter(providerFromRef(ref));\n await adapter.cancel(ref.id, pickCredentials(ref));\n};\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "A3005BE7078C61CE64756E2164756E21", | ||
| "names": [] | ||
| } |
| // src/errors.ts | ||
| class BatchworkError extends Error { | ||
| constructor(message, options) { | ||
| super(message, options); | ||
| this.name = "BatchworkError"; | ||
| } | ||
| } | ||
| class UnsupportedProviderError extends BatchworkError { | ||
| provider; | ||
| constructor(provider) { | ||
| super(`batchwork: provider "${provider}" is not supported yet. Supported providers: openai, anthropic, google, groq, mistral, together, xai.`); | ||
| this.name = "UnsupportedProviderError"; | ||
| this.provider = provider; | ||
| } | ||
| } | ||
| class MissingDependencyError extends BatchworkError { | ||
| constructor(pkg, provider) { | ||
| super(`batchwork: install \`${pkg}\` to batch ${provider} models (\`npm install ${pkg}\`).`); | ||
| this.name = "MissingDependencyError"; | ||
| } | ||
| } | ||
| // src/job.ts | ||
| var DEFAULT_POLL_INTERVAL_MS = 15000; | ||
| var TERMINAL_STATUSES = new Set([ | ||
| "completed", | ||
| "failed", | ||
| "expired", | ||
| "cancelled" | ||
| ]); | ||
| var isTerminalStatus = (status) => TERMINAL_STATUSES.has(status); | ||
| var delay = (ms, signal) => new Promise((resolve, reject) => { | ||
| if (signal?.aborted) { | ||
| reject(new BatchworkError("batchwork: wait aborted.")); | ||
| return; | ||
| } | ||
| const timer = setTimeout(resolve, ms); | ||
| signal?.addEventListener("abort", () => { | ||
| clearTimeout(timer); | ||
| reject(new BatchworkError("batchwork: wait aborted.")); | ||
| }, { once: true }); | ||
| }); | ||
| class BatchJob { | ||
| provider; | ||
| id; | ||
| #adapter; | ||
| #credentials; | ||
| #snapshot; | ||
| constructor(adapter, credentials, snapshot) { | ||
| this.#adapter = adapter; | ||
| this.#credentials = credentials; | ||
| this.#snapshot = snapshot; | ||
| this.id = snapshot.id; | ||
| this.provider = snapshot.provider; | ||
| } | ||
| get status() { | ||
| return this.#snapshot.status; | ||
| } | ||
| get requestCounts() { | ||
| return this.#snapshot.requestCounts; | ||
| } | ||
| get snapshot() { | ||
| return this.#snapshot; | ||
| } | ||
| async poll() { | ||
| this.#snapshot = await this.#adapter.retrieve(this.id, this.#credentials); | ||
| return this.#snapshot; | ||
| } | ||
| async wait(options = {}) { | ||
| const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS; | ||
| const deadline = options.timeoutMs ? Date.now() + options.timeoutMs : undefined; | ||
| let snapshot = await this.poll(); | ||
| options.onPoll?.(snapshot); | ||
| while (!isTerminalStatus(snapshot.status)) { | ||
| if (options.signal?.aborted) { | ||
| throw new BatchworkError("batchwork: wait aborted."); | ||
| } | ||
| if (deadline !== undefined && Date.now() > deadline) { | ||
| throw new BatchworkError(`batchwork: timed out waiting for batch "${this.id}".`); | ||
| } | ||
| await delay(interval, options.signal); | ||
| snapshot = await this.poll(); | ||
| options.onPoll?.(snapshot); | ||
| } | ||
| return snapshot; | ||
| } | ||
| results() { | ||
| return this.#adapter.results(this.id, this.#credentials); | ||
| } | ||
| async collect() { | ||
| const out = []; | ||
| for await (const result of this.results()) { | ||
| out.push(result); | ||
| } | ||
| return out; | ||
| } | ||
| async cancel() { | ||
| await this.#adapter.cancel(this.id, this.#credentials); | ||
| return await this.poll(); | ||
| } | ||
| } | ||
| // src/http.ts | ||
| var safeText = async (response) => { | ||
| try { | ||
| return await response.text(); | ||
| } catch { | ||
| return "<no body>"; | ||
| } | ||
| }; | ||
| var assertOk = async (url, init, response) => { | ||
| if (!response.ok) { | ||
| const detail = await safeText(response); | ||
| throw new BatchworkError(`batchwork: ${init.method ?? "GET"} ${url} failed with ${response.status}: ${detail}`); | ||
| } | ||
| }; | ||
| var requestJson = async (url, init) => { | ||
| const response = await fetch(url, init); | ||
| await assertOk(url, init, response); | ||
| return await response.json(); | ||
| }; | ||
| var requestStream = async (url, init) => { | ||
| const response = await fetch(url, init); | ||
| await assertOk(url, init, response); | ||
| if (!response.body) { | ||
| throw new BatchworkError(`batchwork: ${url} returned an empty body.`); | ||
| } | ||
| return response.body; | ||
| }; | ||
| // src/jsonl.ts | ||
| var NEWLINE = ` | ||
| `; | ||
| var encodeJsonl = (items) => { | ||
| if (items.length === 0) { | ||
| return ""; | ||
| } | ||
| const body = items.map((item) => JSON.stringify(item)).join(NEWLINE); | ||
| return `${body}${NEWLINE}`; | ||
| }; | ||
| var isReadableStream = (source) => ("getReader" in source) && typeof source.getReader === "function"; | ||
| async function* toByteIterable(source) { | ||
| if (isReadableStream(source)) { | ||
| const reader = source.getReader(); | ||
| try { | ||
| let chunk = await reader.read(); | ||
| while (!chunk.done) { | ||
| if (chunk.value) { | ||
| yield chunk.value; | ||
| } | ||
| chunk = await reader.read(); | ||
| } | ||
| } finally { | ||
| reader.releaseLock(); | ||
| } | ||
| return; | ||
| } | ||
| yield* source; | ||
| } | ||
| async function* streamJsonl(source) { | ||
| const decoder = new TextDecoder; | ||
| let buffer = ""; | ||
| for await (const chunk of toByteIterable(source)) { | ||
| buffer += decoder.decode(chunk, { stream: true }); | ||
| let newlineIndex = buffer.indexOf(NEWLINE); | ||
| while (newlineIndex !== -1) { | ||
| const line = buffer.slice(0, newlineIndex).trim(); | ||
| buffer = buffer.slice(newlineIndex + 1); | ||
| if (line.length > 0) { | ||
| yield JSON.parse(line); | ||
| } | ||
| newlineIndex = buffer.indexOf(NEWLINE); | ||
| } | ||
| } | ||
| buffer += decoder.decode(); | ||
| const tail = buffer.trim(); | ||
| if (tail.length > 0) { | ||
| yield JSON.parse(tail); | ||
| } | ||
| } | ||
| // src/util.ts | ||
| var asRecord = (value) => { | ||
| if (typeof value === "object" && value !== null) { | ||
| return value; | ||
| } | ||
| return {}; | ||
| }; | ||
| var asString = (value) => typeof value === "string" ? value : undefined; | ||
| var asNumber = (value) => typeof value === "number" ? value : undefined; | ||
| var asArray = (value) => Array.isArray(value) ? value : []; | ||
| var omit = (obj, key) => { | ||
| const result = {}; | ||
| for (const [k, v] of Object.entries(obj)) { | ||
| if (k !== key) { | ||
| result[k] = v; | ||
| } | ||
| } | ||
| return result; | ||
| }; | ||
| var toDate = (value) => { | ||
| if (typeof value === "string") { | ||
| return new Date(value); | ||
| } | ||
| if (typeof value === "number") { | ||
| return new Date(value * 1000); | ||
| } | ||
| }; | ||
| // src/providers/anthropic.ts | ||
| var ANTHROPIC_BASE = "https://api.anthropic.com"; | ||
| var ANTHROPIC_VERSION = "2023-06-01"; | ||
| var apiKey = (credentials) => { | ||
| const key = credentials.apiKey ?? process.env.ANTHROPIC_API_KEY; | ||
| if (!key) { | ||
| throw new BatchworkError("batchwork: missing Anthropic API key. Set ANTHROPIC_API_KEY or pass `apiKey`."); | ||
| } | ||
| return key; | ||
| }; | ||
| var baseUrl = (credentials) => credentials.baseURL ?? ANTHROPIC_BASE; | ||
| var headers = (credentials) => ({ | ||
| "anthropic-version": ANTHROPIC_VERSION, | ||
| "content-type": "application/json", | ||
| "x-api-key": apiKey(credentials), | ||
| ...credentials.headers | ||
| }); | ||
| var mapStatus = (status) => { | ||
| if (status === "ended") { | ||
| return "completed"; | ||
| } | ||
| if (status === "canceling") { | ||
| return "cancelling"; | ||
| } | ||
| return "in_progress"; | ||
| }; | ||
| var normalizeSnapshot = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const counts = asRecord(obj.request_counts); | ||
| const succeeded = asNumber(counts.succeeded) ?? 0; | ||
| const errored = asNumber(counts.errored) ?? 0; | ||
| const processing = asNumber(counts.processing) ?? 0; | ||
| const canceled = asNumber(counts.canceled) ?? 0; | ||
| const expired = asNumber(counts.expired) ?? 0; | ||
| return { | ||
| completedAt: toDate(obj.ended_at), | ||
| createdAt: toDate(obj.created_at), | ||
| expiresAt: toDate(obj.expires_at), | ||
| id: asString(obj.id) ?? "", | ||
| provider: "anthropic", | ||
| raw, | ||
| requestCounts: { | ||
| canceled, | ||
| completed: succeeded, | ||
| expired, | ||
| failed: errored, | ||
| processing, | ||
| total: succeeded + errored + processing + canceled + expired | ||
| }, | ||
| status: mapStatus(asString(obj.processing_status)) | ||
| }; | ||
| }; | ||
| var textFromMessage = (message) => { | ||
| const text = asArray(asRecord(message).content).map((block) => asRecord(block)).filter((block) => block.type === "text").map((block) => asString(block.text) ?? "").join(""); | ||
| return text.length > 0 ? text : undefined; | ||
| }; | ||
| var usageFromMessage = (message) => { | ||
| const usage = asRecord(asRecord(message).usage); | ||
| const inputTokens = asNumber(usage.input_tokens); | ||
| const outputTokens = asNumber(usage.output_tokens); | ||
| if (inputTokens === undefined && outputTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var normalizeResult = (line) => { | ||
| const obj = asRecord(line); | ||
| const customId = asString(obj.custom_id) ?? ""; | ||
| const result = asRecord(obj.result); | ||
| const type = asString(result.type); | ||
| if (type === "succeeded") { | ||
| return { | ||
| customId, | ||
| response: result.message, | ||
| status: "succeeded", | ||
| text: textFromMessage(result.message), | ||
| usage: usageFromMessage(result.message) | ||
| }; | ||
| } | ||
| if (type === "errored") { | ||
| const error = asRecord(result.error); | ||
| const nested = asRecord(error.error); | ||
| const source = Object.keys(nested).length > 0 ? nested : error; | ||
| return { | ||
| customId, | ||
| error: { | ||
| message: asString(source.message) ?? "Request errored.", | ||
| type: asString(source.type) | ||
| }, | ||
| response: result.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| if (type === "expired") { | ||
| return { customId, status: "expired" }; | ||
| } | ||
| return { customId, status: "canceled" }; | ||
| }; | ||
| var submit = async (input) => { | ||
| const requests = input.built.map((item) => ({ | ||
| custom_id: item.customId, | ||
| params: omit(item.body, "stream") | ||
| })); | ||
| const raw = await requestJson(`${baseUrl(input.credentials)}/v1/messages/batches`, { | ||
| body: JSON.stringify({ requests }), | ||
| headers: headers(input.credentials), | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot(raw); | ||
| }; | ||
| var retrieve = async (id, credentials) => { | ||
| const raw = await requestJson(`${baseUrl(credentials)}/v1/messages/batches/${id}`, { headers: headers(credentials) }); | ||
| return normalizeSnapshot(raw); | ||
| }; | ||
| async function* results(id, credentials) { | ||
| const snapshot = await retrieve(id, credentials); | ||
| const resultsUrl = asString(asRecord(snapshot.raw).results_url); | ||
| if (!resultsUrl) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| const stream = await requestStream(resultsUrl, { | ||
| headers: headers(credentials) | ||
| }); | ||
| for await (const line of streamJsonl(stream)) { | ||
| yield normalizeResult(line); | ||
| } | ||
| } | ||
| var cancel = async (id, credentials) => { | ||
| await requestJson(`${baseUrl(credentials)}/v1/messages/batches/${id}/cancel`, { | ||
| headers: headers(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var anthropicAdapter = { | ||
| cancel, | ||
| id: "anthropic", | ||
| results, | ||
| retrieve, | ||
| submit | ||
| }; | ||
| // src/providers/google.ts | ||
| var GOOGLE_BASE = "https://generativelanguage.googleapis.com/v1beta"; | ||
| var apiKey2 = (credentials) => { | ||
| const key = credentials.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? process.env.GEMINI_API_KEY; | ||
| if (!key) { | ||
| throw new BatchworkError("batchwork: missing Google Gemini API key. Set GOOGLE_GENERATIVE_AI_API_KEY (or GEMINI_API_KEY) or pass `apiKey`."); | ||
| } | ||
| return key; | ||
| }; | ||
| var baseUrl2 = (credentials) => credentials.baseURL ?? GOOGLE_BASE; | ||
| var headers2 = (credentials) => ({ | ||
| "content-type": "application/json", | ||
| "x-goog-api-key": apiKey2(credentials), | ||
| ...credentials.headers | ||
| }); | ||
| var mapState = (state, done) => { | ||
| if (state) { | ||
| if (state.endsWith("SUCCEEDED")) { | ||
| return "completed"; | ||
| } | ||
| if (state.endsWith("FAILED")) { | ||
| return "failed"; | ||
| } | ||
| if (state.endsWith("CANCELLED")) { | ||
| return "cancelled"; | ||
| } | ||
| if (state.endsWith("EXPIRED")) { | ||
| return "expired"; | ||
| } | ||
| if (state.endsWith("PENDING")) { | ||
| return "validating"; | ||
| } | ||
| if (state.endsWith("RUNNING")) { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| return done ? "completed" : "in_progress"; | ||
| }; | ||
| var inlinedResponses = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const response = asRecord(obj.response); | ||
| const dest = asRecord(obj.dest); | ||
| const responseInline = response.inlinedResponses ?? response.inlined_responses; | ||
| const destInline = dest.inlinedResponses ?? dest.inlined_responses; | ||
| const nestedResponseInline = asRecord(responseInline); | ||
| const nestedDestInline = asRecord(destInline); | ||
| return [ | ||
| ...asArray(responseInline), | ||
| ...asArray(nestedResponseInline.inlinedResponses), | ||
| ...asArray(nestedResponseInline.inlined_responses), | ||
| ...asArray(destInline), | ||
| ...asArray(nestedDestInline.inlinedResponses), | ||
| ...asArray(nestedDestInline.inlined_responses) | ||
| ]; | ||
| }; | ||
| var normalizeSnapshot2 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const items = inlinedResponses(raw); | ||
| const failed = items.filter((item) => asRecord(item).error).length; | ||
| return { | ||
| id: asString(obj.name) ?? "", | ||
| provider: "google", | ||
| raw, | ||
| requestCounts: { | ||
| completed: items.length - failed, | ||
| failed, | ||
| total: items.length | ||
| }, | ||
| status: mapState(asString(obj.state) ?? asString(asRecord(obj.state).name) ?? asString(asRecord(obj.metadata).state), obj.done === true) | ||
| }; | ||
| }; | ||
| var textFromResponse = (response) => { | ||
| const candidate = asRecord(asArray(asRecord(response).candidates)[0]); | ||
| const text = asArray(asRecord(candidate.content).parts).map((part) => asString(asRecord(part).text) ?? "").join(""); | ||
| return text.length > 0 ? text : undefined; | ||
| }; | ||
| var usageFromResponse = (response) => { | ||
| const usage = asRecord(asRecord(response).usageMetadata); | ||
| const inputTokens = asNumber(usage.promptTokenCount); | ||
| const outputTokens = asNumber(usage.candidatesTokenCount); | ||
| const totalTokens = asNumber(usage.totalTokenCount); | ||
| if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var normalizeResult2 = (item) => { | ||
| const obj = asRecord(item); | ||
| const customId = asString(asRecord(obj.metadata).key) ?? asString(obj.key) ?? asString(obj.custom_id) ?? ""; | ||
| if (obj.error) { | ||
| const error = asRecord(obj.error); | ||
| return { | ||
| customId, | ||
| error: { | ||
| code: asNumber(error.code) ?? asString(error.code), | ||
| message: asString(error.message) ?? "Request errored.", | ||
| type: asString(error.status) | ||
| }, | ||
| response: obj.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| return { | ||
| customId, | ||
| response: obj.response, | ||
| status: "succeeded", | ||
| text: textFromResponse(obj.response), | ||
| usage: usageFromResponse(obj.response) | ||
| }; | ||
| }; | ||
| var submit2 = async (input) => { | ||
| const requests = input.built.map((item) => ({ | ||
| metadata: { key: item.customId }, | ||
| request: omit(item.body, "stream") | ||
| })); | ||
| const raw = await requestJson(`${baseUrl2(input.credentials)}/models/${input.modelId}:batchGenerateContent`, { | ||
| body: JSON.stringify({ | ||
| batch: { | ||
| display_name: "batchwork", | ||
| input_config: { requests: { requests } } | ||
| } | ||
| }), | ||
| headers: headers2(input.credentials), | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot2(raw); | ||
| }; | ||
| var retrieve2 = async (id, credentials) => { | ||
| const raw = await requestJson(`${baseUrl2(credentials)}/${id}`, { | ||
| headers: headers2(credentials) | ||
| }); | ||
| return normalizeSnapshot2(raw); | ||
| }; | ||
| async function* results2(id, credentials) { | ||
| const snapshot = await retrieve2(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const response = asRecord(raw.response); | ||
| const dest = asRecord(raw.dest); | ||
| const responsesFile = asString(asRecord(response.responsesFile).name) ?? asString(response.responsesFile) ?? asString(asRecord(response.responses_file).name) ?? asString(response.responses_file) ?? asString(dest.fileName) ?? asString(dest.file_name); | ||
| if (responsesFile) { | ||
| throw new BatchworkError(`batchwork: batch "${id}" returned file-mode results, which are not supported yet.`); | ||
| } | ||
| const items = inlinedResponses(raw); | ||
| if (items.length === 0) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| for (const item of items) { | ||
| yield normalizeResult2(item); | ||
| } | ||
| } | ||
| var cancel2 = async (id, credentials) => { | ||
| await requestJson(`${baseUrl2(credentials)}/${id}:cancel`, { | ||
| headers: headers2(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var googleAdapter = { | ||
| cancel: cancel2, | ||
| id: "google", | ||
| results: results2, | ||
| retrieve: retrieve2, | ||
| submit: submit2 | ||
| }; | ||
| // src/providers/shared.ts | ||
| var HTTP_OK_MIN = 200; | ||
| var HTTP_OK_MAX = 300; | ||
| var resolveApiKey = (credentials, envVar, label) => { | ||
| const key = credentials.apiKey ?? process.env[envVar]; | ||
| if (!key) { | ||
| throw new BatchworkError(`batchwork: missing ${label} API key. Set ${envVar} or pass \`apiKey\`.`); | ||
| } | ||
| return key; | ||
| }; | ||
| var textFromBody = (body) => { | ||
| const obj = asRecord(body); | ||
| const choices = asArray(obj.choices); | ||
| if (choices.length > 0) { | ||
| const content = asString(asRecord(asRecord(choices[0]).message).content); | ||
| if (content) { | ||
| return content; | ||
| } | ||
| } | ||
| return asString(obj.output_text); | ||
| }; | ||
| var usageFromBody = (body) => { | ||
| const usage = asRecord(asRecord(body).usage); | ||
| const inputTokens = asNumber(usage.prompt_tokens) ?? asNumber(usage.input_tokens); | ||
| const outputTokens = asNumber(usage.completion_tokens) ?? asNumber(usage.output_tokens); | ||
| const totalTokens = asNumber(usage.total_tokens); | ||
| if (inputTokens === undefined && outputTokens === undefined && totalTokens === undefined) { | ||
| return; | ||
| } | ||
| return { | ||
| inputTokens, | ||
| outputTokens, | ||
| totalTokens: totalTokens ?? (inputTokens ?? 0) + (outputTokens ?? 0) | ||
| }; | ||
| }; | ||
| var errorFromValue = (value, fallback) => { | ||
| const obj = asRecord(value); | ||
| const nested = asRecord(obj.error); | ||
| const source = nested.message ? nested : obj; | ||
| return { | ||
| code: asNumber(source.code) ?? asString(source.code), | ||
| message: asString(source.message) ?? fallback, | ||
| type: asString(source.type) | ||
| }; | ||
| }; | ||
| var normalizeOpenAIResult = (line) => { | ||
| const obj = asRecord(line); | ||
| const customId = asString(obj.custom_id) ?? ""; | ||
| if (obj.error) { | ||
| return { | ||
| customId, | ||
| error: errorFromValue(obj.error, "Request errored."), | ||
| response: obj.error, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| const response = asRecord(obj.response); | ||
| const statusCode = asNumber(response.status_code) ?? 0; | ||
| if (statusCode >= HTTP_OK_MIN && statusCode < HTTP_OK_MAX) { | ||
| return { | ||
| customId, | ||
| response: response.body, | ||
| status: "succeeded", | ||
| text: textFromBody(response.body), | ||
| usage: usageFromBody(response.body) | ||
| }; | ||
| } | ||
| return { | ||
| customId, | ||
| error: errorFromValue(response.body, `Request failed with status ${statusCode}.`), | ||
| response: response.body, | ||
| status: "errored" | ||
| }; | ||
| }; | ||
| var uploadInputFile = async (jsonl, baseUrl3, headers3, options = {}) => { | ||
| const form = new FormData; | ||
| const purpose = options.purpose === undefined ? "batch" : options.purpose; | ||
| if (purpose !== null) { | ||
| form.append("purpose", purpose); | ||
| } | ||
| form.append("file", new Blob([jsonl], { type: "application/jsonl" }), "batchwork.jsonl"); | ||
| const raw = await requestJson(`${baseUrl3}/files`, { | ||
| body: form, | ||
| headers: headers3, | ||
| method: "POST" | ||
| }); | ||
| return raw.id; | ||
| }; | ||
| async function* streamResultFile(fileId, baseUrl3, headers3) { | ||
| const stream = await requestStream(`${baseUrl3}/files/${fileId}/content`, { | ||
| headers: headers3 | ||
| }); | ||
| for await (const line of streamJsonl(stream)) { | ||
| yield normalizeOpenAIResult(line); | ||
| } | ||
| } | ||
| // src/providers/openai-compatible.ts | ||
| var DEFAULT_COMPLETION_WINDOW = "24h"; | ||
| var mapStatus2 = (status) => { | ||
| const normalized = status?.toLowerCase(); | ||
| switch (normalized) { | ||
| case "validating": | ||
| case "in_progress": | ||
| case "finalizing": | ||
| case "completed": | ||
| case "failed": | ||
| case "expired": | ||
| case "cancelling": | ||
| case "cancelled": { | ||
| return normalized; | ||
| } | ||
| default: { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| }; | ||
| var normalizeSnapshot3 = (raw, provider) => { | ||
| const outer = asRecord(raw); | ||
| const obj = asRecord(outer.job); | ||
| const source = Object.keys(obj).length > 0 ? obj : outer; | ||
| const counts = asRecord(source.request_counts); | ||
| return { | ||
| completedAt: toDate(source.completed_at), | ||
| createdAt: toDate(source.created_at), | ||
| expiresAt: toDate(source.expires_at), | ||
| id: asString(source.id) ?? "", | ||
| provider, | ||
| raw: source, | ||
| requestCounts: { | ||
| completed: asNumber(counts.completed) ?? 0, | ||
| failed: asNumber(counts.failed) ?? 0, | ||
| total: asNumber(counts.total) ?? 0 | ||
| }, | ||
| status: mapStatus2(asString(source.status)) | ||
| }; | ||
| }; | ||
| var createOpenAICompatibleAdapter = (config) => { | ||
| const completionWindow = config.completionWindow ?? DEFAULT_COMPLETION_WINDOW; | ||
| const lineFormat = config.lineFormat ?? "method-url"; | ||
| const baseUrl3 = (credentials) => credentials.baseURL ?? config.baseUrl; | ||
| const authHeaders = (credentials) => ({ | ||
| Authorization: `Bearer ${resolveApiKey(credentials, config.apiKeyEnv, config.apiKeyLabel)}`, | ||
| ...credentials.headers | ||
| }); | ||
| const submit3 = async (input) => { | ||
| const endpoint = config.normalizeEndpoint ? config.normalizeEndpoint(input.endpoint) : input.endpoint; | ||
| const jsonl = encodeJsonl(input.built.map((item) => { | ||
| const body = omit(item.body, "stream"); | ||
| if (lineFormat === "body-only") { | ||
| return { body, custom_id: item.customId }; | ||
| } | ||
| return { | ||
| body, | ||
| custom_id: item.customId, | ||
| method: "POST", | ||
| url: endpoint | ||
| }; | ||
| })); | ||
| const headers3 = authHeaders(input.credentials); | ||
| const url = baseUrl3(input.credentials); | ||
| const purpose = config.filePurpose ?? "batch"; | ||
| const inputFileId = await (config.uploadFile ? config.uploadFile({ baseUrl: url, headers: headers3, jsonl, purpose }) : uploadInputFile(jsonl, url, headers3, { purpose })); | ||
| const raw = await requestJson(`${url}/batches`, { | ||
| body: JSON.stringify({ | ||
| completion_window: completionWindow, | ||
| endpoint, | ||
| input_file_id: inputFileId, | ||
| metadata: input.metadata | ||
| }), | ||
| headers: { ...headers3, "content-type": "application/json" }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot3(raw, config.id); | ||
| }; | ||
| const retrieve3 = async (id, credentials) => { | ||
| const raw = await requestJson(`${baseUrl3(credentials)}/batches/${id}`, { | ||
| headers: authHeaders(credentials) | ||
| }); | ||
| return normalizeSnapshot3(raw, config.id); | ||
| }; | ||
| async function* results3(id, credentials) { | ||
| const snapshot = await retrieve3(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const outputFileId = asString(raw.output_file_id); | ||
| const errorFileId = asString(raw.error_file_id); | ||
| if (!(outputFileId || errorFileId)) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| const headers3 = authHeaders(credentials); | ||
| if (outputFileId) { | ||
| yield* streamResultFile(outputFileId, baseUrl3(credentials), headers3); | ||
| } | ||
| if (errorFileId) { | ||
| yield* streamResultFile(errorFileId, baseUrl3(credentials), headers3); | ||
| } | ||
| } | ||
| const cancel3 = async (id, credentials) => { | ||
| await requestJson(`${baseUrl3(credentials)}/batches/${id}/cancel`, { | ||
| headers: authHeaders(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| return { cancel: cancel3, id: config.id, results: results3, retrieve: retrieve3, submit: submit3 }; | ||
| }; | ||
| // src/providers/groq.ts | ||
| var groqAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "GROQ_API_KEY", | ||
| apiKeyLabel: "Groq", | ||
| baseUrl: "https://api.groq.com/openai/v1", | ||
| id: "groq", | ||
| lineFormat: "method-url", | ||
| normalizeEndpoint: (endpoint) => endpoint.replace(/^\/openai/u, "") | ||
| }); | ||
| // src/providers/mistral.ts | ||
| var MISTRAL_BASE = "https://api.mistral.ai/v1"; | ||
| var apiKey3 = (credentials) => resolveApiKey(credentials, "MISTRAL_API_KEY", "Mistral"); | ||
| var baseUrl3 = (credentials) => credentials.baseURL ?? MISTRAL_BASE; | ||
| var authHeaders = (credentials) => ({ | ||
| Authorization: `Bearer ${apiKey3(credentials)}`, | ||
| ...credentials.headers | ||
| }); | ||
| var mapStatus3 = (status) => { | ||
| switch (status) { | ||
| case "QUEUED": { | ||
| return "validating"; | ||
| } | ||
| case "SUCCESS": { | ||
| return "completed"; | ||
| } | ||
| case "FAILED": { | ||
| return "failed"; | ||
| } | ||
| case "TIMEOUT_EXCEEDED": { | ||
| return "expired"; | ||
| } | ||
| case "CANCELLATION_REQUESTED": { | ||
| return "cancelling"; | ||
| } | ||
| case "CANCELLED": { | ||
| return "cancelled"; | ||
| } | ||
| default: { | ||
| return "in_progress"; | ||
| } | ||
| } | ||
| }; | ||
| var normalizeSnapshot4 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const succeeded = asNumber(obj.succeeded_requests) ?? 0; | ||
| const failed = asNumber(obj.failed_requests) ?? 0; | ||
| return { | ||
| completedAt: toDate(obj.completed_at), | ||
| createdAt: toDate(obj.created_at), | ||
| id: asString(obj.id) ?? "", | ||
| provider: "mistral", | ||
| raw, | ||
| requestCounts: { | ||
| completed: succeeded, | ||
| failed, | ||
| total: asNumber(obj.total_requests) ?? succeeded + failed | ||
| }, | ||
| status: mapStatus3(asString(obj.status)) | ||
| }; | ||
| }; | ||
| var submit3 = async (input) => { | ||
| const jsonl = encodeJsonl(input.built.map((item) => ({ | ||
| body: omit(omit(item.body, "stream"), "model"), | ||
| custom_id: item.customId | ||
| }))); | ||
| const inputFileId = await uploadInputFile(jsonl, baseUrl3(input.credentials), authHeaders(input.credentials)); | ||
| const raw = await requestJson(`${baseUrl3(input.credentials)}/batch/jobs`, { | ||
| body: JSON.stringify({ | ||
| endpoint: input.endpoint, | ||
| input_files: [inputFileId], | ||
| metadata: input.metadata, | ||
| model: input.modelId | ||
| }), | ||
| headers: { | ||
| ...authHeaders(input.credentials), | ||
| "content-type": "application/json" | ||
| }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot4(raw); | ||
| }; | ||
| var retrieve3 = async (id, credentials) => { | ||
| const raw = await requestJson(`${baseUrl3(credentials)}/batch/jobs/${id}`, { | ||
| headers: authHeaders(credentials) | ||
| }); | ||
| return normalizeSnapshot4(raw); | ||
| }; | ||
| async function* results3(id, credentials) { | ||
| const snapshot = await retrieve3(id, credentials); | ||
| const raw = asRecord(snapshot.raw); | ||
| const outputFileId = asString(raw.output_file); | ||
| const errorFileId = asString(raw.error_file); | ||
| const headers3 = authHeaders(credentials); | ||
| if (outputFileId) { | ||
| yield* streamResultFile(outputFileId, baseUrl3(credentials), headers3); | ||
| } | ||
| if (errorFileId) { | ||
| yield* streamResultFile(errorFileId, baseUrl3(credentials), headers3); | ||
| } | ||
| if (!(outputFileId || errorFileId)) { | ||
| throw new BatchworkError(`batchwork: results are not ready for batch "${id}" (status: ${snapshot.status}).`); | ||
| } | ||
| } | ||
| var cancel3 = async (id, credentials) => { | ||
| await requestJson(`${baseUrl3(credentials)}/batch/jobs/${id}/cancel`, { | ||
| headers: authHeaders(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var mistralAdapter = { | ||
| cancel: cancel3, | ||
| id: "mistral", | ||
| results: results3, | ||
| retrieve: retrieve3, | ||
| submit: submit3 | ||
| }; | ||
| // src/providers/openai.ts | ||
| var openaiAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "OPENAI_API_KEY", | ||
| apiKeyLabel: "OpenAI", | ||
| baseUrl: "https://api.openai.com/v1", | ||
| id: "openai", | ||
| lineFormat: "method-url" | ||
| }); | ||
| // src/providers/together.ts | ||
| var INPUT_FILE_NAME = "batchwork.jsonl"; | ||
| var HTTP_FOUND = 302; | ||
| var safeText2 = async (response) => { | ||
| try { | ||
| return await response.text(); | ||
| } catch { | ||
| return "<no body>"; | ||
| } | ||
| }; | ||
| var uploadTogetherFile = async (args) => { | ||
| const metadata = new FormData; | ||
| metadata.append("purpose", args.purpose); | ||
| metadata.append("file_name", INPUT_FILE_NAME); | ||
| metadata.append("file_type", "jsonl"); | ||
| const init = await fetch(`${args.baseUrl}/files`, { | ||
| body: metadata, | ||
| headers: args.headers, | ||
| method: "POST", | ||
| redirect: "manual" | ||
| }); | ||
| const location = init.headers.get("location"); | ||
| const fileId = init.headers.get("x-together-file-id"); | ||
| if (init.status !== HTTP_FOUND || !(location && fileId)) { | ||
| throw new BatchworkError(`batchwork: Together upload could not be initiated (${init.status}): ${await safeText2(init)}`); | ||
| } | ||
| const upload = await fetch(location, { body: args.jsonl, method: "PUT" }); | ||
| if (!upload.ok) { | ||
| throw new BatchworkError(`batchwork: Together file upload failed (${upload.status}): ${await safeText2(upload)}`); | ||
| } | ||
| await requestJson(`${args.baseUrl}/files/${fileId}/preprocess`, { | ||
| headers: args.headers, | ||
| method: "POST" | ||
| }); | ||
| return fileId; | ||
| }; | ||
| var togetherAdapter = createOpenAICompatibleAdapter({ | ||
| apiKeyEnv: "TOGETHER_API_KEY", | ||
| apiKeyLabel: "Together AI", | ||
| baseUrl: "https://api.together.xyz/v1", | ||
| filePurpose: "batch-api", | ||
| id: "together", | ||
| lineFormat: "body-only", | ||
| uploadFile: uploadTogetherFile | ||
| }); | ||
| // src/providers/xai.ts | ||
| var XAI_BASE = "https://api.x.ai/v1"; | ||
| var RESULTS_PAGE_SIZE = 100; | ||
| var apiKey4 = (credentials) => resolveApiKey(credentials, "XAI_API_KEY", "xAI"); | ||
| var baseUrl4 = (credentials) => credentials.baseURL ?? XAI_BASE; | ||
| var authHeaders2 = (credentials) => ({ | ||
| Authorization: `Bearer ${apiKey4(credentials)}`, | ||
| ...credentials.headers | ||
| }); | ||
| var deriveStatus = (state) => { | ||
| const pending = asNumber(state.num_pending); | ||
| if (pending === undefined) { | ||
| return "in_progress"; | ||
| } | ||
| const total = asNumber(state.num_requests) ?? 0; | ||
| const cancelled = asNumber(state.num_cancelled) ?? 0; | ||
| if (total === 0) { | ||
| return "in_progress"; | ||
| } | ||
| if (pending > 0) { | ||
| return "in_progress"; | ||
| } | ||
| if (cancelled > 0 && cancelled === total) { | ||
| return "cancelled"; | ||
| } | ||
| return "completed"; | ||
| }; | ||
| var normalizeSnapshot5 = (raw) => { | ||
| const obj = asRecord(raw); | ||
| const state = asRecord(obj.state); | ||
| return { | ||
| completedAt: toDate(obj.cancel_time), | ||
| createdAt: toDate(obj.create_time), | ||
| expiresAt: toDate(obj.expire_time ?? obj.expires_at), | ||
| id: asString(obj.batch_id) ?? asString(obj.id) ?? "", | ||
| provider: "xai", | ||
| raw, | ||
| requestCounts: { | ||
| canceled: asNumber(state.num_cancelled) ?? 0, | ||
| completed: asNumber(state.num_success) ?? 0, | ||
| failed: asNumber(state.num_error) ?? 0, | ||
| processing: asNumber(state.num_pending) ?? 0, | ||
| total: asNumber(state.num_requests) ?? 0 | ||
| }, | ||
| status: deriveStatus(state) | ||
| }; | ||
| }; | ||
| var normalizeResult3 = (item) => { | ||
| const obj = asRecord(item); | ||
| const customId = asString(obj.batch_request_id) ?? ""; | ||
| const batchResult = asRecord(obj.batch_result); | ||
| const resultError = batchResult.error; | ||
| const errorMessage = asString(obj.error_message) ?? asString(resultError) ?? asString(asRecord(resultError).message); | ||
| if (errorMessage) { | ||
| return { | ||
| customId, | ||
| error: { | ||
| message: errorMessage, | ||
| type: asString(asRecord(resultError).type) | ||
| }, | ||
| response: obj, | ||
| status: "errored" | ||
| }; | ||
| } | ||
| const response = asRecord(batchResult.response); | ||
| const completion = response.chat_get_completion ?? Object.values(response)[0]; | ||
| return { | ||
| customId, | ||
| response: completion, | ||
| status: "succeeded", | ||
| text: textFromBody(completion), | ||
| usage: usageFromBody(completion) | ||
| }; | ||
| }; | ||
| var submit4 = async (input) => { | ||
| const jsonl = encodeJsonl(input.built.map((item) => ({ | ||
| body: omit(item.body, "stream"), | ||
| custom_id: item.customId, | ||
| method: "POST", | ||
| url: input.endpoint | ||
| }))); | ||
| const inputFileId = await uploadInputFile(jsonl, baseUrl4(input.credentials), authHeaders2(input.credentials), { purpose: null }); | ||
| const raw = await requestJson(`${baseUrl4(input.credentials)}/batches`, { | ||
| body: JSON.stringify({ input_file_id: inputFileId, name: "batchwork" }), | ||
| headers: { | ||
| ...authHeaders2(input.credentials), | ||
| "content-type": "application/json" | ||
| }, | ||
| method: "POST" | ||
| }); | ||
| return normalizeSnapshot5(raw); | ||
| }; | ||
| var retrieve4 = async (id, credentials) => { | ||
| const raw = await requestJson(`${baseUrl4(credentials)}/batches/${id}`, { | ||
| headers: authHeaders2(credentials) | ||
| }); | ||
| return normalizeSnapshot5(raw); | ||
| }; | ||
| async function* results4(id, credentials) { | ||
| const headers3 = authHeaders2(credentials); | ||
| let token; | ||
| do { | ||
| const query = new URLSearchParams({ limit: String(RESULTS_PAGE_SIZE) }); | ||
| if (token) { | ||
| query.set("pagination_token", token); | ||
| } | ||
| const raw = await requestJson(`${baseUrl4(credentials)}/batches/${id}/results?${query.toString()}`, { headers: headers3 }); | ||
| const page = asRecord(raw); | ||
| for (const item of Array.isArray(page.results) ? page.results : []) { | ||
| yield normalizeResult3(item); | ||
| } | ||
| token = asString(page.pagination_token); | ||
| } while (token); | ||
| } | ||
| var cancel4 = async (id, credentials) => { | ||
| await requestJson(`${baseUrl4(credentials)}/batches/${id}:cancel`, { | ||
| headers: authHeaders2(credentials), | ||
| method: "POST" | ||
| }); | ||
| }; | ||
| var xaiAdapter = { | ||
| cancel: cancel4, | ||
| id: "xai", | ||
| results: results4, | ||
| retrieve: retrieve4, | ||
| submit: submit4 | ||
| }; | ||
| // src/providers/index.ts | ||
| var adapters = { | ||
| anthropic: anthropicAdapter, | ||
| google: googleAdapter, | ||
| groq: groqAdapter, | ||
| mistral: mistralAdapter, | ||
| openai: openaiAdapter, | ||
| together: togetherAdapter, | ||
| xai: xaiAdapter | ||
| }; | ||
| var getAdapter = (provider) => adapters[provider]; | ||
| export { BatchworkError, UnsupportedProviderError, MissingDependencyError, isTerminalStatus, BatchJob, getAdapter }; | ||
| //# debugId=DA60AE45A8F12B3C64756E2164756E21 | ||
| //# sourceMappingURL=chunk-kv3847wy.js.map |
Sorry, the diff of this file is too big to display
| import { | ||
| BatchworkError, | ||
| getAdapter, | ||
| isTerminalStatus | ||
| } from "./chunk-kv3847wy.js"; | ||
| // src/server/events.ts | ||
| var EVENT_BY_STATUS = { | ||
| cancelled: "batch.cancelled", | ||
| completed: "batch.completed", | ||
| expired: "batch.expired", | ||
| failed: "batch.failed" | ||
| }; | ||
| var toEvent = (provider, snapshot) => ({ | ||
| completedAt: snapshot.completedAt?.toISOString(), | ||
| createdAt: snapshot.createdAt?.toISOString(), | ||
| id: snapshot.id, | ||
| provider, | ||
| requestCounts: snapshot.requestCounts, | ||
| type: EVENT_BY_STATUS[snapshot.status] ?? "batch.completed" | ||
| }); | ||
| // src/server/signing.ts | ||
| var SIGNATURE_VERSION = "v1"; | ||
| var TOLERANCE_SECONDS = 300; | ||
| var SECRET_PREFIX = "whsec_"; | ||
| var encoder = new TextEncoder; | ||
| var base64ToBytes = (value) => Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0); | ||
| var bytesToBase64 = (bytes) => { | ||
| let binary = ""; | ||
| for (const byte of bytes) { | ||
| binary += String.fromCodePoint(byte); | ||
| } | ||
| return btoa(binary); | ||
| }; | ||
| var importKey = (secret) => { | ||
| const raw = secret.startsWith(SECRET_PREFIX) ? base64ToBytes(secret.slice(SECRET_PREFIX.length)) : encoder.encode(secret); | ||
| return crypto.subtle.importKey("raw", raw, { hash: "SHA-256", name: "HMAC" }, false, ["sign", "verify"]); | ||
| }; | ||
| var signContent = async (secret, content) => { | ||
| const key = await importKey(secret); | ||
| const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(content)); | ||
| return bytesToBase64(new Uint8Array(signature)); | ||
| }; | ||
| var verifyContent = async (secret, content, signature) => { | ||
| const key = await importKey(secret); | ||
| return crypto.subtle.verify("HMAC", key, base64ToBytes(signature), encoder.encode(content)); | ||
| }; | ||
| var signWebhook = async (secret, id, body, timestampSeconds) => { | ||
| const timestamp = Math.floor(timestampSeconds).toString(); | ||
| const signature = await signContent(secret, `${id}.${timestamp}.${body}`); | ||
| return { | ||
| "webhook-id": id, | ||
| "webhook-signature": `${SIGNATURE_VERSION},${signature}`, | ||
| "webhook-timestamp": timestamp | ||
| }; | ||
| }; | ||
| var verifyWebhook = async (request, secret) => { | ||
| const id = request.headers.get("webhook-id"); | ||
| const timestamp = request.headers.get("webhook-timestamp"); | ||
| const signatureHeader = request.headers.get("webhook-signature"); | ||
| if (!(id && timestamp && signatureHeader)) { | ||
| throw new BatchworkError("batchwork: missing webhook signature headers."); | ||
| } | ||
| const seconds = Number(timestamp); | ||
| if (!Number.isFinite(seconds) || Math.abs(Date.now() / 1000 - seconds) > TOLERANCE_SECONDS) { | ||
| throw new BatchworkError("batchwork: webhook timestamp outside tolerance."); | ||
| } | ||
| const body = await request.text(); | ||
| const content = `${id}.${timestamp}.${body}`; | ||
| const signatures = signatureHeader.split(" ").map((part) => { | ||
| const comma = part.indexOf(","); | ||
| return comma === -1 ? part : part.slice(comma + 1); | ||
| }); | ||
| let valid = false; | ||
| for (const signature of signatures) { | ||
| if (await verifyContent(secret, content, signature)) { | ||
| valid = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!valid) { | ||
| throw new BatchworkError("batchwork: webhook signature verification failed."); | ||
| } | ||
| return { body, id, timestamp: seconds }; | ||
| }; | ||
| var verifyBatchWebhook = async (request, secret) => { | ||
| const { body } = await verifyWebhook(request, secret); | ||
| return JSON.parse(body); | ||
| }; | ||
| // src/server/poller.ts | ||
| var sendWebhook = async (record, snapshot) => { | ||
| if (!record.webhookUrl) { | ||
| throw new BatchworkError("batchwork: tracked batch has no webhookUrl to deliver to."); | ||
| } | ||
| const body = JSON.stringify(toEvent(record.provider, snapshot)); | ||
| const headers = { | ||
| "content-type": "application/json" | ||
| }; | ||
| if (record.webhookSecret) { | ||
| Object.assign(headers, await signWebhook(record.webhookSecret, record.id, body, Date.now() / 1000)); | ||
| } | ||
| const response = await fetch(record.webhookUrl, { | ||
| body, | ||
| headers, | ||
| method: "POST" | ||
| }); | ||
| if (!response.ok) { | ||
| throw new BatchworkError(`batchwork: webhook delivery to ${record.webhookUrl} failed (${response.status}).`); | ||
| } | ||
| }; | ||
| var createBatchPoller = (options) => { | ||
| const resolveCredentials = (provider) => { | ||
| if (typeof options.credentials === "function") { | ||
| return options.credentials(provider); | ||
| } | ||
| return options.credentials ?? {}; | ||
| }; | ||
| const sink = options.onComplete ?? sendWebhook; | ||
| const deliver = async (record, snapshot) => { | ||
| await sink(record, snapshot); | ||
| await options.store.set({ | ||
| ...record, | ||
| deliveredAt: new Date().toISOString(), | ||
| status: snapshot.status | ||
| }); | ||
| }; | ||
| const track = async (target, opts) => { | ||
| const record = { | ||
| createdAt: new Date().toISOString(), | ||
| id: target.id, | ||
| provider: target.provider, | ||
| status: target.status ?? "in_progress", | ||
| webhookSecret: opts.secret, | ||
| webhookUrl: opts.webhookUrl | ||
| }; | ||
| await options.store.set(record); | ||
| return record; | ||
| }; | ||
| const process = async (record, delivered) => { | ||
| const adapter = getAdapter(record.provider); | ||
| const snapshot = await adapter.retrieve(record.id, resolveCredentials(record.provider)); | ||
| if (isTerminalStatus(snapshot.status)) { | ||
| await deliver(record, snapshot); | ||
| delivered.push(record.id); | ||
| } else if (snapshot.status !== record.status) { | ||
| await options.store.set({ ...record, status: snapshot.status }); | ||
| } | ||
| }; | ||
| const tick = async () => { | ||
| const pending = await options.store.list({ delivered: false }); | ||
| const delivered = []; | ||
| const failed = []; | ||
| for (const record of pending) { | ||
| try { | ||
| await process(record, delivered); | ||
| } catch (error) { | ||
| if (!options.onError) { | ||
| throw error; | ||
| } | ||
| options.onError(record, error); | ||
| failed.push({ | ||
| error: error instanceof Error ? error.message : String(error), | ||
| id: record.id | ||
| }); | ||
| } | ||
| } | ||
| return failed.length > 0 ? { checked: pending.length, delivered, failed } : { checked: pending.length, delivered }; | ||
| }; | ||
| const openaiWebhookHandler = (config) => async (request) => { | ||
| let verified; | ||
| try { | ||
| verified = await verifyWebhook(request, config.signingSecret); | ||
| } catch { | ||
| return new Response("invalid signature", { status: 400 }); | ||
| } | ||
| const payload = JSON.parse(verified.body); | ||
| if (!payload.type?.startsWith("batch.")) { | ||
| return new Response("ignored", { status: 202 }); | ||
| } | ||
| const id = payload.data?.id; | ||
| if (!id) { | ||
| return new Response("missing batch id", { status: 400 }); | ||
| } | ||
| const record = await options.store.get(id); | ||
| if (!record || record.deliveredAt) { | ||
| return new Response("ok", { status: 200 }); | ||
| } | ||
| const snapshot = await getAdapter("openai").retrieve(id, resolveCredentials("openai")); | ||
| if (isTerminalStatus(snapshot.status)) { | ||
| await deliver(record, snapshot); | ||
| } | ||
| return new Response("ok", { status: 200 }); | ||
| }; | ||
| return { deliver, openaiWebhookHandler, tick, track }; | ||
| }; | ||
| // src/server/store.ts | ||
| var createMemoryStore = () => { | ||
| const records = new Map; | ||
| return { | ||
| delete: (id) => { | ||
| records.delete(id); | ||
| return Promise.resolve(); | ||
| }, | ||
| get: (id) => Promise.resolve(records.get(id) ?? null), | ||
| list: (filter) => { | ||
| const all = [...records.values()]; | ||
| if (filter?.delivered === undefined) { | ||
| return Promise.resolve(all); | ||
| } | ||
| const { delivered } = filter; | ||
| return Promise.resolve(all.filter((record) => record.deliveredAt !== undefined === delivered)); | ||
| }, | ||
| set: (record) => { | ||
| records.set(record.id, record); | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| }; | ||
| export { toEvent, signWebhook, verifyWebhook, verifyBatchWebhook, createBatchPoller, createMemoryStore }; | ||
| //# debugId=344750DFEF66E25964756E2164756E21 | ||
| //# sourceMappingURL=chunk-zp2cxkyb.js.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/server/events.ts", "../src/server/signing.ts", "../src/server/poller.ts", "../src/server/store.ts"], | ||
| "sourcesContent": [ | ||
| "import type { BatchProvider, BatchSnapshot, BatchStatus } from \"../types\";\nimport type { BatchWebhookEvent, BatchWebhookEventType } from \"./types\";\n\nconst EVENT_BY_STATUS: Partial<Record<BatchStatus, BatchWebhookEventType>> = {\n cancelled: \"batch.cancelled\",\n completed: \"batch.completed\",\n expired: \"batch.expired\",\n failed: \"batch.failed\",\n};\n\n/** Map a terminal snapshot to the unified webhook event batchwork delivers. */\nexport const toEvent = (\n provider: BatchProvider,\n snapshot: BatchSnapshot\n): BatchWebhookEvent => ({\n completedAt: snapshot.completedAt?.toISOString(),\n createdAt: snapshot.createdAt?.toISOString(),\n id: snapshot.id,\n provider,\n requestCounts: snapshot.requestCounts,\n type: EVENT_BY_STATUS[snapshot.status] ?? \"batch.completed\",\n});\n", | ||
| "import { BatchworkError } from \"../errors\";\nimport type { BatchWebhookEvent } from \"./types\";\n\n// Standard Webhooks-style HMAC-SHA256 signing, compatible with OpenAI's webhook\n// signatures (so the same verifier handles inbound OpenAI events and batchwork's\n// own outbound deliveries). Uses Web Crypto so it runs on edge runtimes.\n\nconst SIGNATURE_VERSION = \"v1\";\nconst TOLERANCE_SECONDS = 300;\nconst SECRET_PREFIX = \"whsec_\";\nconst encoder = new TextEncoder();\n\nconst base64ToBytes = (value: string): Uint8Array<ArrayBuffer> =>\n Uint8Array.from(atob(value), (char) => char.codePointAt(0) ?? 0);\n\nconst bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary);\n};\n\nconst importKey = (secret: string): Promise<CryptoKey> => {\n const raw = secret.startsWith(SECRET_PREFIX)\n ? base64ToBytes(secret.slice(SECRET_PREFIX.length))\n : encoder.encode(secret);\n return crypto.subtle.importKey(\n \"raw\",\n raw,\n { hash: \"SHA-256\", name: \"HMAC\" },\n false,\n [\"sign\", \"verify\"]\n );\n};\n\nconst signContent = async (\n secret: string,\n content: string\n): Promise<string> => {\n const key = await importKey(secret);\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n encoder.encode(content)\n );\n return bytesToBase64(new Uint8Array(signature));\n};\n\nconst verifyContent = async (\n secret: string,\n content: string,\n signature: string\n): Promise<boolean> => {\n const key = await importKey(secret);\n return crypto.subtle.verify(\n \"HMAC\",\n key,\n base64ToBytes(signature),\n encoder.encode(content)\n );\n};\n\n/** Build Standard Webhooks signature headers for an outbound delivery. */\nexport const signWebhook = async (\n secret: string,\n id: string,\n body: string,\n timestampSeconds: number\n): Promise<Record<string, string>> => {\n const timestamp = Math.floor(timestampSeconds).toString();\n const signature = await signContent(secret, `${id}.${timestamp}.${body}`);\n return {\n \"webhook-id\": id,\n \"webhook-signature\": `${SIGNATURE_VERSION},${signature}`,\n \"webhook-timestamp\": timestamp,\n };\n};\n\nexport interface VerifiedWebhook {\n id: string;\n timestamp: number;\n body: string;\n}\n\n/**\n * Verify a Standard Webhooks-signed request and return its raw body. Throws if\n * headers are missing, the timestamp is outside tolerance, or no signature\n * matches. Consumes the request body.\n */\nexport const verifyWebhook = async (\n request: Request,\n secret: string\n): Promise<VerifiedWebhook> => {\n const id = request.headers.get(\"webhook-id\");\n const timestamp = request.headers.get(\"webhook-timestamp\");\n const signatureHeader = request.headers.get(\"webhook-signature\");\n if (!(id && timestamp && signatureHeader)) {\n throw new BatchworkError(\"batchwork: missing webhook signature headers.\");\n }\n\n const seconds = Number(timestamp);\n if (\n !Number.isFinite(seconds) ||\n Math.abs(Date.now() / 1000 - seconds) > TOLERANCE_SECONDS\n ) {\n throw new BatchworkError(\"batchwork: webhook timestamp outside tolerance.\");\n }\n\n const body = await request.text();\n const content = `${id}.${timestamp}.${body}`;\n const signatures = signatureHeader.split(\" \").map((part) => {\n const comma = part.indexOf(\",\");\n return comma === -1 ? part : part.slice(comma + 1);\n });\n\n let valid = false;\n for (const signature of signatures) {\n // oxlint-disable-next-line no-await-in-loop -- usually a single signature.\n if (await verifyContent(secret, content, signature)) {\n valid = true;\n break;\n }\n }\n if (!valid) {\n throw new BatchworkError(\n \"batchwork: webhook signature verification failed.\"\n );\n }\n\n return { body, id, timestamp: seconds };\n};\n\n/**\n * Verify and parse a batchwork webhook on your receiving endpoint. Returns the\n * unified {@link BatchWebhookEvent}.\n */\nexport const verifyBatchWebhook = async (\n request: Request,\n secret: string\n): Promise<BatchWebhookEvent> => {\n const { body } = await verifyWebhook(request, secret);\n return JSON.parse(body) as BatchWebhookEvent;\n};\n", | ||
| "import { BatchworkError } from \"../errors\";\nimport { isTerminalStatus } from \"../job\";\nimport { getAdapter } from \"../providers\";\nimport type {\n BatchProvider,\n BatchSnapshot,\n BatchStatus,\n ProviderCredentials,\n} from \"../types\";\nimport { toEvent } from \"./events\";\nimport { signWebhook, verifyWebhook } from \"./signing\";\nimport type { BatchStore, TrackedBatch } from \"./types\";\n\n/** Credentials for polling: a fixed config, or one resolved per provider. */\nexport type CredentialResolver =\n | ProviderCredentials\n | ((provider: BatchProvider) => ProviderCredentials);\n\n/**\n * Handles a batch reaching a terminal status. Replaces the default signed\n * webhook delivery — e.g. to invoke a callback instead (see `batchwork/next`).\n */\nexport type CompletionSink = (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n) => Promise<void>;\n\nexport interface BatchPollerOptions {\n store: BatchStore;\n /** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */\n credentials?: CredentialResolver;\n /** Replaces signed-webhook delivery when a batch finishes. */\n onComplete?: CompletionSink;\n /**\n * Called when processing a single batch throws during `tick`. When provided,\n * the tick reports the error and continues to the next batch; when omitted,\n * the error propagates out of `tick`.\n */\n onError?: (record: TrackedBatch, error: unknown) => void;\n}\n\nexport interface TrackTarget {\n id: string;\n provider: BatchProvider;\n status?: BatchStatus;\n}\n\nexport interface TrackOptions {\n /** Where to POST the completion webhook. Omit for callback-based delivery. */\n webhookUrl?: string;\n /** Signs the outbound webhook (Standard Webhooks HMAC) when provided. */\n secret?: string;\n}\n\nexport interface TickResult {\n checked: number;\n delivered: string[];\n /** Batches whose processing threw this tick (only when `onError` is set). */\n failed?: { id: string; error: string }[];\n}\n\nexport interface OpenAIWebhookOptions {\n /** The OpenAI webhook signing secret (`whsec_…`). */\n signingSecret: string;\n}\n\nexport interface BatchPoller {\n track: (target: TrackTarget, options: TrackOptions) => Promise<TrackedBatch>;\n tick: () => Promise<TickResult>;\n deliver: (record: TrackedBatch, snapshot: BatchSnapshot) => Promise<void>;\n openaiWebhookHandler: (\n options: OpenAIWebhookOptions\n ) => (request: Request) => Promise<Response>;\n}\n\n/** The default completion sink: POST a signed webhook to the tracked URL. */\nconst sendWebhook: CompletionSink = async (record, snapshot) => {\n if (!record.webhookUrl) {\n throw new BatchworkError(\n \"batchwork: tracked batch has no webhookUrl to deliver to.\"\n );\n }\n const body = JSON.stringify(toEvent(record.provider, snapshot));\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (record.webhookSecret) {\n Object.assign(\n headers,\n await signWebhook(\n record.webhookSecret,\n record.id,\n body,\n Date.now() / 1000\n )\n );\n }\n const response = await fetch(record.webhookUrl, {\n body,\n headers,\n method: \"POST\",\n });\n if (!response.ok) {\n throw new BatchworkError(\n `batchwork: webhook delivery to ${record.webhookUrl} failed (${response.status}).`\n );\n }\n};\n\n/**\n * Create a managed poller: register submitted batches with `track`, then run\n * `tick` on a schedule (cron) to poll open batches and deliver one unified,\n * signed webhook per batch when it finishes. For OpenAI, mount\n * `openaiWebhookHandler` to skip polling and react to native webhooks instead.\n */\nexport const createBatchPoller = (options: BatchPollerOptions): BatchPoller => {\n const resolveCredentials = (provider: BatchProvider): ProviderCredentials => {\n if (typeof options.credentials === \"function\") {\n return options.credentials(provider);\n }\n return options.credentials ?? {};\n };\n\n const sink = options.onComplete ?? sendWebhook;\n\n const deliver = async (\n record: TrackedBatch,\n snapshot: BatchSnapshot\n ): Promise<void> => {\n // Run the side effect before marking delivered: if it throws, the record\n // stays pending and is retried on the next tick (at-least-once delivery).\n await sink(record, snapshot);\n await options.store.set({\n ...record,\n deliveredAt: new Date().toISOString(),\n status: snapshot.status,\n });\n };\n\n const track = async (\n target: TrackTarget,\n opts: TrackOptions\n ): Promise<TrackedBatch> => {\n const record: TrackedBatch = {\n createdAt: new Date().toISOString(),\n id: target.id,\n provider: target.provider,\n status: target.status ?? \"in_progress\",\n webhookSecret: opts.secret,\n webhookUrl: opts.webhookUrl,\n };\n await options.store.set(record);\n return record;\n };\n\n const process = async (\n record: TrackedBatch,\n delivered: string[]\n ): Promise<void> => {\n const adapter = getAdapter(record.provider);\n const snapshot = await adapter.retrieve(\n record.id,\n resolveCredentials(record.provider)\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n delivered.push(record.id);\n } else if (snapshot.status !== record.status) {\n await options.store.set({ ...record, status: snapshot.status });\n }\n };\n\n const tick = async (): Promise<TickResult> => {\n const pending = await options.store.list({ delivered: false });\n const delivered: string[] = [];\n const failed: { id: string; error: string }[] = [];\n for (const record of pending) {\n // oxlint-disable-next-line no-await-in-loop -- batches are polled serially\n // to avoid hammering provider rate limits; deliver before the next.\n try {\n // oxlint-disable-next-line no-await-in-loop -- see above.\n await process(record, delivered);\n } catch (error) {\n // Without an `onError` handler, preserve the propagate-the-throw\n // behavior; with one, report and continue so a single failing batch\n // can't starve the rest of the queue. Either way the record stays\n // pending (it was never marked delivered) and retries next tick.\n if (!options.onError) {\n throw error;\n }\n options.onError(record, error);\n failed.push({\n error: error instanceof Error ? error.message : String(error),\n id: record.id,\n });\n }\n }\n return failed.length > 0\n ? { checked: pending.length, delivered, failed }\n : { checked: pending.length, delivered };\n };\n\n const openaiWebhookHandler =\n (config: OpenAIWebhookOptions) =>\n async (request: Request): Promise<Response> => {\n let verified: { body: string };\n try {\n verified = await verifyWebhook(request, config.signingSecret);\n } catch {\n return new Response(\"invalid signature\", { status: 400 });\n }\n\n const payload = JSON.parse(verified.body) as {\n type?: string;\n data?: { id?: string };\n };\n if (!payload.type?.startsWith(\"batch.\")) {\n return new Response(\"ignored\", { status: 202 });\n }\n const id = payload.data?.id;\n if (!id) {\n return new Response(\"missing batch id\", { status: 400 });\n }\n const record = await options.store.get(id);\n if (!record || record.deliveredAt) {\n return new Response(\"ok\", { status: 200 });\n }\n const snapshot = await getAdapter(\"openai\").retrieve(\n id,\n resolveCredentials(\"openai\")\n );\n if (isTerminalStatus(snapshot.status)) {\n await deliver(record, snapshot);\n }\n return new Response(\"ok\", { status: 200 });\n };\n\n return { deliver, openaiWebhookHandler, tick, track };\n};\n", | ||
| "import type { BatchStore, TrackedBatch } from \"./types\";\n\n/** An in-memory `BatchStore`. Suitable for development and single-process use. */\nexport const createMemoryStore = (): BatchStore => {\n const records = new Map<string, TrackedBatch>();\n\n return {\n delete: (id) => {\n records.delete(id);\n return Promise.resolve();\n },\n get: (id) => Promise.resolve(records.get(id) ?? null),\n list: (filter) => {\n const all = [...records.values()];\n if (filter?.delivered === undefined) {\n return Promise.resolve(all);\n }\n const { delivered } = filter;\n return Promise.resolve(\n all.filter((record) => (record.deliveredAt !== undefined) === delivered)\n );\n },\n set: (record) => {\n records.set(record.id, record);\n return Promise.resolve();\n },\n };\n};\n" | ||
| ], | ||
| "mappings": ";;;;;;;AAGA,IAAM,kBAAuE;AAAA,EAC3E,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AACV;AAGO,IAAM,UAAU,CACrB,UACA,cACuB;AAAA,EACvB,aAAa,SAAS,aAAa,YAAY;AAAA,EAC/C,WAAW,SAAS,WAAW,YAAY;AAAA,EAC3C,IAAI,SAAS;AAAA,EACb;AAAA,EACA,eAAe,SAAS;AAAA,EACxB,MAAM,gBAAgB,SAAS,WAAW;AAC5C;;;ACdA,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAC1B,IAAM,gBAAgB;AACtB,IAAM,UAAU,IAAI;AAEpB,IAAM,gBAAgB,CAAC,UACrB,WAAW,KAAK,KAAK,KAAK,GAAG,CAAC,SAAS,KAAK,YAAY,CAAC,KAAK,CAAC;AAEjE,IAAM,gBAAgB,CAAC,UAA8B;AAAA,EACnD,IAAI,SAAS;AAAA,EACb,WAAW,QAAQ,OAAO;AAAA,IACxB,UAAU,OAAO,cAAc,IAAI;AAAA,EACrC;AAAA,EACA,OAAO,KAAK,MAAM;AAAA;AAGpB,IAAM,YAAY,CAAC,WAAuC;AAAA,EACxD,MAAM,MAAM,OAAO,WAAW,aAAa,IACvC,cAAc,OAAO,MAAM,cAAc,MAAM,CAAC,IAChD,QAAQ,OAAO,MAAM;AAAA,EACzB,OAAO,OAAO,OAAO,UACnB,OACA,KACA,EAAE,MAAM,WAAW,MAAM,OAAO,GAChC,OACA,CAAC,QAAQ,QAAQ,CACnB;AAAA;AAGF,IAAM,cAAc,OAClB,QACA,YACoB;AAAA,EACpB,MAAM,MAAM,MAAM,UAAU,MAAM;AAAA,EAClC,MAAM,YAAY,MAAM,OAAO,OAAO,KACpC,QACA,KACA,QAAQ,OAAO,OAAO,CACxB;AAAA,EACA,OAAO,cAAc,IAAI,WAAW,SAAS,CAAC;AAAA;AAGhD,IAAM,gBAAgB,OACpB,QACA,SACA,cACqB;AAAA,EACrB,MAAM,MAAM,MAAM,UAAU,MAAM;AAAA,EAClC,OAAO,OAAO,OAAO,OACnB,QACA,KACA,cAAc,SAAS,GACvB,QAAQ,OAAO,OAAO,CACxB;AAAA;AAIK,IAAM,cAAc,OACzB,QACA,IACA,MACA,qBACoC;AAAA,EACpC,MAAM,YAAY,KAAK,MAAM,gBAAgB,EAAE,SAAS;AAAA,EACxD,MAAM,YAAY,MAAM,YAAY,QAAQ,GAAG,MAAM,aAAa,MAAM;AAAA,EACxE,OAAO;AAAA,IACL,cAAc;AAAA,IACd,qBAAqB,GAAG,qBAAqB;AAAA,IAC7C,qBAAqB;AAAA,EACvB;AAAA;AAcK,IAAM,gBAAgB,OAC3B,SACA,WAC6B;AAAA,EAC7B,MAAM,KAAK,QAAQ,QAAQ,IAAI,YAAY;AAAA,EAC3C,MAAM,YAAY,QAAQ,QAAQ,IAAI,mBAAmB;AAAA,EACzD,MAAM,kBAAkB,QAAQ,QAAQ,IAAI,mBAAmB;AAAA,EAC/D,IAAI,EAAE,MAAM,aAAa,kBAAkB;AAAA,IACzC,MAAM,IAAI,eAAe,+CAA+C;AAAA,EAC1E;AAAA,EAEA,MAAM,UAAU,OAAO,SAAS;AAAA,EAChC,IACE,CAAC,OAAO,SAAS,OAAO,KACxB,KAAK,IAAI,KAAK,IAAI,IAAI,OAAO,OAAO,IAAI,mBACxC;AAAA,IACA,MAAM,IAAI,eAAe,iDAAiD;AAAA,EAC5E;AAAA,EAEA,MAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,EAChC,MAAM,UAAU,GAAG,MAAM,aAAa;AAAA,EACtC,MAAM,aAAa,gBAAgB,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS;AAAA,IAC1D,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAAA,IAC9B,OAAO,UAAU,KAAK,OAAO,KAAK,MAAM,QAAQ,CAAC;AAAA,GAClD;AAAA,EAED,IAAI,QAAQ;AAAA,EACZ,WAAW,aAAa,YAAY;AAAA,IAElC,IAAI,MAAM,cAAc,QAAQ,SAAS,SAAS,GAAG;AAAA,MACnD,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EACA,IAAI,CAAC,OAAO;AAAA,IACV,MAAM,IAAI,eACR,mDACF;AAAA,EACF;AAAA,EAEA,OAAO,EAAE,MAAM,IAAI,WAAW,QAAQ;AAAA;AAOjC,IAAM,qBAAqB,OAChC,SACA,WAC+B;AAAA,EAC/B,QAAQ,SAAS,MAAM,cAAc,SAAS,MAAM;AAAA,EACpD,OAAO,KAAK,MAAM,IAAI;AAAA;;;AClExB,IAAM,cAA8B,OAAO,QAAQ,aAAa;AAAA,EAC9D,IAAI,CAAC,OAAO,YAAY;AAAA,IACtB,MAAM,IAAI,eACR,2DACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,KAAK,UAAU,QAAQ,OAAO,UAAU,QAAQ,CAAC;AAAA,EAC9D,MAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI,OAAO,eAAe;AAAA,IACxB,OAAO,OACL,SACA,MAAM,YACJ,OAAO,eACP,OAAO,IACP,MACA,KAAK,IAAI,IAAI,IACf,CACF;AAAA,EACF;AAAA,EACA,MAAM,WAAW,MAAM,MAAM,OAAO,YAAY;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAAA,EACD,IAAI,CAAC,SAAS,IAAI;AAAA,IAChB,MAAM,IAAI,eACR,kCAAkC,OAAO,sBAAsB,SAAS,UAC1E;AAAA,EACF;AAAA;AASK,IAAM,oBAAoB,CAAC,YAA6C;AAAA,EAC7E,MAAM,qBAAqB,CAAC,aAAiD;AAAA,IAC3E,IAAI,OAAO,QAAQ,gBAAgB,YAAY;AAAA,MAC7C,OAAO,QAAQ,YAAY,QAAQ;AAAA,IACrC;AAAA,IACA,OAAO,QAAQ,eAAe,CAAC;AAAA;AAAA,EAGjC,MAAM,OAAO,QAAQ,cAAc;AAAA,EAEnC,MAAM,UAAU,OACd,QACA,aACkB;AAAA,IAGlB,MAAM,KAAK,QAAQ,QAAQ;AAAA,IAC3B,MAAM,QAAQ,MAAM,IAAI;AAAA,SACnB;AAAA,MACH,aAAa,IAAI,KAAK,EAAE,YAAY;AAAA,MACpC,QAAQ,SAAS;AAAA,IACnB,CAAC;AAAA;AAAA,EAGH,MAAM,QAAQ,OACZ,QACA,SAC0B;AAAA,IAC1B,MAAM,SAAuB;AAAA,MAC3B,WAAW,IAAI,KAAK,EAAE,YAAY;AAAA,MAClC,IAAI,OAAO;AAAA,MACX,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO,UAAU;AAAA,MACzB,eAAe,KAAK;AAAA,MACpB,YAAY,KAAK;AAAA,IACnB;AAAA,IACA,MAAM,QAAQ,MAAM,IAAI,MAAM;AAAA,IAC9B,OAAO;AAAA;AAAA,EAGT,MAAM,UAAU,OACd,QACA,cACkB;AAAA,IAClB,MAAM,UAAU,WAAW,OAAO,QAAQ;AAAA,IAC1C,MAAM,WAAW,MAAM,QAAQ,SAC7B,OAAO,IACP,mBAAmB,OAAO,QAAQ,CACpC;AAAA,IACA,IAAI,iBAAiB,SAAS,MAAM,GAAG;AAAA,MACrC,MAAM,QAAQ,QAAQ,QAAQ;AAAA,MAC9B,UAAU,KAAK,OAAO,EAAE;AAAA,IAC1B,EAAO,SAAI,SAAS,WAAW,OAAO,QAAQ;AAAA,MAC5C,MAAM,QAAQ,MAAM,IAAI,KAAK,QAAQ,QAAQ,SAAS,OAAO,CAAC;AAAA,IAChE;AAAA;AAAA,EAGF,MAAM,OAAO,YAAiC;AAAA,IAC5C,MAAM,UAAU,MAAM,QAAQ,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AAAA,IAC7D,MAAM,YAAsB,CAAC;AAAA,IAC7B,MAAM,SAA0C,CAAC;AAAA,IACjD,WAAW,UAAU,SAAS;AAAA,MAG5B,IAAI;AAAA,QAEF,MAAM,QAAQ,QAAQ,SAAS;AAAA,QAC/B,OAAO,OAAO;AAAA,QAKd,IAAI,CAAC,QAAQ,SAAS;AAAA,UACpB,MAAM;AAAA,QACR;AAAA,QACA,QAAQ,QAAQ,QAAQ,KAAK;AAAA,QAC7B,OAAO,KAAK;AAAA,UACV,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,UAC5D,IAAI,OAAO;AAAA,QACb,CAAC;AAAA;AAAA,IAEL;AAAA,IACA,OAAO,OAAO,SAAS,IACnB,EAAE,SAAS,QAAQ,QAAQ,WAAW,OAAO,IAC7C,EAAE,SAAS,QAAQ,QAAQ,UAAU;AAAA;AAAA,EAG3C,MAAM,uBACJ,CAAC,WACD,OAAO,YAAwC;AAAA,IAC7C,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,WAAW,MAAM,cAAc,SAAS,OAAO,aAAa;AAAA,MAC5D,MAAM;AAAA,MACN,OAAO,IAAI,SAAS,qBAAqB,EAAE,QAAQ,IAAI,CAAC;AAAA;AAAA,IAG1D,MAAM,UAAU,KAAK,MAAM,SAAS,IAAI;AAAA,IAIxC,IAAI,CAAC,QAAQ,MAAM,WAAW,QAAQ,GAAG;AAAA,MACvC,OAAO,IAAI,SAAS,WAAW,EAAE,QAAQ,IAAI,CAAC;AAAA,IAChD;AAAA,IACA,MAAM,KAAK,QAAQ,MAAM;AAAA,IACzB,IAAI,CAAC,IAAI;AAAA,MACP,OAAO,IAAI,SAAS,oBAAoB,EAAE,QAAQ,IAAI,CAAC;AAAA,IACzD;AAAA,IACA,MAAM,SAAS,MAAM,QAAQ,MAAM,IAAI,EAAE;AAAA,IACzC,IAAI,CAAC,UAAU,OAAO,aAAa;AAAA,MACjC,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAAA,IACA,MAAM,WAAW,MAAM,WAAW,QAAQ,EAAE,SAC1C,IACA,mBAAmB,QAAQ,CAC7B;AAAA,IACA,IAAI,iBAAiB,SAAS,MAAM,GAAG;AAAA,MACrC,MAAM,QAAQ,QAAQ,QAAQ;AAAA,IAChC;AAAA,IACA,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA;AAAA,EAG7C,OAAO,EAAE,SAAS,sBAAsB,MAAM,MAAM;AAAA;;;AC1O/C,IAAM,oBAAoB,MAAkB;AAAA,EACjD,MAAM,UAAU,IAAI;AAAA,EAEpB,OAAO;AAAA,IACL,QAAQ,CAAC,OAAO;AAAA,MACd,QAAQ,OAAO,EAAE;AAAA,MACjB,OAAO,QAAQ,QAAQ;AAAA;AAAA,IAEzB,KAAK,CAAC,OAAO,QAAQ,QAAQ,QAAQ,IAAI,EAAE,KAAK,IAAI;AAAA,IACpD,MAAM,CAAC,WAAW;AAAA,MAChB,MAAM,MAAM,CAAC,GAAG,QAAQ,OAAO,CAAC;AAAA,MAChC,IAAI,QAAQ,cAAc,WAAW;AAAA,QACnC,OAAO,QAAQ,QAAQ,GAAG;AAAA,MAC5B;AAAA,MACA,QAAQ,cAAc;AAAA,MACtB,OAAO,QAAQ,QACb,IAAI,OAAO,CAAC,WAAY,OAAO,gBAAgB,cAAe,SAAS,CACzE;AAAA;AAAA,IAEF,KAAK,CAAC,WAAW;AAAA,MACf,QAAQ,IAAI,OAAO,IAAI,MAAM;AAAA,MAC7B,OAAO,QAAQ,QAAQ;AAAA;AAAA,EAE3B;AAAA;", | ||
| "debugId": "344750DFEF66E25964756E2164756E21", | ||
| "names": [] | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
296229
14.59%95
4.4%2791
11.02%