Skip to content

Commit 7c40d45

Browse files
chore(client): move misc public files to new core/ directory, deprecate old paths (#40)
1 parent e7ada47 commit 7c40d45

21 files changed

+268
-254
lines changed

src/api-promise.ts

Lines changed: 2 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,2 @@
1-
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2-
3-
import { type Lightswitch } from './client';
4-
5-
import { type PromiseOrValue } from './internal/types';
6-
import { APIResponseProps, defaultParseResponse } from './internal/parse';
7-
8-
/**
9-
* A subclass of `Promise` providing additional helper methods
10-
* for interacting with the SDK.
11-
*/
12-
export class APIPromise<T> extends Promise<T> {
13-
private parsedPromise: Promise<T> | undefined;
14-
#client: Lightswitch;
15-
16-
constructor(
17-
client: Lightswitch,
18-
private responsePromise: Promise<APIResponseProps>,
19-
private parseResponse: (
20-
client: Lightswitch,
21-
props: APIResponseProps,
22-
) => PromiseOrValue<T> = defaultParseResponse,
23-
) {
24-
super((resolve) => {
25-
// this is maybe a bit weird but this has to be a no-op to not implicitly
26-
// parse the response body; instead .then, .catch, .finally are overridden
27-
// to parse the response
28-
resolve(null as any);
29-
});
30-
this.#client = client;
31-
}
32-
33-
_thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
34-
return new APIPromise(this.#client, this.responsePromise, async (client, props) =>
35-
transform(await this.parseResponse(client, props), props),
36-
);
37-
}
38-
39-
/**
40-
* Gets the raw `Response` instance instead of parsing the response
41-
* data.
42-
*
43-
* If you want to parse the response body but still get the `Response`
44-
* instance, you can use {@link withResponse()}.
45-
*
46-
* 👋 Getting the wrong TypeScript type for `Response`?
47-
* Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
48-
* to your `tsconfig.json`.
49-
*/
50-
asResponse(): Promise<Response> {
51-
return this.responsePromise.then((p) => p.response);
52-
}
53-
54-
/**
55-
* Gets the parsed response data and the raw `Response` instance.
56-
*
57-
* If you just want to get the raw `Response` instance without parsing it,
58-
* you can use {@link asResponse()}.
59-
*
60-
* 👋 Getting the wrong TypeScript type for `Response`?
61-
* Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
62-
* to your `tsconfig.json`.
63-
*/
64-
async withResponse(): Promise<{ data: T; response: Response }> {
65-
const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
66-
return { data, response };
67-
}
68-
69-
private parse(): Promise<T> {
70-
if (!this.parsedPromise) {
71-
this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
72-
}
73-
return this.parsedPromise;
74-
}
75-
76-
override then<TResult1 = T, TResult2 = never>(
77-
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
78-
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
79-
): Promise<TResult1 | TResult2> {
80-
return this.parse().then(onfulfilled, onrejected);
81-
}
82-
83-
override catch<TResult = never>(
84-
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
85-
): Promise<T | TResult> {
86-
return this.parse().catch(onrejected);
87-
}
88-
89-
override finally(onfinally?: (() => void) | undefined | null): Promise<T> {
90-
return this.parse().finally(onfinally);
91-
}
92-
}
1+
/** @deprecated Import from ./core/api-promise instead */
2+
export * from './core/api-promise';

src/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import { getPlatformHeaders } from './internal/detect-platform';
1313
import * as Shims from './internal/shims';
1414
import * as Opts from './internal/request-options';
1515
import { VERSION } from './version';
16-
import * as Errors from './error';
17-
import * as Uploads from './uploads';
16+
import * as Errors from './core/error';
17+
import * as Uploads from './core/uploads';
1818
import * as API from './resources/index';
19-
import { APIPromise } from './api-promise';
19+
import { APIPromise } from './core/api-promise';
2020
import { type Fetch } from './internal/builtin-types';
2121
import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
2222
import { FinalRequestOptions, RequestOptions } from './internal/request-options';

src/core/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `core`
2+
3+
This directory holds public modules implementing non-resource-specific SDK functionality.

src/core/api-promise.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
import { type Lightswitch } from '../client';
4+
5+
import { type PromiseOrValue } from '../internal/types';
6+
import { APIResponseProps, defaultParseResponse } from '../internal/parse';
7+
8+
/**
9+
* A subclass of `Promise` providing additional helper methods
10+
* for interacting with the SDK.
11+
*/
12+
export class APIPromise<T> extends Promise<T> {
13+
private parsedPromise: Promise<T> | undefined;
14+
#client: Lightswitch;
15+
16+
constructor(
17+
client: Lightswitch,
18+
private responsePromise: Promise<APIResponseProps>,
19+
private parseResponse: (
20+
client: Lightswitch,
21+
props: APIResponseProps,
22+
) => PromiseOrValue<T> = defaultParseResponse,
23+
) {
24+
super((resolve) => {
25+
// this is maybe a bit weird but this has to be a no-op to not implicitly
26+
// parse the response body; instead .then, .catch, .finally are overridden
27+
// to parse the response
28+
resolve(null as any);
29+
});
30+
this.#client = client;
31+
}
32+
33+
_thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
34+
return new APIPromise(this.#client, this.responsePromise, async (client, props) =>
35+
transform(await this.parseResponse(client, props), props),
36+
);
37+
}
38+
39+
/**
40+
* Gets the raw `Response` instance instead of parsing the response
41+
* data.
42+
*
43+
* If you want to parse the response body but still get the `Response`
44+
* instance, you can use {@link withResponse()}.
45+
*
46+
* 👋 Getting the wrong TypeScript type for `Response`?
47+
* Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
48+
* to your `tsconfig.json`.
49+
*/
50+
asResponse(): Promise<Response> {
51+
return this.responsePromise.then((p) => p.response);
52+
}
53+
54+
/**
55+
* Gets the parsed response data and the raw `Response` instance.
56+
*
57+
* If you just want to get the raw `Response` instance without parsing it,
58+
* you can use {@link asResponse()}.
59+
*
60+
* 👋 Getting the wrong TypeScript type for `Response`?
61+
* Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
62+
* to your `tsconfig.json`.
63+
*/
64+
async withResponse(): Promise<{ data: T; response: Response }> {
65+
const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
66+
return { data, response };
67+
}
68+
69+
private parse(): Promise<T> {
70+
if (!this.parsedPromise) {
71+
this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
72+
}
73+
return this.parsedPromise;
74+
}
75+
76+
override then<TResult1 = T, TResult2 = never>(
77+
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
78+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
79+
): Promise<TResult1 | TResult2> {
80+
return this.parse().then(onfulfilled, onrejected);
81+
}
82+
83+
override catch<TResult = never>(
84+
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
85+
): Promise<T | TResult> {
86+
return this.parse().catch(onrejected);
87+
}
88+
89+
override finally(onfinally?: (() => void) | undefined | null): Promise<T> {
90+
return this.parse().finally(onfinally);
91+
}
92+
}

src/core/error.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
import { castToError } from '../internal/errors';
4+
5+
export class LightswitchError extends Error {}
6+
7+
export class APIError<
8+
TStatus extends number | undefined = number | undefined,
9+
THeaders extends Headers | undefined = Headers | undefined,
10+
TError extends Object | undefined = Object | undefined,
11+
> extends LightswitchError {
12+
/** HTTP status for the response that caused the error */
13+
readonly status: TStatus;
14+
/** HTTP headers for the response that caused the error */
15+
readonly headers: THeaders;
16+
/** JSON body of the response that caused the error */
17+
readonly error: TError;
18+
19+
constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) {
20+
super(`${APIError.makeMessage(status, error, message)}`);
21+
this.status = status;
22+
this.headers = headers;
23+
this.error = error;
24+
}
25+
26+
private static makeMessage(status: number | undefined, error: any, message: string | undefined) {
27+
const msg =
28+
error?.message ?
29+
typeof error.message === 'string' ?
30+
error.message
31+
: JSON.stringify(error.message)
32+
: error ? JSON.stringify(error)
33+
: message;
34+
35+
if (status && msg) {
36+
return `${status} ${msg}`;
37+
}
38+
if (status) {
39+
return `${status} status code (no body)`;
40+
}
41+
if (msg) {
42+
return msg;
43+
}
44+
return '(no status code or body)';
45+
}
46+
47+
static generate(
48+
status: number | undefined,
49+
errorResponse: Object | undefined,
50+
message: string | undefined,
51+
headers: Headers | undefined,
52+
): APIError {
53+
if (!status || !headers) {
54+
return new APIConnectionError({ message, cause: castToError(errorResponse) });
55+
}
56+
57+
const error = errorResponse as Record<string, any>;
58+
59+
if (status === 400) {
60+
return new BadRequestError(status, error, message, headers);
61+
}
62+
63+
if (status === 401) {
64+
return new AuthenticationError(status, error, message, headers);
65+
}
66+
67+
if (status === 403) {
68+
return new PermissionDeniedError(status, error, message, headers);
69+
}
70+
71+
if (status === 404) {
72+
return new NotFoundError(status, error, message, headers);
73+
}
74+
75+
if (status === 409) {
76+
return new ConflictError(status, error, message, headers);
77+
}
78+
79+
if (status === 422) {
80+
return new UnprocessableEntityError(status, error, message, headers);
81+
}
82+
83+
if (status === 429) {
84+
return new RateLimitError(status, error, message, headers);
85+
}
86+
87+
if (status >= 500) {
88+
return new InternalServerError(status, error, message, headers);
89+
}
90+
91+
return new APIError(status, error, message, headers);
92+
}
93+
}
94+
95+
export class APIUserAbortError extends APIError<undefined, undefined, undefined> {
96+
constructor({ message }: { message?: string } = {}) {
97+
super(undefined, undefined, message || 'Request was aborted.', undefined);
98+
}
99+
}
100+
101+
export class APIConnectionError extends APIError<undefined, undefined, undefined> {
102+
constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) {
103+
super(undefined, undefined, message || 'Connection error.', undefined);
104+
// in some environments the 'cause' property is already declared
105+
// @ts-ignore
106+
if (cause) this.cause = cause;
107+
}
108+
}
109+
110+
export class APIConnectionTimeoutError extends APIConnectionError {
111+
constructor({ message }: { message?: string } = {}) {
112+
super({ message: message ?? 'Request timed out.' });
113+
}
114+
}
115+
116+
export class BadRequestError extends APIError<400, Headers> {}
117+
118+
export class AuthenticationError extends APIError<401, Headers> {}
119+
120+
export class PermissionDeniedError extends APIError<403, Headers> {}
121+
122+
export class NotFoundError extends APIError<404, Headers> {}
123+
124+
export class ConflictError extends APIError<409, Headers> {}
125+
126+
export class UnprocessableEntityError extends APIError<422, Headers> {}
127+
128+
export class RateLimitError extends APIError<429, Headers> {}
129+
130+
export class InternalServerError extends APIError<number, Headers> {}

src/core/resource.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
import type { Lightswitch } from '../client';
4+
5+
export class APIResource {
6+
protected _client: Lightswitch;
7+
8+
constructor(client: Lightswitch) {
9+
this._client = client;
10+
}
11+
}

src/core/uploads.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { type Uploadable } from '../internal/uploads';
2+
export { toFile, type ToFileInput } from '../internal/to-file';

0 commit comments

Comments
 (0)