summaryrefslogtreecommitdiffhomepage
path: root/packages/sdk/src/core
diff options
context:
space:
mode:
authorDax <[email protected]>2025-07-31 01:00:29 -0400
committerGitHub <[email protected]>2025-07-31 01:00:29 -0400
commit33cef075d228e80aefb44671ec68e1989c2855a8 (patch)
treed43a5c1bcc40d4d938eacccfd923c80301706cf1 /packages/sdk/src/core
parentb09ebf464552f3899120b22c7a8572669000a554 (diff)
downloadopencode-33cef075d228e80aefb44671ec68e1989c2855a8.tar.gz
opencode-33cef075d228e80aefb44671ec68e1989c2855a8.zip
ci: new publish method (#1451)
Diffstat (limited to 'packages/sdk/src/core')
-rw-r--r--packages/sdk/src/core/README.md3
-rw-r--r--packages/sdk/src/core/api-promise.ts92
-rw-r--r--packages/sdk/src/core/error.ts130
-rw-r--r--packages/sdk/src/core/resource.ts11
-rw-r--r--packages/sdk/src/core/streaming.ts315
-rw-r--r--packages/sdk/src/core/uploads.ts2
6 files changed, 0 insertions, 553 deletions
diff --git a/packages/sdk/src/core/README.md b/packages/sdk/src/core/README.md
deleted file mode 100644
index 485fce861..000000000
--- a/packages/sdk/src/core/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# `core`
-
-This directory holds public modules implementing non-resource-specific SDK functionality.
diff --git a/packages/sdk/src/core/api-promise.ts b/packages/sdk/src/core/api-promise.ts
deleted file mode 100644
index fc1c6dd76..000000000
--- a/packages/sdk/src/core/api-promise.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { type Opencode } from '../client';
-
-import { type PromiseOrValue } from '../internal/types';
-import { APIResponseProps, defaultParseResponse } from '../internal/parse';
-
-/**
- * A subclass of `Promise` providing additional helper methods
- * for interacting with the SDK.
- */
-export class APIPromise<T> extends Promise<T> {
- private parsedPromise: Promise<T> | undefined;
- #client: Opencode;
-
- constructor(
- client: Opencode,
- private responsePromise: Promise<APIResponseProps>,
- private parseResponse: (
- client: Opencode,
- props: APIResponseProps,
- ) => PromiseOrValue<T> = defaultParseResponse,
- ) {
- super((resolve) => {
- // this is maybe a bit weird but this has to be a no-op to not implicitly
- // parse the response body; instead .then, .catch, .finally are overridden
- // to parse the response
- resolve(null as any);
- });
- this.#client = client;
- }
-
- _thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
- return new APIPromise(this.#client, this.responsePromise, async (client, props) =>
- transform(await this.parseResponse(client, props), props),
- );
- }
-
- /**
- * Gets the raw `Response` instance instead of parsing the response
- * data.
- *
- * If you want to parse the response body but still get the `Response`
- * instance, you can use {@link withResponse()}.
- *
- * 👋 Getting the wrong TypeScript type for `Response`?
- * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
- * to your `tsconfig.json`.
- */
- asResponse(): Promise<Response> {
- return this.responsePromise.then((p) => p.response);
- }
-
- /**
- * Gets the parsed response data and the raw `Response` instance.
- *
- * If you just want to get the raw `Response` instance without parsing it,
- * you can use {@link asResponse()}.
- *
- * 👋 Getting the wrong TypeScript type for `Response`?
- * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
- * to your `tsconfig.json`.
- */
- async withResponse(): Promise<{ data: T; response: Response }> {
- const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
- return { data, response };
- }
-
- private parse(): Promise<T> {
- if (!this.parsedPromise) {
- this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
- }
- return this.parsedPromise;
- }
-
- override then<TResult1 = T, TResult2 = never>(
- onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
- ): Promise<TResult1 | TResult2> {
- return this.parse().then(onfulfilled, onrejected);
- }
-
- override catch<TResult = never>(
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
- ): Promise<T | TResult> {
- return this.parse().catch(onrejected);
- }
-
- override finally(onfinally?: (() => void) | undefined | null): Promise<T> {
- return this.parse().finally(onfinally);
- }
-}
diff --git a/packages/sdk/src/core/error.ts b/packages/sdk/src/core/error.ts
deleted file mode 100644
index 44698c739..000000000
--- a/packages/sdk/src/core/error.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { castToError } from '../internal/errors';
-
-export class OpencodeError extends Error {}
-
-export class APIError<
- TStatus extends number | undefined = number | undefined,
- THeaders extends Headers | undefined = Headers | undefined,
- TError extends Object | undefined = Object | undefined,
-> extends OpencodeError {
- /** HTTP status for the response that caused the error */
- readonly status: TStatus;
- /** HTTP headers for the response that caused the error */
- readonly headers: THeaders;
- /** JSON body of the response that caused the error */
- readonly error: TError;
-
- constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) {
- super(`${APIError.makeMessage(status, error, message)}`);
- this.status = status;
- this.headers = headers;
- this.error = error;
- }
-
- private static makeMessage(status: number | undefined, error: any, message: string | undefined) {
- const msg =
- error?.message ?
- typeof error.message === 'string' ?
- error.message
- : JSON.stringify(error.message)
- : error ? JSON.stringify(error)
- : message;
-
- if (status && msg) {
- return `${status} ${msg}`;
- }
- if (status) {
- return `${status} status code (no body)`;
- }
- if (msg) {
- return msg;
- }
- return '(no status code or body)';
- }
-
- static generate(
- status: number | undefined,
- errorResponse: Object | undefined,
- message: string | undefined,
- headers: Headers | undefined,
- ): APIError {
- if (!status || !headers) {
- return new APIConnectionError({ message, cause: castToError(errorResponse) });
- }
-
- const error = errorResponse as Record<string, any>;
-
- if (status === 400) {
- return new BadRequestError(status, error, message, headers);
- }
-
- if (status === 401) {
- return new AuthenticationError(status, error, message, headers);
- }
-
- if (status === 403) {
- return new PermissionDeniedError(status, error, message, headers);
- }
-
- if (status === 404) {
- return new NotFoundError(status, error, message, headers);
- }
-
- if (status === 409) {
- return new ConflictError(status, error, message, headers);
- }
-
- if (status === 422) {
- return new UnprocessableEntityError(status, error, message, headers);
- }
-
- if (status === 429) {
- return new RateLimitError(status, error, message, headers);
- }
-
- if (status >= 500) {
- return new InternalServerError(status, error, message, headers);
- }
-
- return new APIError(status, error, message, headers);
- }
-}
-
-export class APIUserAbortError extends APIError<undefined, undefined, undefined> {
- constructor({ message }: { message?: string } = {}) {
- super(undefined, undefined, message || 'Request was aborted.', undefined);
- }
-}
-
-export class APIConnectionError extends APIError<undefined, undefined, undefined> {
- constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) {
- super(undefined, undefined, message || 'Connection error.', undefined);
- // in some environments the 'cause' property is already declared
- // @ts-ignore
- if (cause) this.cause = cause;
- }
-}
-
-export class APIConnectionTimeoutError extends APIConnectionError {
- constructor({ message }: { message?: string } = {}) {
- super({ message: message ?? 'Request timed out.' });
- }
-}
-
-export class BadRequestError extends APIError<400, Headers> {}
-
-export class AuthenticationError extends APIError<401, Headers> {}
-
-export class PermissionDeniedError extends APIError<403, Headers> {}
-
-export class NotFoundError extends APIError<404, Headers> {}
-
-export class ConflictError extends APIError<409, Headers> {}
-
-export class UnprocessableEntityError extends APIError<422, Headers> {}
-
-export class RateLimitError extends APIError<429, Headers> {}
-
-export class InternalServerError extends APIError<number, Headers> {}
diff --git a/packages/sdk/src/core/resource.ts b/packages/sdk/src/core/resource.ts
deleted file mode 100644
index ccf9032e4..000000000
--- a/packages/sdk/src/core/resource.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import type { Opencode } from '../client';
-
-export abstract class APIResource {
- protected _client: Opencode;
-
- constructor(client: Opencode) {
- this._client = client;
- }
-}
diff --git a/packages/sdk/src/core/streaming.ts b/packages/sdk/src/core/streaming.ts
deleted file mode 100644
index 33dcfd126..000000000
--- a/packages/sdk/src/core/streaming.ts
+++ /dev/null
@@ -1,315 +0,0 @@
-import { OpencodeError } from './error';
-import { type ReadableStream } from '../internal/shim-types';
-import { makeReadableStream } from '../internal/shims';
-import { findDoubleNewlineIndex, LineDecoder } from '../internal/decoders/line';
-import { ReadableStreamToAsyncIterable } from '../internal/shims';
-import { isAbortError } from '../internal/errors';
-import { encodeUTF8 } from '../internal/utils/bytes';
-import { loggerFor } from '../internal/utils/log';
-import type { Opencode } from '../client';
-
-type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
-
-export type ServerSentEvent = {
- event: string | null;
- data: string;
- raw: string[];
-};
-
-export class Stream<Item> implements AsyncIterable<Item> {
- controller: AbortController;
- #client: Opencode | undefined;
-
- constructor(
- private iterator: () => AsyncIterator<Item>,
- controller: AbortController,
- client?: Opencode,
- ) {
- this.controller = controller;
- this.#client = client;
- }
-
- static fromSSEResponse<Item>(
- response: Response,
- controller: AbortController,
- client?: Opencode,
- ): Stream<Item> {
- let consumed = false;
- const logger = client ? loggerFor(client) : console;
-
- async function* iterator(): AsyncIterator<Item, any, undefined> {
- if (consumed) {
- throw new OpencodeError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
- }
- consumed = true;
- let done = false;
- try {
- for await (const sse of _iterSSEMessages(response, controller)) {
- try {
- yield JSON.parse(sse.data);
- } catch (e) {
- logger.error(`Could not parse message into JSON:`, sse.data);
- logger.error(`From chunk:`, sse.raw);
- throw e;
- }
- }
- done = true;
- } catch (e) {
- // If the user calls `stream.controller.abort()`, we should exit without throwing.
- if (isAbortError(e)) return;
- throw e;
- } finally {
- // If the user `break`s, abort the ongoing request.
- if (!done) controller.abort();
- }
- }
-
- return new Stream(iterator, controller, client);
- }
-
- /**
- * Generates a Stream from a newline-separated ReadableStream
- * where each item is a JSON value.
- */
- static fromReadableStream<Item>(
- readableStream: ReadableStream,
- controller: AbortController,
- client?: Opencode,
- ): Stream<Item> {
- let consumed = false;
-
- async function* iterLines(): AsyncGenerator<string, void, unknown> {
- const lineDecoder = new LineDecoder();
-
- const iter = ReadableStreamToAsyncIterable<Bytes>(readableStream);
- for await (const chunk of iter) {
- for (const line of lineDecoder.decode(chunk)) {
- yield line;
- }
- }
-
- for (const line of lineDecoder.flush()) {
- yield line;
- }
- }
-
- async function* iterator(): AsyncIterator<Item, any, undefined> {
- if (consumed) {
- throw new OpencodeError('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
- }
- consumed = true;
- let done = false;
- try {
- for await (const line of iterLines()) {
- if (done) continue;
- if (line) yield JSON.parse(line);
- }
- done = true;
- } catch (e) {
- // If the user calls `stream.controller.abort()`, we should exit without throwing.
- if (isAbortError(e)) return;
- throw e;
- } finally {
- // If the user `break`s, abort the ongoing request.
- if (!done) controller.abort();
- }
- }
-
- return new Stream(iterator, controller, client);
- }
-
- [Symbol.asyncIterator](): AsyncIterator<Item> {
- return this.iterator();
- }
-
- /**
- * Splits the stream into two streams which can be
- * independently read from at different speeds.
- */
- tee(): [Stream<Item>, Stream<Item>] {
- const left: Array<Promise<IteratorResult<Item>>> = [];
- const right: Array<Promise<IteratorResult<Item>>> = [];
- const iterator = this.iterator();
-
- const teeIterator = (queue: Array<Promise<IteratorResult<Item>>>): AsyncIterator<Item> => {
- return {
- next: () => {
- if (queue.length === 0) {
- const result = iterator.next();
- left.push(result);
- right.push(result);
- }
- return queue.shift()!;
- },
- };
- };
-
- return [
- new Stream(() => teeIterator(left), this.controller, this.#client),
- new Stream(() => teeIterator(right), this.controller, this.#client),
- ];
- }
-
- /**
- * Converts this stream to a newline-separated ReadableStream of
- * JSON stringified values in the stream
- * which can be turned back into a Stream with `Stream.fromReadableStream()`.
- */
- toReadableStream(): ReadableStream {
- const self = this;
- let iter: AsyncIterator<Item>;
-
- return makeReadableStream({
- async start() {
- iter = self[Symbol.asyncIterator]();
- },
- async pull(ctrl: any) {
- try {
- const { value, done } = await iter.next();
- if (done) return ctrl.close();
-
- const bytes = encodeUTF8(JSON.stringify(value) + '\n');
-
- ctrl.enqueue(bytes);
- } catch (err) {
- ctrl.error(err);
- }
- },
- async cancel() {
- await iter.return?.();
- },
- });
- }
-}
-
-export async function* _iterSSEMessages(
- response: Response,
- controller: AbortController,
-): AsyncGenerator<ServerSentEvent, void, unknown> {
- if (!response.body) {
- controller.abort();
- if (
- typeof (globalThis as any).navigator !== 'undefined' &&
- (globalThis as any).navigator.product === 'ReactNative'
- ) {
- throw new OpencodeError(
- `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`,
- );
- }
- throw new OpencodeError(`Attempted to iterate over a response with no body`);
- }
-
- const sseDecoder = new SSEDecoder();
- const lineDecoder = new LineDecoder();
-
- const iter = ReadableStreamToAsyncIterable<Bytes>(response.body);
- for await (const sseChunk of iterSSEChunks(iter)) {
- for (const line of lineDecoder.decode(sseChunk)) {
- const sse = sseDecoder.decode(line);
- if (sse) yield sse;
- }
- }
-
- for (const line of lineDecoder.flush()) {
- const sse = sseDecoder.decode(line);
- if (sse) yield sse;
- }
-}
-
-/**
- * Given an async iterable iterator, iterates over it and yields full
- * SSE chunks, i.e. yields when a double new-line is encountered.
- */
-async function* iterSSEChunks(iterator: AsyncIterableIterator<Bytes>): AsyncGenerator<Uint8Array> {
- let data = new Uint8Array();
-
- for await (const chunk of iterator) {
- if (chunk == null) {
- continue;
- }
-
- const binaryChunk =
- chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
- : typeof chunk === 'string' ? encodeUTF8(chunk)
- : chunk;
-
- let newData = new Uint8Array(data.length + binaryChunk.length);
- newData.set(data);
- newData.set(binaryChunk, data.length);
- data = newData;
-
- let patternIndex;
- while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {
- yield data.slice(0, patternIndex);
- data = data.slice(patternIndex);
- }
- }
-
- if (data.length > 0) {
- yield data;
- }
-}
-
-class SSEDecoder {
- private data: string[];
- private event: string | null;
- private chunks: string[];
-
- constructor() {
- this.event = null;
- this.data = [];
- this.chunks = [];
- }
-
- decode(line: string) {
- if (line.endsWith('\r')) {
- line = line.substring(0, line.length - 1);
- }
-
- if (!line) {
- // empty line and we didn't previously encounter any messages
- if (!this.event && !this.data.length) return null;
-
- const sse: ServerSentEvent = {
- event: this.event,
- data: this.data.join('\n'),
- raw: this.chunks,
- };
-
- this.event = null;
- this.data = [];
- this.chunks = [];
-
- return sse;
- }
-
- this.chunks.push(line);
-
- if (line.startsWith(':')) {
- return null;
- }
-
- let [fieldname, _, value] = partition(line, ':');
-
- if (value.startsWith(' ')) {
- value = value.substring(1);
- }
-
- if (fieldname === 'event') {
- this.event = value;
- } else if (fieldname === 'data') {
- this.data.push(value);
- }
-
- return null;
- }
-}
-
-function partition(str: string, delimiter: string): [string, string, string] {
- const index = str.indexOf(delimiter);
- if (index !== -1) {
- return [str.substring(0, index), delimiter, str.substring(index + delimiter.length)];
- }
-
- return [str, '', ''];
-}
diff --git a/packages/sdk/src/core/uploads.ts b/packages/sdk/src/core/uploads.ts
deleted file mode 100644
index 2882ca6d1..000000000
--- a/packages/sdk/src/core/uploads.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { type Uploadable } from '../internal/uploads';
-export { toFile, type ToFileInput } from '../internal/to-file';