batchwork 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/batch.d.ts +27 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/body.d.ts +18 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/chunk-ab2d71gk.js +325 -0
- package/dist/chunk-ab2d71gk.js.map +12 -0
- package/dist/chunk-kv3847wy.js +1048 -0
- package/dist/chunk-kv3847wy.js.map +24 -0
- package/dist/chunk-v0bahtg2.js +7 -0
- package/dist/chunk-v0bahtg2.js.map +9 -0
- package/dist/chunk-zp2cxkyb.js +226 -0
- package/dist/chunk-zp2cxkyb.js.map +13 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/http.d.ts +5 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +9 -0
- package/dist/job.d.ts +31 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/jsonl.d.ts +17 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/model.d.ts +32 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/next/index.d.ts +59 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +61 -0
- package/dist/next/index.js.map +10 -0
- package/dist/postgres/batch-store.d.ts +19 -0
- package/dist/postgres/batch-store.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +6 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +101 -0
- package/dist/postgres/index.js.map +12 -0
- package/dist/postgres/migrate.d.ts +15 -0
- package/dist/postgres/migrate.d.ts.map +1 -0
- package/dist/postgres/types.d.ts +26 -0
- package/dist/postgres/types.d.ts.map +1 -0
- package/dist/providers/adapter.d.ts +24 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/anthropic.d.ts +3 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/google.d.ts +8 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/groq.d.ts +8 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/mistral.d.ts +8 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/openai-compatible.d.ts +41 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/shared.d.ts +23 -0
- package/dist/providers/shared.d.ts.map +1 -0
- package/dist/providers/together.d.ts +8 -0
- package/dist/providers/together.d.ts.map +1 -0
- package/dist/providers/xai.d.ts +8 -0
- package/dist/providers/xai.d.ts.map +1 -0
- package/dist/redis/batch-store.d.ts +15 -0
- package/dist/redis/batch-store.d.ts.map +1 -0
- package/dist/redis/index.d.ts +4 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +55 -0
- package/dist/redis/index.js.map +11 -0
- package/dist/redis/types.d.ts +19 -0
- package/dist/redis/types.d.ts.map +1 -0
- package/dist/server/events.d.ts +5 -0
- package/dist/server/events.d.ts.map +1 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +19 -0
- package/dist/server/index.js.map +9 -0
- package/dist/server/poller.d.ts +60 -0
- package/dist/server/poller.d.ts.map +1 -0
- package/dist/server/signing.d.ts +20 -0
- package/dist/server/signing.d.ts.map +1 -0
- package/dist/server/store.d.ts +4 -0
- package/dist/server/store.d.ts.map +1 -0
- package/dist/server/types.d.ts +37 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/util.d.ts +10 -0
- package/dist/util.d.ts.map +1 -0
- package/package.json +119 -7
package/dist/jsonl.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL (newline-delimited JSON) helpers. OpenAI batch input/output and
|
|
3
|
+
* Anthropic batch results are all JSONL, so batchwork builds and parses it for
|
|
4
|
+
* the user.
|
|
5
|
+
*/
|
|
6
|
+
/** Serialize an array of values to a JSONL string (trailing newline included). */
|
|
7
|
+
export declare const encodeJsonl: (items: readonly unknown[]) => string;
|
|
8
|
+
/** Parse a complete JSONL string into an array, skipping blank lines. */
|
|
9
|
+
export declare const parseJsonl: <T = unknown>(text: string) => T[];
|
|
10
|
+
/**
|
|
11
|
+
* Stream-parse JSONL from a byte stream, yielding one parsed value per line as
|
|
12
|
+
* it arrives. Memory-efficient for large result files.
|
|
13
|
+
*
|
|
14
|
+
* @yields {T} the parsed value for each non-empty line.
|
|
15
|
+
*/
|
|
16
|
+
export declare function streamJsonl<T = unknown>(source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>): AsyncGenerator<T>;
|
|
17
|
+
//# sourceMappingURL=jsonl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { LanguageModel } from "ai";
|
|
2
|
+
import type { BatchProvider, ProviderCredentials } from "./types";
|
|
3
|
+
/** A fetch implementation compatible with the AI SDK provider `fetch` option. */
|
|
4
|
+
export type CapturingFetch = typeof globalThis.fetch;
|
|
5
|
+
/** OpenAI exposes several request shapes; we mirror the one the model implies. */
|
|
6
|
+
export type OpenAIModelKind = "chat" | "responses" | "completion";
|
|
7
|
+
export interface ResolvedModel {
|
|
8
|
+
/** Relevant for OpenAI; other providers always use a single chat endpoint. */
|
|
9
|
+
kind: OpenAIModelKind;
|
|
10
|
+
modelId: string;
|
|
11
|
+
provider: BatchProvider;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve any AI SDK `model` (a `"provider/model"` string or a provider model
|
|
15
|
+
* object such as `openai("gpt-5.5")`) to a provider + model id + request
|
|
16
|
+
* shape. Gateway/registry model objects whose `modelId` is itself
|
|
17
|
+
* `"provider/model"` are also handled.
|
|
18
|
+
*/
|
|
19
|
+
export declare const resolveModel: (model: LanguageModel) => ResolvedModel;
|
|
20
|
+
/**
|
|
21
|
+
* Import the `@ai-sdk/*` package for a provider, translating a missing optional
|
|
22
|
+
* dependency into a `MissingDependencyError`. The importer is injectable (like
|
|
23
|
+
* the capturing `fetch`) so tests can drive the failure paths without
|
|
24
|
+
* uninstalling a package. Exported for testing; not part of the public API.
|
|
25
|
+
*/
|
|
26
|
+
export declare const loadProvider: <T>(provider: BatchProvider, load?: (target: BatchProvider) => Promise<unknown>) => Promise<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Construct an AI SDK model wired to a capturing `fetch`, used to derive the
|
|
29
|
+
* provider request body for each batch item without making a network call.
|
|
30
|
+
*/
|
|
31
|
+
export declare const createCaptureModel: (resolved: ResolvedModel, credentials: ProviderCredentials, fetchImpl: CapturingFetch) => Promise<LanguageModel>;
|
|
32
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAGxC,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAElE,iFAAiF;AACjF,MAAM,MAAM,cAAc,GAAG,OAAO,UAAU,CAAC,KAAK,CAAC;AAErD,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;CACzB;AA4ED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAW,aAAa,KAAG,aAkBnD,CAAC;AA+BF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,CAAC,YACxB,aAAa,SACjB,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,KAChD,OAAO,CAAC,CAAC,CAUX,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,aACnB,aAAa,eACV,mBAAmB,aACrB,cAAc,KACxB,OAAO,CAAC,aAAa,CAqDvB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CredentialResolver, TrackTarget } from "../server/poller";
|
|
2
|
+
import { createMemoryStore } from "../server/store";
|
|
3
|
+
import type { BatchStore, BatchWebhookEvent, TrackedBatch } from "../server/types";
|
|
4
|
+
import type { BatchResult } from "../types";
|
|
5
|
+
export { createMemoryStore };
|
|
6
|
+
export type { BatchStore, BatchWebhookEvent, BatchResult, TrackedBatch, TrackTarget, };
|
|
7
|
+
/**
|
|
8
|
+
* Invoked once per batch when it reaches a terminal status. Persist the results
|
|
9
|
+
* to your database here. `results` streams the parsed result lines for a
|
|
10
|
+
* completed batch and is empty for failure events (`batch.failed` /
|
|
11
|
+
* `batch.expired` / `batch.cancelled`) — inspect `event.type`.
|
|
12
|
+
*
|
|
13
|
+
* May fire more than once for the same batch (a cron tick racing the OpenAI
|
|
14
|
+
* native webhook, or a retry after a partial save), so make persistence
|
|
15
|
+
* idempotent — upsert keyed by `(provider, batchId, customId)`.
|
|
16
|
+
*/
|
|
17
|
+
export type OnBatchComplete = (event: BatchWebhookEvent, results: AsyncIterable<BatchResult>) => void | Promise<void>;
|
|
18
|
+
export interface BatchRoutesOptions {
|
|
19
|
+
store: BatchStore;
|
|
20
|
+
/** Called when each batch finishes; persist results here. */
|
|
21
|
+
onComplete: OnBatchComplete;
|
|
22
|
+
/** Falls back to provider env vars (e.g. `OPENAI_API_KEY`) when omitted. */
|
|
23
|
+
credentials?: CredentialResolver;
|
|
24
|
+
/** When set, the cron `GET` requires `Authorization: Bearer <cronSecret>`. */
|
|
25
|
+
cronSecret?: string;
|
|
26
|
+
/** When set, mounts an OpenAI native-webhook handler on `POST`. */
|
|
27
|
+
openaiSigningSecret?: string;
|
|
28
|
+
/** Observe per-batch processing errors during a tick; the tick continues. */
|
|
29
|
+
onError?: (record: TrackedBatch, error: unknown) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface BatchRoutes {
|
|
32
|
+
/** Cron-triggered poll tick. Wire to Vercel Cron (or any scheduler). */
|
|
33
|
+
GET: (request: Request) => Promise<Response>;
|
|
34
|
+
/** OpenAI native-webhook handler. Present only with `openaiSigningSecret`. */
|
|
35
|
+
POST?: (request: Request) => Promise<Response>;
|
|
36
|
+
/** Register a submitted batch so the cron polls it (a `BatchJob` works). */
|
|
37
|
+
track: (target: TrackTarget) => Promise<TrackedBatch>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build Next.js App Router route handlers that poll your in-flight batches on a
|
|
41
|
+
* cron tick and invoke `onComplete` directly when each finishes — persist
|
|
42
|
+
* results to your DB without round-tripping an HTTP webhook back to your app.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* import { createBatchRoutes, createMemoryStore } from "batchwork/next";
|
|
46
|
+
*
|
|
47
|
+
* export const { GET, POST } = createBatchRoutes({
|
|
48
|
+
* store: createMemoryStore(),
|
|
49
|
+
* cronSecret: process.env.CRON_SECRET,
|
|
50
|
+
* openaiSigningSecret: process.env.OPENAI_WEBHOOK_SECRET,
|
|
51
|
+
* onComplete: async (event, results) => {
|
|
52
|
+
* for await (const r of results) {
|
|
53
|
+
* await db.insert({ id: r.customId, status: r.status, text: r.text });
|
|
54
|
+
* }
|
|
55
|
+
* },
|
|
56
|
+
* });
|
|
57
|
+
*/
|
|
58
|
+
export declare const createBatchRoutes: (options: BatchRoutesOptions) => BatchRoutes;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBatchResults
|
|
3
|
+
} from "../chunk-ab2d71gk.js";
|
|
4
|
+
import {
|
|
5
|
+
createBatchPoller,
|
|
6
|
+
createMemoryStore,
|
|
7
|
+
toEvent
|
|
8
|
+
} from "../chunk-zp2cxkyb.js";
|
|
9
|
+
import"../chunk-kv3847wy.js";
|
|
10
|
+
import"../chunk-v0bahtg2.js";
|
|
11
|
+
|
|
12
|
+
// src/next/index.ts
|
|
13
|
+
var EMPTY_RESULTS = {
|
|
14
|
+
[Symbol.asyncIterator]: () => ({
|
|
15
|
+
next: () => Promise.resolve({ done: true, value: undefined })
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
var createBatchRoutes = (options) => {
|
|
19
|
+
const resolveCredentials = (provider) => {
|
|
20
|
+
if (typeof options.credentials === "function") {
|
|
21
|
+
return options.credentials(provider);
|
|
22
|
+
}
|
|
23
|
+
return options.credentials ?? {};
|
|
24
|
+
};
|
|
25
|
+
const sink = async (record, snapshot) => {
|
|
26
|
+
const event = toEvent(record.provider, snapshot);
|
|
27
|
+
const results = event.type === "batch.completed" ? getBatchResults({
|
|
28
|
+
id: record.id,
|
|
29
|
+
provider: record.provider,
|
|
30
|
+
...resolveCredentials(record.provider)
|
|
31
|
+
}) : EMPTY_RESULTS;
|
|
32
|
+
await options.onComplete(event, results);
|
|
33
|
+
};
|
|
34
|
+
const poller = createBatchPoller({
|
|
35
|
+
credentials: options.credentials,
|
|
36
|
+
onComplete: sink,
|
|
37
|
+
onError: (record, error) => options.onError?.(record, error),
|
|
38
|
+
store: options.store
|
|
39
|
+
});
|
|
40
|
+
const GET = (request) => {
|
|
41
|
+
if (options.cronSecret && request.headers.get("authorization") !== `Bearer ${options.cronSecret}`) {
|
|
42
|
+
return Promise.resolve(new Response("unauthorized", { status: 401 }));
|
|
43
|
+
}
|
|
44
|
+
return poller.tick().then((result) => Response.json(result));
|
|
45
|
+
};
|
|
46
|
+
const track = (target) => poller.track(target, {});
|
|
47
|
+
const routes = { GET, track };
|
|
48
|
+
if (options.openaiSigningSecret) {
|
|
49
|
+
routes.POST = poller.openaiWebhookHandler({
|
|
50
|
+
signingSecret: options.openaiSigningSecret
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return routes;
|
|
54
|
+
};
|
|
55
|
+
export {
|
|
56
|
+
createMemoryStore,
|
|
57
|
+
createBatchRoutes
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
//# debugId=2EB8C739158F3A9164756E2164756E21
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/next/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"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"
|
|
6
|
+
],
|
|
7
|
+
"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;",
|
|
8
|
+
"debugId": "2EB8C739158F3A9164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BatchStore } from "../server/types";
|
|
2
|
+
import type { PostgresStoreOptions } from "./types";
|
|
3
|
+
/** Options for {@link createPostgresStore}. */
|
|
4
|
+
export interface PostgresBatchStoreOptions extends PostgresStoreOptions {
|
|
5
|
+
/** Table to store tracked batches in. Defaults to `batchwork_batches`. */
|
|
6
|
+
table?: string;
|
|
7
|
+
}
|
|
8
|
+
/** `CREATE TABLE`/index DDL for the tracked-batch table (run by `migratePostgres`). */
|
|
9
|
+
export declare const batchTableDdl: (table?: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* A Postgres-backed {@link BatchStore} for the poller. Run {@link migratePostgres}
|
|
12
|
+
* (or {@link batchTableDdl}) once to create the table.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { Pool } from "pg";
|
|
16
|
+
* const store = createPostgresStore({ client: new Pool({ connectionString }) });
|
|
17
|
+
*/
|
|
18
|
+
export declare const createPostgresStore: (options: PostgresBatchStoreOptions) => BatchStore;
|
|
19
|
+
//# sourceMappingURL=batch-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-store.d.ts","sourceRoot":"","sources":["../../src/postgres/batch-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,iBAAiB,CAAC;AAGhE,OAAO,KAAK,EAAE,oBAAoB,EAAe,MAAM,SAAS,CAAC;AAEjE,+CAA+C;AAC/C,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACrE,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyBD,uFAAuF;AACvF,eAAO,MAAM,aAAa,sBAAkC,MAc3D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,YACrB,yBAAyB,KACjC,UAkDF,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { batchTableDdl, createPostgresStore } from "./batch-store";
|
|
2
|
+
export type { PostgresBatchStoreOptions } from "./batch-store";
|
|
3
|
+
export { migratePostgres } from "./migrate";
|
|
4
|
+
export type { MigrateOptions } from "./migrate";
|
|
5
|
+
export { DEFAULT_BATCH_TABLE, type PostgresStoreOptions, type SqlExecutor, } from "./types";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import"../chunk-v0bahtg2.js";
|
|
2
|
+
|
|
3
|
+
// src/postgres/types.ts
|
|
4
|
+
var DEFAULT_BATCH_TABLE = "batchwork_batches";
|
|
5
|
+
var assertSafeTable = (table) => {
|
|
6
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*(?<schema>\.[A-Za-z_][A-Za-z0-9_]*)?$/u.test(table)) {
|
|
7
|
+
throw new Error(`batchwork: unsafe Postgres table name ${JSON.stringify(table)}.`);
|
|
8
|
+
}
|
|
9
|
+
return table;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/postgres/batch-store.ts
|
|
13
|
+
var toBatch = (row) => ({
|
|
14
|
+
createdAt: row.created_at,
|
|
15
|
+
id: row.id,
|
|
16
|
+
provider: row.provider,
|
|
17
|
+
status: row.status,
|
|
18
|
+
...row.webhook_url === null ? {} : { webhookUrl: row.webhook_url },
|
|
19
|
+
...row.webhook_secret === null ? {} : { webhookSecret: row.webhook_secret },
|
|
20
|
+
...row.delivered_at === null ? {} : { deliveredAt: row.delivered_at }
|
|
21
|
+
});
|
|
22
|
+
var batchTableDdl = (table = DEFAULT_BATCH_TABLE) => {
|
|
23
|
+
const name = assertSafeTable(table);
|
|
24
|
+
return `
|
|
25
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
26
|
+
id text PRIMARY KEY,
|
|
27
|
+
provider text NOT NULL,
|
|
28
|
+
webhook_url text,
|
|
29
|
+
webhook_secret text,
|
|
30
|
+
status text NOT NULL,
|
|
31
|
+
created_at text NOT NULL,
|
|
32
|
+
delivered_at text
|
|
33
|
+
);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS ${name}_undelivered_idx
|
|
35
|
+
ON ${name} (created_at) WHERE delivered_at IS NULL;`;
|
|
36
|
+
};
|
|
37
|
+
var createPostgresStore = (options) => {
|
|
38
|
+
const { client } = options;
|
|
39
|
+
const table = assertSafeTable(options.table ?? DEFAULT_BATCH_TABLE);
|
|
40
|
+
return {
|
|
41
|
+
delete: async (id) => {
|
|
42
|
+
await client.query(`DELETE FROM ${table} WHERE id = $1`, [id]);
|
|
43
|
+
},
|
|
44
|
+
get: async (id) => {
|
|
45
|
+
const { rows } = await client.query(`SELECT * FROM ${table} WHERE id = $1`, [id]);
|
|
46
|
+
const [row] = rows;
|
|
47
|
+
return row ? toBatch(row) : null;
|
|
48
|
+
},
|
|
49
|
+
list: async (filter) => {
|
|
50
|
+
let text = `SELECT * FROM ${table}`;
|
|
51
|
+
if (filter?.delivered === true) {
|
|
52
|
+
text += " WHERE delivered_at IS NOT NULL";
|
|
53
|
+
} else if (filter?.delivered === false) {
|
|
54
|
+
text += " WHERE delivered_at IS NULL";
|
|
55
|
+
}
|
|
56
|
+
const { rows } = await client.query(text);
|
|
57
|
+
return rows.map(toBatch);
|
|
58
|
+
},
|
|
59
|
+
set: async (record) => {
|
|
60
|
+
await client.query(`INSERT INTO ${table}
|
|
61
|
+
(id, provider, webhook_url, webhook_secret, status, created_at, delivered_at)
|
|
62
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
63
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
64
|
+
provider = EXCLUDED.provider,
|
|
65
|
+
webhook_url = EXCLUDED.webhook_url,
|
|
66
|
+
webhook_secret = EXCLUDED.webhook_secret,
|
|
67
|
+
status = EXCLUDED.status,
|
|
68
|
+
created_at = EXCLUDED.created_at,
|
|
69
|
+
delivered_at = EXCLUDED.delivered_at`, [
|
|
70
|
+
record.id,
|
|
71
|
+
record.provider,
|
|
72
|
+
record.webhookUrl ?? null,
|
|
73
|
+
record.webhookSecret ?? null,
|
|
74
|
+
record.status,
|
|
75
|
+
record.createdAt,
|
|
76
|
+
record.deliveredAt ?? null
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
// src/postgres/migrate.ts
|
|
82
|
+
var runDdl = async (client, ddl) => {
|
|
83
|
+
for (const statement of ddl.split(";")) {
|
|
84
|
+
const trimmed = statement.trim();
|
|
85
|
+
if (trimmed) {
|
|
86
|
+
await client.query(trimmed);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var migratePostgres = async (client, options = {}) => {
|
|
91
|
+
await runDdl(client, batchTableDdl(assertSafeTable(options.batchTable ?? DEFAULT_BATCH_TABLE)));
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
migratePostgres,
|
|
95
|
+
createPostgresStore,
|
|
96
|
+
batchTableDdl,
|
|
97
|
+
DEFAULT_BATCH_TABLE
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
//# debugId=119F35EA4B39BF6464756E2164756E21
|
|
101
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/postgres/types.ts", "../src/postgres/batch-store.ts", "../src/postgres/migrate.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * The minimal slice of a Postgres client batchwork's adapters use: a\n * node-postgres-style `query(text, params)` returning `{ rows }`. This is\n * deliberately structural so you can pass a `pg.Pool`/`pg.Client`, a\n * `@neondatabase/serverless` client, or any thin wrapper that exposes `query` —\n * batchwork never imports a driver, so it adds no dependency of its own.\n */\nexport interface SqlExecutor {\n query: <Row = Record<string, unknown>>(\n text: string,\n params?: unknown[]\n ) => Promise<{ rows: Row[] }>;\n}\n\n/** Options for the Postgres-backed batch store. */\nexport interface PostgresStoreOptions {\n /** A `pg.Pool`-compatible client (anything with `query(text, params)`). */\n client: SqlExecutor;\n}\n\n/** Table name defaults to this; override for a custom schema. */\nexport const DEFAULT_BATCH_TABLE = \"batchwork_batches\";\n\n/**\n * Guard against SQL injection through a caller-supplied table name: identifiers\n * can't be parameterized, so they're interpolated directly and must be a plain\n * unquoted identifier (optionally schema-qualified).\n */\nexport const assertSafeTable = (table: string): string => {\n if (\n !/^[A-Za-z_][A-Za-z0-9_]*(?<schema>\\.[A-Za-z_][A-Za-z0-9_]*)?$/u.test(table)\n ) {\n throw new Error(\n `batchwork: unsafe Postgres table name ${JSON.stringify(table)}.`\n );\n }\n return table;\n};\n",
|
|
6
|
+
"import type { BatchStore, TrackedBatch } from \"../server/types\";\nimport type { BatchProvider, BatchStatus } from \"../types\";\nimport { assertSafeTable, DEFAULT_BATCH_TABLE } from \"./types\";\nimport type { PostgresStoreOptions, SqlExecutor } from \"./types\";\n\n/** Options for {@link createPostgresStore}. */\nexport interface PostgresBatchStoreOptions extends PostgresStoreOptions {\n /** Table to store tracked batches in. Defaults to `batchwork_batches`. */\n table?: string;\n}\n\n/** The raw row shape returned by the tracked-batch table. */\ninterface BatchRow {\n id: string;\n provider: BatchProvider;\n webhook_url: string | null;\n webhook_secret: string | null;\n status: BatchStatus;\n created_at: string;\n delivered_at: string | null;\n}\n\nconst toBatch = (row: BatchRow): TrackedBatch => ({\n createdAt: row.created_at,\n id: row.id,\n provider: row.provider,\n status: row.status,\n // Re-add optional fields only when set, so the shape matches `createMemoryStore`\n // (and `list`'s `deliveredAt !== undefined` delivery filter stays correct).\n ...(row.webhook_url === null ? {} : { webhookUrl: row.webhook_url }),\n ...(row.webhook_secret === null ? {} : { webhookSecret: row.webhook_secret }),\n ...(row.delivered_at === null ? {} : { deliveredAt: row.delivered_at }),\n});\n\n/** `CREATE TABLE`/index DDL for the tracked-batch table (run by `migratePostgres`). */\nexport const batchTableDdl = (table = DEFAULT_BATCH_TABLE): string => {\n const name = assertSafeTable(table);\n return `\nCREATE TABLE IF NOT EXISTS ${name} (\n id text PRIMARY KEY,\n provider text NOT NULL,\n webhook_url text,\n webhook_secret text,\n status text NOT NULL,\n created_at text NOT NULL,\n delivered_at text\n);\nCREATE INDEX IF NOT EXISTS ${name}_undelivered_idx\n ON ${name} (created_at) WHERE delivered_at IS NULL;`;\n};\n\n/**\n * A Postgres-backed {@link BatchStore} for the poller. Run {@link migratePostgres}\n * (or {@link batchTableDdl}) once to create the table.\n *\n * @example\n * import { Pool } from \"pg\";\n * const store = createPostgresStore({ client: new Pool({ connectionString }) });\n */\nexport const createPostgresStore = (\n options: PostgresBatchStoreOptions\n): BatchStore => {\n const { client }: { client: SqlExecutor } = options;\n const table = assertSafeTable(options.table ?? DEFAULT_BATCH_TABLE);\n\n return {\n delete: async (id) => {\n await client.query(`DELETE FROM ${table} WHERE id = $1`, [id]);\n },\n get: async (id) => {\n const { rows } = await client.query<BatchRow>(\n `SELECT * FROM ${table} WHERE id = $1`,\n [id]\n );\n const [row] = rows;\n return row ? toBatch(row) : null;\n },\n list: async (filter) => {\n let text = `SELECT * FROM ${table}`;\n if (filter?.delivered === true) {\n text += \" WHERE delivered_at IS NOT NULL\";\n } else if (filter?.delivered === false) {\n text += \" WHERE delivered_at IS NULL\";\n }\n const { rows } = await client.query<BatchRow>(text);\n return rows.map(toBatch);\n },\n set: async (record) => {\n await client.query(\n `INSERT INTO ${table}\n (id, provider, webhook_url, webhook_secret, status, created_at, delivered_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (id) DO UPDATE SET\n provider = EXCLUDED.provider,\n webhook_url = EXCLUDED.webhook_url,\n webhook_secret = EXCLUDED.webhook_secret,\n status = EXCLUDED.status,\n created_at = EXCLUDED.created_at,\n delivered_at = EXCLUDED.delivered_at`,\n [\n record.id,\n record.provider,\n record.webhookUrl ?? null,\n record.webhookSecret ?? null,\n record.status,\n record.createdAt,\n record.deliveredAt ?? null,\n ]\n );\n },\n };\n};\n",
|
|
7
|
+
"import { batchTableDdl } from \"./batch-store\";\nimport { assertSafeTable, DEFAULT_BATCH_TABLE } from \"./types\";\nimport type { SqlExecutor } from \"./types\";\n\n/** Options for {@link migratePostgres}. */\nexport interface MigrateOptions {\n /** Tracked-batch table name. Defaults to `batchwork_batches`. */\n batchTable?: string;\n}\n\n// Run a multi-statement DDL string one statement at a time: some drivers\n// (notably PGlite's extended protocol) reject multiple statements per query.\nconst runDdl = async (client: SqlExecutor, ddl: string): Promise<void> => {\n for (const statement of ddl.split(\";\")) {\n const trimmed = statement.trim();\n if (trimmed) {\n // oxlint-disable-next-line no-await-in-loop -- DDL must run in order.\n await client.query(trimmed);\n }\n }\n};\n\n/**\n * Create the table the batch store needs. Idempotent (`CREATE TABLE IF NOT\n * EXISTS`), so it's safe to run on every boot. Call once before using the store.\n *\n * @example\n * await migratePostgres(pool);\n */\nexport const migratePostgres = async (\n client: SqlExecutor,\n options: MigrateOptions = {}\n): Promise<void> => {\n await runDdl(\n client,\n batchTableDdl(assertSafeTable(options.batchTable ?? DEFAULT_BATCH_TABLE))\n );\n};\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": ";;;AAqBO,IAAM,sBAAsB;AAO5B,IAAM,kBAAkB,CAAC,UAA0B;AAAA,EACxD,IACE,CAAC,gEAAgE,KAAK,KAAK,GAC3E;AAAA,IACA,MAAM,IAAI,MACR,yCAAyC,KAAK,UAAU,KAAK,IAC/D;AAAA,EACF;AAAA,EACA,OAAO;AAAA;;;ACdT,IAAM,UAAU,CAAC,SAAiC;AAAA,EAChD,WAAW,IAAI;AAAA,EACf,IAAI,IAAI;AAAA,EACR,UAAU,IAAI;AAAA,EACd,QAAQ,IAAI;AAAA,KAGR,IAAI,gBAAgB,OAAO,CAAC,IAAI,EAAE,YAAY,IAAI,YAAY;AAAA,KAC9D,IAAI,mBAAmB,OAAO,CAAC,IAAI,EAAE,eAAe,IAAI,eAAe;AAAA,KACvE,IAAI,iBAAiB,OAAO,CAAC,IAAI,EAAE,aAAa,IAAI,aAAa;AACvE;AAGO,IAAM,gBAAgB,CAAC,QAAQ,wBAAgC;AAAA,EACpE,MAAM,OAAO,gBAAgB,KAAK;AAAA,EAClC,OAAO;AAAA,6BACoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BASA;AAAA,OACtB;AAAA;AAWA,IAAM,sBAAsB,CACjC,YACe;AAAA,EACf,QAAQ,WAAoC;AAAA,EAC5C,MAAM,QAAQ,gBAAgB,QAAQ,SAAS,mBAAmB;AAAA,EAElE,OAAO;AAAA,IACL,QAAQ,OAAO,OAAO;AAAA,MACpB,MAAM,OAAO,MAAM,eAAe,uBAAuB,CAAC,EAAE,CAAC;AAAA;AAAA,IAE/D,KAAK,OAAO,OAAO;AAAA,MACjB,QAAQ,SAAS,MAAM,OAAO,MAC5B,iBAAiB,uBACjB,CAAC,EAAE,CACL;AAAA,MACA,OAAO,OAAO;AAAA,MACd,OAAO,MAAM,QAAQ,GAAG,IAAI;AAAA;AAAA,IAE9B,MAAM,OAAO,WAAW;AAAA,MACtB,IAAI,OAAO,iBAAiB;AAAA,MAC5B,IAAI,QAAQ,cAAc,MAAM;AAAA,QAC9B,QAAQ;AAAA,MACV,EAAO,SAAI,QAAQ,cAAc,OAAO;AAAA,QACtC,QAAQ;AAAA,MACV;AAAA,MACA,QAAQ,SAAS,MAAM,OAAO,MAAgB,IAAI;AAAA,MAClD,OAAO,KAAK,IAAI,OAAO;AAAA;AAAA,IAEzB,KAAK,OAAO,WAAW;AAAA,MACrB,MAAM,OAAO,MACX,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kDAUf;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO,cAAc;AAAA,QACrB,OAAO,iBAAiB;AAAA,QACxB,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO,eAAe;AAAA,MACxB,CACF;AAAA;AAAA,EAEJ;AAAA;;AClGF,IAAM,SAAS,OAAO,QAAqB,QAA+B;AAAA,EACxE,WAAW,aAAa,IAAI,MAAM,GAAG,GAAG;AAAA,IACtC,MAAM,UAAU,UAAU,KAAK;AAAA,IAC/B,IAAI,SAAS;AAAA,MAEX,MAAM,OAAO,MAAM,OAAO;AAAA,IAC5B;AAAA,EACF;AAAA;AAUK,IAAM,kBAAkB,OAC7B,QACA,UAA0B,CAAC,MACT;AAAA,EAClB,MAAM,OACJ,QACA,cAAc,gBAAgB,QAAQ,cAAc,mBAAmB,CAAC,CAC1E;AAAA;",
|
|
10
|
+
"debugId": "119F35EA4B39BF6464756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SqlExecutor } from "./types";
|
|
2
|
+
/** Options for {@link migratePostgres}. */
|
|
3
|
+
export interface MigrateOptions {
|
|
4
|
+
/** Tracked-batch table name. Defaults to `batchwork_batches`. */
|
|
5
|
+
batchTable?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Create the table the batch store needs. Idempotent (`CREATE TABLE IF NOT
|
|
9
|
+
* EXISTS`), so it's safe to run on every boot. Call once before using the store.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* await migratePostgres(pool);
|
|
13
|
+
*/
|
|
14
|
+
export declare const migratePostgres: (client: SqlExecutor, options?: MigrateOptions) => Promise<void>;
|
|
15
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/postgres/migrate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAcD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,WAClB,WAAW,YACV,cAAc,KACtB,OAAO,CAAC,IAAI,CAKd,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal slice of a Postgres client batchwork's adapters use: a
|
|
3
|
+
* node-postgres-style `query(text, params)` returning `{ rows }`. This is
|
|
4
|
+
* deliberately structural so you can pass a `pg.Pool`/`pg.Client`, a
|
|
5
|
+
* `@neondatabase/serverless` client, or any thin wrapper that exposes `query` —
|
|
6
|
+
* batchwork never imports a driver, so it adds no dependency of its own.
|
|
7
|
+
*/
|
|
8
|
+
export interface SqlExecutor {
|
|
9
|
+
query: <Row = Record<string, unknown>>(text: string, params?: unknown[]) => Promise<{
|
|
10
|
+
rows: Row[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
/** Options for the Postgres-backed batch store. */
|
|
14
|
+
export interface PostgresStoreOptions {
|
|
15
|
+
/** A `pg.Pool`-compatible client (anything with `query(text, params)`). */
|
|
16
|
+
client: SqlExecutor;
|
|
17
|
+
}
|
|
18
|
+
/** Table name defaults to this; override for a custom schema. */
|
|
19
|
+
export declare const DEFAULT_BATCH_TABLE = "batchwork_batches";
|
|
20
|
+
/**
|
|
21
|
+
* Guard against SQL injection through a caller-supplied table name: identifiers
|
|
22
|
+
* can't be parameterized, so they're interpolated directly and must be a plain
|
|
23
|
+
* unquoted identifier (optionally schema-qualified).
|
|
24
|
+
*/
|
|
25
|
+
export declare const assertSafeTable: (table: string) => string;
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/postgres/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,EAAE,KACf,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,eAAe,UAAW,MAAM,KAAG,MAS/C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BuiltRequest } from "../body";
|
|
2
|
+
import type { BatchProvider, BatchResult, BatchSnapshot, ProviderCredentials } from "../types";
|
|
3
|
+
export interface SubmitInput {
|
|
4
|
+
built: BuiltRequest[];
|
|
5
|
+
credentials: ProviderCredentials;
|
|
6
|
+
/** Endpoint path the captured requests target (e.g. `/v1/chat/completions`). */
|
|
7
|
+
endpoint: string;
|
|
8
|
+
metadata?: Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Resolved model id. Needed by providers that set the model on the batch/job
|
|
11
|
+
* (Gemini puts it in the create URL, Mistral on the job); ignored by providers
|
|
12
|
+
* that carry the model in each request line (OpenAI, Anthropic).
|
|
13
|
+
*/
|
|
14
|
+
modelId: string;
|
|
15
|
+
}
|
|
16
|
+
/** A provider's batch lifecycle: submit, poll, stream results, cancel. */
|
|
17
|
+
export interface BatchAdapter {
|
|
18
|
+
cancel: (id: string, credentials: ProviderCredentials) => Promise<void>;
|
|
19
|
+
readonly id: BatchProvider;
|
|
20
|
+
results: (id: string, credentials: ProviderCredentials) => AsyncGenerator<BatchResult>;
|
|
21
|
+
retrieve: (id: string, credentials: ProviderCredentials) => Promise<BatchSnapshot>;
|
|
22
|
+
submit: (input: SubmitInput) => Promise<BatchSnapshot>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BatchAdapter } from "./adapter";
|
|
2
|
+
/**
|
|
3
|
+
* Google Gemini (Developer API) batch adapter: submits inline requests via
|
|
4
|
+
* `:batchGenerateContent`, polls the returned long-running operation, then reads
|
|
5
|
+
* the inline responses keyed by each request's `metadata.key`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const googleAdapter: BatchAdapter;
|
|
8
|
+
//# sourceMappingURL=google.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groq batch adapter. Groq's batch API is OpenAI-compatible (Files API +
|
|
3
|
+
* `/batches`), so it reuses the OpenAI-compatible flow. Groq is served under
|
|
4
|
+
* `/openai/v1`, but its batch `url` must be `/v1/chat/completions`, so the
|
|
5
|
+
* captured endpoint's `/openai` prefix is stripped.
|
|
6
|
+
*/
|
|
7
|
+
export declare const groqAdapter: import("./adapter").BatchAdapter;
|
|
8
|
+
//# sourceMappingURL=groq.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groq.d.ts","sourceRoot":"","sources":["../../src/providers/groq.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,kCAOtB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BatchProvider } from "../types";
|
|
2
|
+
import type { BatchAdapter } from "./adapter";
|
|
3
|
+
export declare const getAdapter: (provider: BatchProvider) => BatchAdapter;
|
|
4
|
+
export type { BatchAdapter, SubmitInput } from "./adapter";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAmB9C,eAAO,MAAM,UAAU,aAAc,aAAa,KAAG,YACjC,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BatchAdapter } from "./adapter";
|
|
2
|
+
/**
|
|
3
|
+
* Mistral batch adapter: uploads JSONL (`purpose=batch`), creates a job with the
|
|
4
|
+
* model/endpoint set on the job, polls `status`, then downloads the OpenAI-shaped
|
|
5
|
+
* output/error files.
|
|
6
|
+
*/
|
|
7
|
+
export declare const mistralAdapter: BatchAdapter;
|
|
8
|
+
//# sourceMappingURL=mistral.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for OpenAI-compatible batch adapters. OpenAI, Groq, and Together AI
|
|
3
|
+
* all share the same lifecycle — upload JSONL via the Files API, create a batch
|
|
4
|
+
* referencing the file, poll a `status` field, then download output/error files
|
|
5
|
+
* — differing only in base URL, credentials, and minor request-line shape.
|
|
6
|
+
*/
|
|
7
|
+
import type { BatchProvider } from "../types";
|
|
8
|
+
import type { BatchAdapter } from "./adapter";
|
|
9
|
+
export type BatchLineFormat = "body-only" | "method-url";
|
|
10
|
+
export interface OpenAICompatibleConfig {
|
|
11
|
+
apiKeyEnv: string;
|
|
12
|
+
apiKeyLabel: string;
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
completionWindow?: string;
|
|
15
|
+
filePurpose?: string;
|
|
16
|
+
id: BatchProvider;
|
|
17
|
+
/**
|
|
18
|
+
* Override the JSONL upload. Defaults to a direct multipart POST to the Files
|
|
19
|
+
* API (OpenAI, Groq); Together replaces it with its presigned-URL flow.
|
|
20
|
+
* Returns the uploaded file id.
|
|
21
|
+
*/
|
|
22
|
+
uploadFile?: (args: {
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
headers: Record<string, string>;
|
|
25
|
+
jsonl: string;
|
|
26
|
+
purpose: string;
|
|
27
|
+
}) => Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Input-line shape. `method-url` writes `{ custom_id, method, url, body }`
|
|
30
|
+
* (OpenAI, Groq); `body-only` writes `{ custom_id, body }` (Together).
|
|
31
|
+
*/
|
|
32
|
+
lineFormat?: BatchLineFormat;
|
|
33
|
+
/**
|
|
34
|
+
* Map the captured endpoint path to the value the provider expects in the
|
|
35
|
+
* batch `url`/`endpoint` field (e.g. Groq serves under `/openai/v1` but its
|
|
36
|
+
* batch `url` must be `/v1/chat/completions`).
|
|
37
|
+
*/
|
|
38
|
+
normalizeEndpoint?: (endpoint: string) => string;
|
|
39
|
+
}
|
|
40
|
+
export declare const createOpenAICompatibleAdapter: (config: OpenAICompatibleConfig) => BatchAdapter;
|
|
41
|
+
//# sourceMappingURL=openai-compatible.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI batch adapter: builds JSONL, uploads it via the Files API
|
|
3
|
+
* (`purpose=batch`), creates the batch, polls `status`, then downloads and
|
|
4
|
+
* parses the output and error files.
|
|
5
|
+
*/
|
|
6
|
+
export declare const openaiAdapter: import("./adapter").BatchAdapter;
|
|
7
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,aAAa,kCAMxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers shared across the OpenAI-shaped adapters (OpenAI, Groq, Together,
|
|
3
|
+
* Mistral). These providers all upload JSONL via a Files API, return results as
|
|
4
|
+
* JSONL keyed by `custom_id`, and shape each result line like OpenAI's
|
|
5
|
+
* (`{ custom_id, response: { status_code, body }, error }`).
|
|
6
|
+
*/
|
|
7
|
+
import type { BatchResult, BatchUsage, ProviderCredentials } from "../types";
|
|
8
|
+
/** Resolve an API key from credentials or the provider's env var, or throw. */
|
|
9
|
+
export declare const resolveApiKey: (credentials: ProviderCredentials, envVar: string, label: string) => string;
|
|
10
|
+
export declare const textFromBody: (body: unknown) => string | undefined;
|
|
11
|
+
export declare const usageFromBody: (body: unknown) => BatchUsage | undefined;
|
|
12
|
+
/** Normalize an OpenAI-shaped result line into a {@link BatchResult}. */
|
|
13
|
+
export declare const normalizeOpenAIResult: (line: unknown) => BatchResult;
|
|
14
|
+
export declare const uploadInputFile: (jsonl: string, baseUrl: string, headers: Record<string, string>, options?: {
|
|
15
|
+
purpose?: string | null;
|
|
16
|
+
}) => Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Stream a JSONL result file's content as normalized OpenAI-shaped results.
|
|
19
|
+
*
|
|
20
|
+
* @yields {BatchResult} the normalized result for each line.
|
|
21
|
+
*/
|
|
22
|
+
export declare function streamResultFile(fileId: string, baseUrl: string, headers: Record<string, string>): AsyncGenerator<BatchResult>;
|
|
23
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/providers/shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,WAAW,EAEX,UAAU,EACV,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAMlB,+EAA+E;AAC/E,eAAO,MAAM,aAAa,gBACX,mBAAmB,UACxB,MAAM,SACP,MAAM,KACZ,MAQF,CAAC;AAEF,eAAO,MAAM,YAAY,SAAU,OAAO,KAAG,MAAM,GAAG,SAUrD,CAAC;AAEF,eAAO,MAAM,aAAa,SAAU,OAAO,KAAG,UAAU,GAAG,SAmB1D,CAAC;AAaF,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,SAAU,OAAO,KAAG,WAkCrD,CAAC;AAGF,eAAO,MAAM,eAAe,UACnB,MAAM,WACJ,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YACtB;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,KACnC,OAAO,CAAC,MAAM,CAiBhB,CAAC;AAEF;;;;GAIG;AAEH,wBAAuB,gBAAgB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,cAAc,CAAC,WAAW,CAAC,CAO7B"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Together AI batch adapter. Together's batch API mirrors OpenAI's (Files API +
|
|
3
|
+
* `/batches`) but uses the leaner `{ custom_id, body }` input-line shape, with
|
|
4
|
+
* the endpoint declared on the batch rather than per line, and a presigned-URL
|
|
5
|
+
* file upload in place of OpenAI's direct multipart POST.
|
|
6
|
+
*/
|
|
7
|
+
export declare const togetherAdapter: import("./adapter").BatchAdapter;
|
|
8
|
+
//# sourceMappingURL=together.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"together.d.ts","sourceRoot":"","sources":["../../src/providers/together.ts"],"names":[],"mappings":"AAkEA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kCAQ1B,CAAC"}
|