@itsoltech/basal-client 0.2.0 → 0.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @itsoltech/basal-client
2
2
 
3
- TypeScript/JavaScript SDK for the native Basal API (`POST /v1/basal`). ESM and CommonJS, with type declarations.
3
+ TypeScript/JavaScript SDK for the native Basal API (`POST /v1/basal`) and the Batch API. ESM and CommonJS, with type declarations.
4
4
  Node.js 22+ or modern browsers with `fetch` and `AbortController`. No runtime dependencies or GPU requirement.
5
5
 
6
6
  This package is versioned independently. Release tag: `sdk-typescript-vX.Y.Z`.
@@ -75,6 +75,60 @@ validated before the promise resolves. Typed evidence defaults to an empty array
75
75
  retains the native JSON without those defaults. The one-argument raw `decide(request, signal)` remains available.
76
76
 
77
77
 
78
+ ## Batch API
79
+
80
+ Requires a Basal server with the Batch API enabled ([docs/BATCHES.md](https://github.com/itsoltech/basal-rs/blob/main/docs/BATCHES.md)).
81
+ Every Batch and Files route needs `apiKey` set to a batch token `<key_id>.<secret>`, also on loopback; the owner
82
+ of every file and batch comes from that token. Records are native `/v1/basal` requests; the SDK only transfers files and controls batches. It never polls:
83
+ call `getBatch` at an interval your application chooses and stop on `completed`, `failed`, `expired` or `cancelled`.
84
+
85
+ ```typescript
86
+ import { BasalClient, batchInput, buildRequest, choice, parseBatchResults } from "@itsoltech/basal-client";
87
+
88
+ const client = new BasalClient({ baseUrl: "https://basal.example.com", apiKey: "<key_id>.<secret>" });
89
+ const body = buildRequest("Klient prosi o zwrot.", { department: choice("Dział?", { returns: "Zwroty", support: "Wsparcie" }) }, {});
90
+ const file = await client.uploadFile({ file: batchInput([{ custom_id: "ticket-1", body }]), filename: "tickets.jsonl" });
91
+ const batch = await client.createBatch({ input_file_id: file.id, metadata: { source: "tickets" } }, { idempotencyKey: "tickets-2026-10-20" });
92
+ // Later:
93
+ const current = await client.getBatch(batch.id);
94
+ if (current.status === "completed" && current.output_file_id) {
95
+ const lines = parseBatchResults(await client.downloadFile(current.output_file_id, { maxBytes: 256 * 1024 * 1024 }));
96
+ }
97
+ ```
98
+
99
+ | Method | Route |
100
+ |---|---|
101
+ | `uploadFile({file, filename, expires_after?}, {signal?, timeoutMs?})` | `POST /v1/files` |
102
+ | `listFiles({after?, limit?, purpose?, signal?})`, `getFile(id)`, `deleteFile(id)` | `/v1/files[/{id}]` |
103
+ | `downloadFile(id, {signal?, timeoutMs?, maxBytes?})` → `Uint8Array` | `GET /v1/files/{id}/content` |
104
+ | `createBatch({input_file_id, metadata?, output_expires_after?}, {idempotencyKey?, signal?})` | `POST /v1/batches` |
105
+ | `listBatches({after?, limit?, signal?})`, `getBatch(id)`, `cancelBatch(id)` | `/v1/batches[/{id}[/cancel]]` |
106
+
107
+ Uploads accept `Uint8Array`, `ArrayBuffer`, `Blob` (a browser `File`, or Node's `fs.openAsBlob`) or a
108
+ `ReadableStream<Uint8Array>`. They never fetch URLs or read paths. The form sends `expires_after[...]`
109
+ and `purpose=batch` before the file part, as the server requires. Bytes and Blobs are sent with a
110
+ Content-Length; a stream is sent chunked and needs streaming request bodies (`duplex: "half"`): Node.js,
111
+ or Chromium over HTTP/2. Otherwise pass a Blob. The SDK takes ownership of a stream: it is
112
+ consumed and cancelled on abort, timeout or an early server answer (e.g. 413). The filename (1–255 UTF-8 bytes)
113
+ must not contain control characters or backslashes; it is stored by the server as given.
114
+
115
+ `downloadFile` returns the raw bytes and never parses JSON. It buffers the body in memory and is bounded by
116
+ `maxResponseBytes` (8 MiB by default). For larger result files, raise `maxBytes` for that call, and
117
+ `timeoutMs` if needed, instead of raising the client-wide limit. `parseBatchResults` turns the bytes into result lines; their
118
+ `response.body` is the native answer (`DecisionResponse` for status 200, the native error body otherwise).
119
+
120
+ `createBatch` fills `endpoint: "/v1/basal"` and `completion_window: "24h"` (the only accepted values).
121
+ `metadata` keeps insertion order, except integer-like keys, which JavaScript enumerates first.
122
+ `idempotencyKey` (1–128 visible ASCII characters) makes a retried create return the same batch while its
123
+ metadata is retained. The SDK does not retry on its own. Resource ids and cursors are URL-encoded;
124
+ the server answers malformed ids like missing resources (404). `cancelBatch` returns the batch in
125
+ `cancelling`; cancellation ends asynchronously.
126
+
127
+ Errors use the same classes as the online API: `BasalHTTPError` with the
128
+ `{"error": {message, type, param, code}}` body and `retryAfter` (429/503), and
129
+ `BasalResponseTooLargeError` above the byte cap. Resource types preserve unknown fields at runtime, and
130
+ status/code enumerations accept future string values.
131
+
78
132
  [Full documentation and publication setup](https://github.com/itsoltech/basal-rs/blob/main/docs/SDK.md).
79
133
 
80
134
 
@@ -103,14 +157,28 @@ Usage exposes `longest_prompt_tokens`, `second_order_branches` and media counts,
103
157
  soft tokens and detail. Discovery exposes `prompt_format`, `orders`, enabled
104
158
  media and calibration temperatures. Model names remain strings: use exact names
105
159
  from model discovery, not a hardcoded model enumeration. Keep old models and
106
- raw JSON access available. These SDKs cover server-supported format-2 decisions;
107
- upstream 2.1 task types are not implemented by this server.
160
+ raw JSON access available. The server validates each model’s capabilities.
161
+ Basal 2.1 task helpers are described below.
108
162
 
109
163
  ```typescript
110
164
  import { BasalClient, image, tool, multi } from "@itsoltech/basal-client";
111
165
  const response = await new BasalClient().decide("Inspect the supplied material.", {
112
- route: tool("Choose a tool.", [{name:"inspect", description:"Inspect material", inputSchema:{type:"object"}}], {extra:["none"], rank:2}),
166
+ route: tool("Choose a tool.", [{name:"inspect", description:"Inspect material", inputSchema:{type:"object"}}], {extra:["none"], rank:2, description_chars:120}),
113
167
  labels: multi("Select visible objects.", {animal:"An animal", vehicle:"A vehicle"}, {objective:"f1"}),
114
168
  }, {model:"basal-omni-1.0-mini", lang:"en", media:[image(new Uint8Array(await file.arrayBuffer()))]});
115
169
  console.log(response.answers.route, response.answers.labels.expected_score);
116
170
  ```
171
+
172
+ Tool descriptions are normalized by the server. Omit `description_chars`
173
+ (`DescriptionChars` in .NET) to keep the default cap of 300 Unicode code points;
174
+ use `0` to disable truncation. Descriptions longer than a positive cap are
175
+ shortened at a word boundary where possible, with an ellipsis appended. The SDK forwards this option
176
+ without changing the supplied tool definitions.
177
+
178
+ ## Experimental Basal 2.1 tasks
179
+
180
+ Typed classify, verify, compress and extract factories preserve nullable task automation,
181
+ Unicode code point offsets and nested JSON extraction values. Task answers do not share
182
+ the primitive probabilities field. See [task semantics](../../docs/TASKS.md) and
183
+ [SDK examples](../../docs/SDK.md#zadania-basal-21). These source changes require a server
184
+ with the experimental task layer; installing an SDK does not update the inference server.
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resourcePath = resourcePath;
4
+ exports.listPath = listPath;
5
+ exports.idempotencyHeader = idempotencyHeader;
6
+ exports.createBody = createBody;
7
+ exports.uploadForm = uploadForm;
8
+ exports.batchInput = batchInput;
9
+ exports.parseBatchResults = parseBatchResults;
10
+ const errors_cjs_1 = require("./errors.cjs");
11
+ const encoder = new TextEncoder();
12
+ /** Path segment of a resource id. `.` and `..` would be resolved as dot segments by URL parsing. */
13
+ function resourcePath(prefix, id, suffix = "") {
14
+ if (typeof id !== "string" || !id || id === "." || id === "..")
15
+ throw new TypeError("resource id must be a nonempty string");
16
+ return `${prefix}/${encodeURIComponent(id)}${suffix}`;
17
+ }
18
+ function listPath(path, options) {
19
+ const query = new URLSearchParams();
20
+ if (options.after !== undefined) {
21
+ if (typeof options.after !== "string" || !options.after)
22
+ throw new TypeError("after must be a nonempty id");
23
+ query.set("after", options.after);
24
+ }
25
+ if (options.limit !== undefined) {
26
+ if (!Number.isSafeInteger(options.limit) || options.limit < 1)
27
+ throw new TypeError("limit must be a positive integer");
28
+ query.set("limit", String(options.limit));
29
+ }
30
+ if (options.purpose !== undefined) {
31
+ if (typeof options.purpose !== "string" || !options.purpose)
32
+ throw new TypeError("purpose must be a nonempty string");
33
+ query.set("purpose", options.purpose);
34
+ }
35
+ const encoded = query.toString();
36
+ return encoded ? `${path}?${encoded}` : path;
37
+ }
38
+ function idempotencyHeader(key) {
39
+ if (key === undefined)
40
+ return {};
41
+ if (typeof key !== "string" || !/^[!-~]{1,128}$/.test(key)) {
42
+ throw new TypeError("idempotencyKey must have 1-128 visible ASCII characters");
43
+ }
44
+ return { "Idempotency-Key": key };
45
+ }
46
+ function createBody(request) {
47
+ if (!request || typeof request !== "object" || typeof request.input_file_id !== "string" || !request.input_file_id) {
48
+ throw new TypeError("input_file_id must be a nonempty string");
49
+ }
50
+ // Extra caller fields are forwarded; the server refuses fields it does not know.
51
+ return { endpoint: "/v1/basal", completion_window: "24h", ...request };
52
+ }
53
+ function expiresFields(expires) {
54
+ if (expires === undefined)
55
+ return [];
56
+ if (!expires || expires.anchor !== "created_at" || !Number.isSafeInteger(expires.seconds) || expires.seconds < 0) {
57
+ throw new TypeError("expires_after needs anchor created_at and whole seconds");
58
+ }
59
+ return [["expires_after[anchor]", expires.anchor], ["expires_after[seconds]", String(expires.seconds)]];
60
+ }
61
+ /** Multipart form of an upload: `expires_after[...]`, then `purpose`, then the file part last (the server reserves the
62
+ * upload session at the file part, so expiry fields must precede it).
63
+ */
64
+ function uploadForm(request) {
65
+ if (!request || typeof request !== "object")
66
+ throw new TypeError("upload request must be an object");
67
+ const { file, filename } = request;
68
+ if (request.purpose !== undefined && request.purpose !== "batch")
69
+ throw new TypeError("purpose must be batch");
70
+ // Control characters (CR/LF) would split the part header; a backslash is ambiguous in a quoted filename.
71
+ if (typeof filename !== "string" || !filename || encoder.encode(filename).length > 255 || /[\p{Cc}\\]/u.test(filename)) {
72
+ throw new TypeError("filename must have 1-255 UTF-8 bytes without control characters or backslashes");
73
+ }
74
+ const random = new Uint8Array(16);
75
+ globalThis.crypto.getRandomValues(random);
76
+ const boundary = `basal-${Array.from(random, b => b.toString(16).padStart(2, "0")).join("")}`;
77
+ let head = "";
78
+ for (const [name, value] of [...expiresFields(request.expires_after), ["purpose", "batch"]]) {
79
+ head += `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
80
+ }
81
+ head += `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename.replaceAll('"', '\\"')}"\r\n`
82
+ + "Content-Type: application/jsonl\r\n\r\n";
83
+ const prefix = encoder.encode(head);
84
+ const suffix = encoder.encode(`\r\n--${boundary}--\r\n`);
85
+ const contentType = `multipart/form-data; boundary=${boundary}`;
86
+ if (file instanceof ReadableStream) {
87
+ if (file.locked)
88
+ throw new TypeError("upload stream is locked");
89
+ return { contentType, body: signal => framedStream(prefix, file, suffix, signal) };
90
+ }
91
+ let content;
92
+ if (file instanceof Blob || file instanceof ArrayBuffer)
93
+ content = file;
94
+ else if (file instanceof Uint8Array)
95
+ content = file.buffer instanceof ArrayBuffer ? file : new Uint8Array(file);
96
+ else
97
+ throw new TypeError("file must be a Uint8Array, ArrayBuffer, Blob or ReadableStream");
98
+ return { contentType, body: new Blob([prefix, content, suffix]) };
99
+ }
100
+ /** Streams the multipart prefix, the caller's chunks and the suffix without buffering the file.
101
+ * Fetch implementations may keep pulling a request body after abort, so `signal` cancels the source here.
102
+ */
103
+ function framedStream(prefix, source, suffix, signal) {
104
+ let reader;
105
+ let stage = "prefix";
106
+ const stop = (reason) => {
107
+ signal.removeEventListener("abort", onAbort);
108
+ if (stage === "finished" || stage === "stopped")
109
+ return Promise.resolve();
110
+ stage = "stopped";
111
+ return (reader ? reader.cancel(reason) : source.cancel(reason)).catch(() => { });
112
+ };
113
+ const onAbort = () => { void stop(signal.reason); };
114
+ signal.addEventListener("abort", onAbort, { once: true });
115
+ if (signal.aborted)
116
+ onAbort();
117
+ return new ReadableStream({
118
+ async pull(controller) {
119
+ if (stage === "stopped")
120
+ throw signal.reason ?? new Error("upload stopped");
121
+ if (stage === "prefix") {
122
+ stage = "file";
123
+ controller.enqueue(prefix);
124
+ return;
125
+ }
126
+ if (stage === "finished") {
127
+ controller.close();
128
+ return;
129
+ }
130
+ reader ??= source.getReader();
131
+ const { done, value } = await reader.read();
132
+ // A read cancelled by `stop` ends as done; it must not be framed as a complete file.
133
+ if (stage !== "file")
134
+ throw signal.reason ?? new Error("upload stopped");
135
+ if (!done) {
136
+ if (!(value instanceof Uint8Array)) {
137
+ const error = new TypeError("upload stream chunks must be Uint8Array");
138
+ await stop(error);
139
+ throw error;
140
+ }
141
+ controller.enqueue(value);
142
+ return;
143
+ }
144
+ reader.releaseLock();
145
+ reader = undefined;
146
+ stage = "finished";
147
+ signal.removeEventListener("abort", onAbort);
148
+ controller.enqueue(suffix);
149
+ },
150
+ cancel(reason) { return stop(reason); },
151
+ });
152
+ }
153
+ /** JSONL input file of native records (`method` POST and `url` /v1/basal are filled in). Never sends anything. */
154
+ function batchInput(lines) {
155
+ const out = [];
156
+ for (const line of lines) {
157
+ if (!line || typeof line.custom_id !== "string" || !line.custom_id || !line.body || typeof line.body !== "object") {
158
+ throw new TypeError("each batch line needs a custom_id and a native request body");
159
+ }
160
+ out.push(JSON.stringify({ custom_id: line.custom_id, method: line.method ?? "POST", url: line.url ?? "/v1/basal", body: line.body }));
161
+ }
162
+ if (!out.length)
163
+ throw new TypeError("a batch input needs at least one line");
164
+ return encoder.encode(out.join("\n") + "\n");
165
+ }
166
+ /** Parse downloaded result bytes (output or error file) into lines. Never fetches anything. */
167
+ function parseBatchResults(content) {
168
+ let text;
169
+ try {
170
+ text = new TextDecoder("utf-8", { fatal: true }).decode(content);
171
+ }
172
+ catch (cause) {
173
+ throw new errors_cjs_1.BasalProtocolError("Batch result file is not UTF-8", { cause });
174
+ }
175
+ const lines = text.split("\n");
176
+ if (lines[lines.length - 1] === "")
177
+ lines.pop();
178
+ return lines.map((line, index) => {
179
+ let value;
180
+ try {
181
+ value = JSON.parse(line);
182
+ }
183
+ catch (cause) {
184
+ throw new errors_cjs_1.BasalProtocolError(`Batch result line ${index + 1} is not JSON`, { cause });
185
+ }
186
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
187
+ throw new errors_cjs_1.BasalProtocolError(`Batch result line ${index + 1} is not a JSON object`);
188
+ }
189
+ return value;
190
+ });
191
+ }
@@ -0,0 +1,166 @@
1
+ import type { DecisionRequest, JsonValue } from "./index.cjs";
2
+ /** Batch and Files wire types (`contracts/batches/openapi.json`). Times are Unix seconds.
3
+ * Unknown response fields are preserved at runtime; `(string & {})` keeps enums open to future server values.
4
+ */
5
+ export type FilePurpose = "batch" | "batch_output";
6
+ export interface FileObject {
7
+ id: string;
8
+ object: "file";
9
+ bytes: number;
10
+ created_at: number;
11
+ expires_at: number | null;
12
+ /** As uploaded, or `batch_<id>_output.jsonl` / `batch_<id>_error.jsonl` for results; never a server path. */
13
+ filename: string;
14
+ purpose: FilePurpose | (string & {});
15
+ /** Always `processed` (OpenAI compatibility). */
16
+ status: "processed" | (string & {});
17
+ status_details: string | null;
18
+ }
19
+ export interface DeletedFile {
20
+ id: string;
21
+ object: "file";
22
+ deleted: boolean;
23
+ }
24
+ export interface ListPage<T> {
25
+ object: "list";
26
+ data: T[];
27
+ first_id: string | null;
28
+ last_id: string | null;
29
+ has_more: boolean;
30
+ }
31
+ export type FileList = ListPage<FileObject>;
32
+ export type BatchList = ListPage<Batch>;
33
+ /** At most 16 pairs, keys of 1–64 and values of up to 512 characters, in insertion order (checked by the server).
34
+ * JavaScript enumerates integer-like keys first; use textual keys when order matters.
35
+ */
36
+ export type Metadata = Record<string, string>;
37
+ export interface ExpiresAfter {
38
+ anchor: "created_at";
39
+ seconds: number;
40
+ }
41
+ export type BatchStatus = "validating" | "failed" | "in_progress" | "finalizing" | "completed" | "expired" | "cancelling" | "cancelled";
42
+ export type BatchErrorCode = "empty_file" | "empty_line" | "invalid_json_line" | "line_too_large" | "too_many_records" | "invalid_custom_id" | "duplicate_custom_id" | "invalid_method" | "invalid_url" | "stream_unsupported" | "invalid_body" | "model_not_found" | "mixed_models" | "budget_exceeded" | "host_budget_exceeded" | "unbounded_feature" | "input_changed" | "batch_expired" | "errors_truncated";
43
+ export interface RequestCounts {
44
+ total: number;
45
+ completed: number;
46
+ failed: number;
47
+ }
48
+ export interface BatchErrorEntry {
49
+ code: BatchErrorCode | (string & {});
50
+ message: string;
51
+ param: string | null;
52
+ line: number | null;
53
+ }
54
+ export interface BatchErrors {
55
+ object: "list";
56
+ data: BatchErrorEntry[];
57
+ }
58
+ export interface Batch {
59
+ id: string;
60
+ object: "batch";
61
+ endpoint: "/v1/basal" | (string & {});
62
+ model: string | null;
63
+ errors: BatchErrors | null;
64
+ input_file_id: string;
65
+ completion_window: "24h" | (string & {});
66
+ status: BatchStatus | (string & {});
67
+ output_file_id: string | null;
68
+ error_file_id: string | null;
69
+ created_at: number;
70
+ in_progress_at: number | null;
71
+ /** End of the 24h inference window; export may finish later. */
72
+ expires_at: number;
73
+ finalizing_at: number | null;
74
+ completed_at: number | null;
75
+ failed_at: number | null;
76
+ expired_at: number | null;
77
+ cancelling_at: number | null;
78
+ cancelled_at: number | null;
79
+ request_counts: RequestCounts;
80
+ metadata: Metadata | null;
81
+ }
82
+ /** `POST /v1/batches` body. `endpoint` and `completion_window` default to their only accepted values. */
83
+ export interface CreateBatchRequest {
84
+ input_file_id: string;
85
+ endpoint?: "/v1/basal";
86
+ completion_window?: "24h";
87
+ metadata?: Metadata | null;
88
+ /** Lifetime of each result file, anchored at its creation. */
89
+ output_expires_after?: ExpiresAfter | null;
90
+ }
91
+ /** One line of an input file; `body` is the native `/v1/basal` request (see `buildRequest`). */
92
+ export interface BatchInputLine {
93
+ custom_id: string;
94
+ method?: "POST";
95
+ url?: "/v1/basal";
96
+ body: DecisionRequest;
97
+ }
98
+ /** One line of a result file. `response.body` is the native answer: a `DecisionResponse` for status 200, otherwise
99
+ * the native error body. `error` is set when the record has no response (e.g. `batch_expired`, `result_too_large`).
100
+ */
101
+ export interface BatchResultLine {
102
+ id: string;
103
+ custom_id: string;
104
+ response: {
105
+ status_code: number;
106
+ request_id: string;
107
+ body: {
108
+ [key: string]: JsonValue;
109
+ };
110
+ } | null;
111
+ error: {
112
+ code: string;
113
+ message: string;
114
+ } | null;
115
+ }
116
+ /** Common options of Batch and Files calls. */
117
+ export interface RequestOptions {
118
+ signal?: AbortSignal;
119
+ }
120
+ /** Upload and download options; `timeoutMs` replaces the client deadline for this call only. */
121
+ export interface TransferOptions extends RequestOptions {
122
+ timeoutMs?: number;
123
+ }
124
+ export interface DownloadOptions extends TransferOptions {
125
+ /** Byte cap of this download; defaults to the client's `maxResponseBytes`. The body is buffered in memory. */
126
+ maxBytes?: number;
127
+ }
128
+ export interface CreateBatchOptions extends RequestOptions {
129
+ /** 1–128 visible ASCII characters; a replay with the same request data returns the existing batch. */
130
+ idempotencyKey?: string;
131
+ }
132
+ export interface ListOptions extends RequestOptions {
133
+ after?: string;
134
+ limit?: number;
135
+ }
136
+ export interface ListFilesOptions extends ListOptions {
137
+ purpose?: FilePurpose;
138
+ }
139
+ /** Caller-owned upload content. The SDK never fetches URLs or reads paths; a stream is consumed and locked. */
140
+ export type UploadContent = Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array>;
141
+ export interface UploadFileRequest {
142
+ file: UploadContent;
143
+ /** 1–255 UTF-8 bytes without control characters or backslashes; stored as given, never used as a path. */
144
+ filename: string;
145
+ purpose?: "batch";
146
+ /** File lifetime (server default 30 days). */
147
+ expires_after?: ExpiresAfter;
148
+ }
149
+ /** Path segment of a resource id. `.` and `..` would be resolved as dot segments by URL parsing. */
150
+ export declare function resourcePath(prefix: string, id: string, suffix?: string): string;
151
+ export declare function listPath(path: string, options: ListFilesOptions): string;
152
+ export declare function idempotencyHeader(key: string | undefined): Record<string, string>;
153
+ export declare function createBody(request: CreateBatchRequest): CreateBatchRequest;
154
+ /** A multipart body; a stream body is created per request and stops its source when `signal` aborts. */
155
+ export interface Multipart {
156
+ contentType: string;
157
+ body: Blob | ((signal: AbortSignal) => ReadableStream<Uint8Array>);
158
+ }
159
+ /** Multipart form of an upload: `expires_after[...]`, then `purpose`, then the file part last (the server reserves the
160
+ * upload session at the file part, so expiry fields must precede it).
161
+ */
162
+ export declare function uploadForm(request: UploadFileRequest): Multipart;
163
+ /** JSONL input file of native records (`method` POST and `url` /v1/basal are filled in). Never sends anything. */
164
+ export declare function batchInput(lines: Iterable<BatchInputLine>): Uint8Array;
165
+ /** Parse downloaded result bytes (output or error file) into lines. Never fetches anything. */
166
+ export declare function parseBatchResults(content: Uint8Array): BatchResultLine[];
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BasalResponseTooLargeError = exports.BasalProtocolError = exports.BasalTransportError = exports.BasalHTTPError = exports.BasalError = void 0;
4
+ class BasalError extends Error {
5
+ }
6
+ exports.BasalError = BasalError;
7
+ /** Non-2xx response. Server body and Retry-After are available without logging them. */
8
+ class BasalHTTPError extends BasalError {
9
+ statusCode;
10
+ body;
11
+ retryAfter;
12
+ constructor(statusCode, body, retryAfter) {
13
+ super(`Basal HTTP ${statusCode}`);
14
+ this.statusCode = statusCode;
15
+ this.body = body;
16
+ this.retryAfter = retryAfter;
17
+ this.name = "BasalHTTPError";
18
+ // Node's default inspection and JSON.stringify must not print reflected state or credentials.
19
+ Object.defineProperty(this, "body", { enumerable: false });
20
+ }
21
+ }
22
+ exports.BasalHTTPError = BasalHTTPError;
23
+ class BasalTransportError extends BasalError {
24
+ }
25
+ exports.BasalTransportError = BasalTransportError;
26
+ class BasalProtocolError extends BasalError {
27
+ }
28
+ exports.BasalProtocolError = BasalProtocolError;
29
+ class BasalResponseTooLargeError extends BasalError {
30
+ }
31
+ exports.BasalResponseTooLargeError = BasalResponseTooLargeError;
@@ -0,0 +1,15 @@
1
+ export declare class BasalError extends Error {
2
+ }
3
+ /** Non-2xx response. Server body and Retry-After are available without logging them. */
4
+ export declare class BasalHTTPError extends BasalError {
5
+ readonly statusCode: number;
6
+ readonly body: unknown;
7
+ readonly retryAfter: string | null;
8
+ constructor(statusCode: number, body: unknown, retryAfter: string | null);
9
+ }
10
+ export declare class BasalTransportError extends BasalError {
11
+ }
12
+ export declare class BasalProtocolError extends BasalError {
13
+ }
14
+ export declare class BasalResponseTooLargeError extends BasalError {
15
+ }