🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

batchwork

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

batchwork - npm Package Compare versions

Comparing version
1.0.0
to
1.0.1
+301
dist/chunk-e6qn48qa.js
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

@@ -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"}

@@ -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"}

@@ -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"}

@@ -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 {

@@ -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"}

@@ -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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"}
{
"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": "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",
"debugId": "344750DFEF66E25964756E2164756E21",
"names": []
}