@lorikeetai/node-sdk 0.1.0-beta.4 → 0.2.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/CHANGELOG.md +40 -0
- package/README.md +11 -11
- package/_shims/MultipartBody.js.map +1 -1
- package/_shims/MultipartBody.mjs.map +1 -1
- package/_shims/auto/runtime-bun.d.ts.map +1 -1
- package/_shims/auto/runtime-bun.js.map +1 -1
- package/_shims/auto/runtime-node.d.ts.map +1 -1
- package/_shims/auto/runtime-node.js.map +1 -1
- package/_shims/auto/runtime.d.ts.map +1 -1
- package/_shims/auto/runtime.js.map +1 -1
- package/_shims/auto/types-node.d.ts.map +1 -1
- package/_shims/auto/types-node.js.map +1 -1
- package/_shims/auto/types.d.ts +52 -52
- package/_shims/bun-runtime.d.ts.map +1 -1
- package/_shims/bun-runtime.js.map +1 -1
- package/_shims/bun-runtime.mjs.map +1 -1
- package/_shims/index.d.ts +36 -36
- package/_shims/index.js +5 -5
- package/_shims/index.mjs +4 -4
- package/_shims/node-runtime.d.ts.map +1 -1
- package/_shims/node-runtime.js.map +1 -1
- package/_shims/node-runtime.mjs.map +1 -1
- package/_shims/node-types.d.ts +27 -27
- package/_shims/registry.d.ts.map +1 -1
- package/_shims/registry.js.map +1 -1
- package/_shims/registry.mjs.map +1 -1
- package/_shims/web-runtime.d.ts.map +1 -1
- package/_shims/web-runtime.js.map +1 -1
- package/_shims/web-runtime.mjs.map +1 -1
- package/_shims/web-types.d.ts +52 -52
- package/core.d.ts.map +1 -1
- package/core.js +0 -1
- package/core.js.map +1 -1
- package/core.mjs +0 -1
- package/core.mjs.map +1 -1
- package/error.d.ts.map +1 -1
- package/error.js.map +1 -1
- package/error.mjs.map +1 -1
- package/index.d.mts +3 -0
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -1
- package/index.js +3 -0
- package/index.js.map +1 -1
- package/index.mjs +3 -0
- package/index.mjs.map +1 -1
- package/lib/generate-signature.js.map +1 -1
- package/lib/generate-signature.mjs.map +1 -1
- package/package.json +1 -1
- package/resource.d.ts.map +1 -1
- package/resource.js.map +1 -1
- package/resource.mjs.map +1 -1
- package/resources/conversation/chat.d.ts +9 -5
- package/resources/conversation/chat.d.ts.map +1 -1
- package/resources/conversation/chat.js +3 -3
- package/resources/conversation/chat.js.map +1 -1
- package/resources/conversation/chat.mjs +3 -3
- package/resources/conversation/chat.mjs.map +1 -1
- package/resources/conversation/conversation.d.ts.map +1 -1
- package/resources/conversation/conversation.js.map +1 -1
- package/resources/conversation/conversation.mjs.map +1 -1
- package/resources/conversation/email.d.ts +6 -2
- package/resources/conversation/email.d.ts.map +1 -1
- package/resources/conversation/email.js +1 -1
- package/resources/conversation/email.js.map +1 -1
- package/resources/conversation/email.mjs +1 -1
- package/resources/conversation/email.mjs.map +1 -1
- package/resources/conversation/index.d.ts.map +1 -1
- package/resources/conversation/index.js.map +1 -1
- package/resources/customer.d.ts +61 -0
- package/resources/customer.d.ts.map +1 -0
- package/resources/customer.js +12 -0
- package/resources/customer.js.map +1 -0
- package/resources/customer.mjs +8 -0
- package/resources/customer.mjs.map +1 -0
- package/resources/index.d.ts +1 -0
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +3 -1
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +1 -0
- package/resources/index.mjs.map +1 -1
- package/resources/ingest.d.ts.map +1 -1
- package/resources/ingest.js.map +1 -1
- package/resources/ingest.mjs.map +1 -1
- package/resources/token.d.ts.map +1 -1
- package/resources/token.js.map +1 -1
- package/resources/token.mjs.map +1 -1
- package/shims/node.d.ts.map +1 -1
- package/shims/node.js.map +1 -1
- package/shims/node.mjs.map +1 -1
- package/shims/web.d.ts.map +1 -1
- package/shims/web.js.map +1 -1
- package/shims/web.mjs.map +1 -1
- package/src/_shims/MultipartBody.ts +1 -1
- package/src/_shims/auto/runtime-bun.ts +1 -1
- package/src/_shims/auto/runtime-node.ts +1 -1
- package/src/_shims/auto/runtime.ts +1 -1
- package/src/_shims/auto/types-node.ts +1 -1
- package/src/_shims/auto/types.d.ts +52 -52
- package/src/_shims/bun-runtime.ts +6 -6
- package/src/_shims/index.d.ts +36 -36
- package/src/_shims/index.js +5 -5
- package/src/_shims/index.mjs +4 -4
- package/src/_shims/node-runtime.ts +32 -32
- package/src/_shims/node-types.d.ts +27 -27
- package/src/_shims/registry.ts +44 -44
- package/src/_shims/web-runtime.ts +16 -16
- package/src/_shims/web-types.d.ts +52 -52
- package/src/core.ts +372 -372
- package/src/error.ts +37 -37
- package/src/index.ts +74 -65
- package/src/lib/generate-signature.ts +3 -3
- package/src/resource.ts +3 -3
- package/src/resources/conversation/chat.ts +23 -18
- package/src/resources/conversation/conversation.ts +11 -11
- package/src/resources/conversation/email.ts +12 -7
- package/src/resources/conversation/index.ts +3 -3
- package/src/resources/customer.ts +81 -0
- package/src/resources/index.ts +4 -3
- package/src/resources/ingest.ts +7 -7
- package/src/resources/token.ts +9 -9
- package/src/shims/node.ts +24 -24
- package/src/shims/web.ts +24 -24
- package/src/uploads.ts +72 -72
- package/src/version.ts +1 -1
- package/uploads.d.ts.map +1 -1
- package/uploads.js.map +1 -1
- package/uploads.mjs.map +1 -1
- package/version.d.ts +1 -1
- package/version.d.ts.map +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/version.mjs +1 -1
- package/version.mjs.map +1 -1
package/src/core.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { VERSION } from './version'
|
|
1
|
+
import { VERSION } from './version';
|
|
2
2
|
import {
|
|
3
3
|
LorikeetError,
|
|
4
4
|
APIError,
|
|
5
5
|
APIConnectionError,
|
|
6
6
|
APIConnectionTimeoutError,
|
|
7
7
|
APIUserAbortError,
|
|
8
|
-
} from './error'
|
|
8
|
+
} from './error';
|
|
9
9
|
import {
|
|
10
10
|
kind as shimsKind,
|
|
11
11
|
type Readable,
|
|
@@ -16,53 +16,53 @@ import {
|
|
|
16
16
|
type RequestInit,
|
|
17
17
|
type Response,
|
|
18
18
|
type HeadersInit,
|
|
19
|
-
} from './_shims/index'
|
|
20
|
-
export { type Response }
|
|
21
|
-
import { BlobLike, isBlobLike, isMultipartBody } from './uploads'
|
|
19
|
+
} from './_shims/index';
|
|
20
|
+
export { type Response };
|
|
21
|
+
import { BlobLike, isBlobLike, isMultipartBody } from './uploads';
|
|
22
22
|
export {
|
|
23
23
|
maybeMultipartFormRequestOptions,
|
|
24
24
|
multipartFormRequestOptions,
|
|
25
25
|
createForm,
|
|
26
26
|
type Uploadable,
|
|
27
|
-
} from './uploads'
|
|
27
|
+
} from './uploads';
|
|
28
28
|
|
|
29
|
-
export type Fetch = (url: RequestInfo, init?: RequestInit) => Promise<Response
|
|
29
|
+
export type Fetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
30
30
|
|
|
31
|
-
type PromiseOrValue<T> = T | Promise<T
|
|
31
|
+
type PromiseOrValue<T> = T | Promise<T>;
|
|
32
32
|
|
|
33
33
|
type APIResponseProps = {
|
|
34
|
-
response: Response
|
|
35
|
-
options: FinalRequestOptions
|
|
36
|
-
controller: AbortController
|
|
37
|
-
}
|
|
34
|
+
response: Response;
|
|
35
|
+
options: FinalRequestOptions;
|
|
36
|
+
controller: AbortController;
|
|
37
|
+
};
|
|
38
38
|
|
|
39
39
|
async function defaultParseResponse<T>(props: APIResponseProps): Promise<T> {
|
|
40
|
-
const { response } = props
|
|
40
|
+
const { response } = props;
|
|
41
41
|
// fetch refuses to read the body when the status code is 204.
|
|
42
42
|
if (response.status === 204) {
|
|
43
|
-
return null as T
|
|
43
|
+
return null as T;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (props.options.__binaryResponse) {
|
|
47
|
-
return response as unknown as T
|
|
47
|
+
return response as unknown as T;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const contentType = response.headers.get('content-type')
|
|
50
|
+
const contentType = response.headers.get('content-type');
|
|
51
51
|
const isJSON =
|
|
52
|
-
contentType?.includes('application/json') || contentType?.includes('application/vnd.api+json')
|
|
52
|
+
contentType?.includes('application/json') || contentType?.includes('application/vnd.api+json');
|
|
53
53
|
if (isJSON) {
|
|
54
|
-
const json = await response.json()
|
|
54
|
+
const json = await response.json();
|
|
55
55
|
|
|
56
|
-
debug('response', response.status, response.url, response.headers, json)
|
|
56
|
+
debug('response', response.status, response.url, response.headers, json);
|
|
57
57
|
|
|
58
|
-
return json as T
|
|
58
|
+
return json as T;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const text = await response.text()
|
|
62
|
-
debug('response', response.status, response.url, response.headers, text)
|
|
61
|
+
const text = await response.text();
|
|
62
|
+
debug('response', response.status, response.url, response.headers, text);
|
|
63
63
|
|
|
64
64
|
// TODO handle blob, arraybuffer, other content types, etc.
|
|
65
|
-
return text as unknown as T
|
|
65
|
+
return text as unknown as T;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -70,7 +70,7 @@ async function defaultParseResponse<T>(props: APIResponseProps): Promise<T> {
|
|
|
70
70
|
* for interacting with the SDK.
|
|
71
71
|
*/
|
|
72
72
|
export class APIPromise<T> extends Promise<T> {
|
|
73
|
-
private parsedPromise: Promise<T> | undefined
|
|
73
|
+
private parsedPromise: Promise<T> | undefined;
|
|
74
74
|
|
|
75
75
|
constructor(
|
|
76
76
|
private responsePromise: Promise<APIResponseProps>,
|
|
@@ -80,14 +80,14 @@ export class APIPromise<T> extends Promise<T> {
|
|
|
80
80
|
// this is maybe a bit weird but this has to be a no-op to not implicitly
|
|
81
81
|
// parse the response body; instead .then, .catch, .finally are overridden
|
|
82
82
|
// to parse the response
|
|
83
|
-
resolve(null as any)
|
|
84
|
-
})
|
|
83
|
+
resolve(null as any);
|
|
84
|
+
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
_thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
|
|
88
88
|
return new APIPromise(this.responsePromise, async (props) =>
|
|
89
89
|
transform(await this.parseResponse(props), props),
|
|
90
|
-
)
|
|
90
|
+
);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
@@ -104,7 +104,7 @@ export class APIPromise<T> extends Promise<T> {
|
|
|
104
104
|
* - `import '@lorikeetai/node-sdk/shims/web'` (otherwise)
|
|
105
105
|
*/
|
|
106
106
|
asResponse(): Promise<Response> {
|
|
107
|
-
return this.responsePromise.then((p) => p.response)
|
|
107
|
+
return this.responsePromise.then((p) => p.response);
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
110
110
|
* Gets the parsed response data and the raw `Response` instance.
|
|
@@ -120,43 +120,43 @@ export class APIPromise<T> extends Promise<T> {
|
|
|
120
120
|
* - `import '@lorikeetai/node-sdk/shims/web'` (otherwise)
|
|
121
121
|
*/
|
|
122
122
|
async withResponse(): Promise<{ data: T; response: Response }> {
|
|
123
|
-
const [data, response] = await Promise.all([this.parse(), this.asResponse()])
|
|
124
|
-
return { data, response }
|
|
123
|
+
const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
|
|
124
|
+
return { data, response };
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
private parse(): Promise<T> {
|
|
128
128
|
if (!this.parsedPromise) {
|
|
129
|
-
this.parsedPromise = this.responsePromise.then(this.parseResponse)
|
|
129
|
+
this.parsedPromise = this.responsePromise.then(this.parseResponse);
|
|
130
130
|
}
|
|
131
|
-
return this.parsedPromise
|
|
131
|
+
return this.parsedPromise;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
override then<TResult1 = T, TResult2 = never>(
|
|
135
135
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
136
136
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
|
137
137
|
): Promise<TResult1 | TResult2> {
|
|
138
|
-
return this.parse().then(onfulfilled, onrejected)
|
|
138
|
+
return this.parse().then(onfulfilled, onrejected);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
override catch<TResult = never>(
|
|
142
142
|
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
|
|
143
143
|
): Promise<T | TResult> {
|
|
144
|
-
return this.parse().catch(onrejected)
|
|
144
|
+
return this.parse().catch(onrejected);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
override finally(onfinally?: (() => void) | undefined | null): Promise<T> {
|
|
148
|
-
return this.parse().finally(onfinally)
|
|
148
|
+
return this.parse().finally(onfinally);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
export abstract class APIClient {
|
|
153
|
-
baseURL: string
|
|
154
|
-
maxRetries: number
|
|
155
|
-
timeout: number
|
|
156
|
-
httpAgent: Agent | undefined
|
|
153
|
+
baseURL: string;
|
|
154
|
+
maxRetries: number;
|
|
155
|
+
timeout: number;
|
|
156
|
+
httpAgent: Agent | undefined;
|
|
157
157
|
|
|
158
|
-
private fetch: Fetch
|
|
159
|
-
protected idempotencyHeader?: string
|
|
158
|
+
private fetch: Fetch;
|
|
159
|
+
protected idempotencyHeader?: string;
|
|
160
160
|
|
|
161
161
|
constructor({
|
|
162
162
|
baseURL,
|
|
@@ -165,22 +165,22 @@ export abstract class APIClient {
|
|
|
165
165
|
httpAgent,
|
|
166
166
|
fetch: overridenFetch,
|
|
167
167
|
}: {
|
|
168
|
-
baseURL: string
|
|
169
|
-
maxRetries?: number | undefined
|
|
170
|
-
timeout: number | undefined
|
|
171
|
-
httpAgent: Agent | undefined
|
|
172
|
-
fetch: Fetch | undefined
|
|
168
|
+
baseURL: string;
|
|
169
|
+
maxRetries?: number | undefined;
|
|
170
|
+
timeout: number | undefined;
|
|
171
|
+
httpAgent: Agent | undefined;
|
|
172
|
+
fetch: Fetch | undefined;
|
|
173
173
|
}) {
|
|
174
|
-
this.baseURL = baseURL
|
|
175
|
-
this.maxRetries = validatePositiveInteger('maxRetries', maxRetries)
|
|
176
|
-
this.timeout = validatePositiveInteger('timeout', timeout)
|
|
177
|
-
this.httpAgent = httpAgent
|
|
174
|
+
this.baseURL = baseURL;
|
|
175
|
+
this.maxRetries = validatePositiveInteger('maxRetries', maxRetries);
|
|
176
|
+
this.timeout = validatePositiveInteger('timeout', timeout);
|
|
177
|
+
this.httpAgent = httpAgent;
|
|
178
178
|
|
|
179
|
-
this.fetch = overridenFetch ?? fetch
|
|
179
|
+
this.fetch = overridenFetch ?? fetch;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
protected authHeaders(opts: FinalRequestOptions): Headers {
|
|
183
|
-
return {}
|
|
183
|
+
return {};
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
@@ -198,10 +198,10 @@ export abstract class APIClient {
|
|
|
198
198
|
'User-Agent': this.getUserAgent(),
|
|
199
199
|
...getPlatformHeaders(),
|
|
200
200
|
...this.authHeaders(opts),
|
|
201
|
-
}
|
|
201
|
+
};
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
protected abstract defaultQuery(): DefaultQuery | undefined
|
|
204
|
+
protected abstract defaultQuery(): DefaultQuery | undefined;
|
|
205
205
|
|
|
206
206
|
/**
|
|
207
207
|
* Override this to add your own headers validation:
|
|
@@ -209,27 +209,27 @@ export abstract class APIClient {
|
|
|
209
209
|
protected validateHeaders(headers: Headers, customHeaders: Headers) {}
|
|
210
210
|
|
|
211
211
|
protected defaultIdempotencyKey(): string {
|
|
212
|
-
return `stainless-node-retry-${uuid4()}
|
|
212
|
+
return `stainless-node-retry-${uuid4()}`;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
get<Req, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp> {
|
|
216
|
-
return this.methodRequest('get', path, opts)
|
|
216
|
+
return this.methodRequest('get', path, opts);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
post<Req, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp> {
|
|
220
|
-
return this.methodRequest('post', path, opts)
|
|
220
|
+
return this.methodRequest('post', path, opts);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
patch<Req, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp> {
|
|
224
|
-
return this.methodRequest('patch', path, opts)
|
|
224
|
+
return this.methodRequest('patch', path, opts);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
put<Req, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp> {
|
|
228
|
-
return this.methodRequest('put', path, opts)
|
|
228
|
+
return this.methodRequest('put', path, opts);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
delete<Req, Rsp>(path: string, opts?: PromiseOrValue<RequestOptions<Req>>): APIPromise<Rsp> {
|
|
232
|
-
return this.methodRequest('delete', path, opts)
|
|
232
|
+
return this.methodRequest('delete', path, opts);
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
private methodRequest<Req, Rsp>(
|
|
@@ -244,10 +244,10 @@ export abstract class APIClient {
|
|
|
244
244
|
: opts?.body instanceof DataView ? opts.body
|
|
245
245
|
: opts?.body instanceof ArrayBuffer ? new DataView(opts.body)
|
|
246
246
|
: opts && ArrayBuffer.isView(opts?.body) ? new DataView(opts.body.buffer)
|
|
247
|
-
: opts?.body
|
|
248
|
-
return { method, path, ...opts, body }
|
|
247
|
+
: opts?.body;
|
|
248
|
+
return { method, path, ...opts, body };
|
|
249
249
|
}),
|
|
250
|
-
)
|
|
250
|
+
);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
getAPIList<Item, PageClass extends AbstractPage<Item> = AbstractPage<Item>>(
|
|
@@ -255,46 +255,46 @@ export abstract class APIClient {
|
|
|
255
255
|
Page: new (...args: any[]) => PageClass,
|
|
256
256
|
opts?: RequestOptions<any>,
|
|
257
257
|
): PagePromise<PageClass, Item> {
|
|
258
|
-
return this.requestAPIList(Page, { method: 'get', path, ...opts })
|
|
258
|
+
return this.requestAPIList(Page, { method: 'get', path, ...opts });
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
private calculateContentLength(body: unknown): string | null {
|
|
262
262
|
if (typeof body === 'string') {
|
|
263
263
|
if (typeof Buffer !== 'undefined') {
|
|
264
|
-
return Buffer.byteLength(body, 'utf8').toString()
|
|
264
|
+
return Buffer.byteLength(body, 'utf8').toString();
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
if (typeof TextEncoder !== 'undefined') {
|
|
268
|
-
const encoder = new TextEncoder()
|
|
269
|
-
const encoded = encoder.encode(body)
|
|
270
|
-
return encoded.length.toString()
|
|
268
|
+
const encoder = new TextEncoder();
|
|
269
|
+
const encoded = encoder.encode(body);
|
|
270
|
+
return encoded.length.toString();
|
|
271
271
|
}
|
|
272
272
|
} else if (ArrayBuffer.isView(body)) {
|
|
273
|
-
return body.byteLength.toString()
|
|
273
|
+
return body.byteLength.toString();
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
return null
|
|
276
|
+
return null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
buildRequest<Req>(
|
|
280
280
|
options: FinalRequestOptions<Req>,
|
|
281
281
|
{ retryCount = 0 }: { retryCount?: number } = {},
|
|
282
282
|
): { req: RequestInit; url: string; timeout: number } {
|
|
283
|
-
const { method, path, query, headers: headers = {} } = options
|
|
283
|
+
const { method, path, query, headers: headers = {} } = options;
|
|
284
284
|
|
|
285
285
|
const body =
|
|
286
286
|
ArrayBuffer.isView(options.body) || (options.__binaryRequest && typeof options.body === 'string') ?
|
|
287
287
|
options.body
|
|
288
288
|
: isMultipartBody(options.body) ? options.body.body
|
|
289
289
|
: options.body ? JSON.stringify(options.body, null, 2)
|
|
290
|
-
: null
|
|
291
|
-
const contentLength = this.calculateContentLength(body)
|
|
292
|
-
|
|
293
|
-
const url = this.buildURL(path!, query)
|
|
294
|
-
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout)
|
|
295
|
-
const timeout = options.timeout ?? this.timeout
|
|
296
|
-
const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url)
|
|
297
|
-
const minAgentTimeout = timeout + 1000
|
|
290
|
+
: null;
|
|
291
|
+
const contentLength = this.calculateContentLength(body);
|
|
292
|
+
|
|
293
|
+
const url = this.buildURL(path!, query);
|
|
294
|
+
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
|
|
295
|
+
const timeout = options.timeout ?? this.timeout;
|
|
296
|
+
const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
|
|
297
|
+
const minAgentTimeout = timeout + 1000;
|
|
298
298
|
if (
|
|
299
299
|
typeof (httpAgent as any)?.options?.timeout === 'number' &&
|
|
300
300
|
minAgentTimeout > ((httpAgent as any).options.timeout ?? 0)
|
|
@@ -303,15 +303,15 @@ export abstract class APIClient {
|
|
|
303
303
|
// This may seem strange, but leaking active sockets should be rare and not particularly problematic,
|
|
304
304
|
// and without mutating agent we would need to create more of them.
|
|
305
305
|
// This tradeoff optimizes for performance.
|
|
306
|
-
|
|
306
|
+
(httpAgent as any).options.timeout = minAgentTimeout;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
if (this.idempotencyHeader && method !== 'get') {
|
|
310
|
-
if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey()
|
|
311
|
-
headers[this.idempotencyHeader] = options.idempotencyKey
|
|
310
|
+
if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
|
|
311
|
+
headers[this.idempotencyHeader] = options.idempotencyKey;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount })
|
|
314
|
+
const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });
|
|
315
315
|
|
|
316
316
|
const req: RequestInit = {
|
|
317
317
|
method,
|
|
@@ -321,9 +321,9 @@ export abstract class APIClient {
|
|
|
321
321
|
// @ts-ignore node-fetch uses a custom AbortSignal type that is
|
|
322
322
|
// not compatible with standard web types
|
|
323
323
|
signal: options.signal ?? null,
|
|
324
|
-
}
|
|
324
|
+
};
|
|
325
325
|
|
|
326
|
-
return { req, url, timeout }
|
|
326
|
+
return { req, url, timeout };
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
private buildHeaders({
|
|
@@ -332,23 +332,23 @@ export abstract class APIClient {
|
|
|
332
332
|
contentLength,
|
|
333
333
|
retryCount,
|
|
334
334
|
}: {
|
|
335
|
-
options: FinalRequestOptions
|
|
336
|
-
headers: Record<string, string | null | undefined
|
|
337
|
-
contentLength: string | null | undefined
|
|
338
|
-
retryCount: number
|
|
335
|
+
options: FinalRequestOptions;
|
|
336
|
+
headers: Record<string, string | null | undefined>;
|
|
337
|
+
contentLength: string | null | undefined;
|
|
338
|
+
retryCount: number;
|
|
339
339
|
}): Record<string, string> {
|
|
340
|
-
const reqHeaders: Record<string, string> = {}
|
|
340
|
+
const reqHeaders: Record<string, string> = {};
|
|
341
341
|
if (contentLength) {
|
|
342
|
-
reqHeaders['content-length'] = contentLength
|
|
342
|
+
reqHeaders['content-length'] = contentLength;
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
const defaultHeaders = this.defaultHeaders(options)
|
|
346
|
-
applyHeadersMut(reqHeaders, defaultHeaders)
|
|
347
|
-
applyHeadersMut(reqHeaders, headers)
|
|
345
|
+
const defaultHeaders = this.defaultHeaders(options);
|
|
346
|
+
applyHeadersMut(reqHeaders, defaultHeaders);
|
|
347
|
+
applyHeadersMut(reqHeaders, headers);
|
|
348
348
|
|
|
349
349
|
// let builtin fetch set the Content-Type for multipart bodies
|
|
350
350
|
if (isMultipartBody(options.body) && shimsKind !== 'node') {
|
|
351
|
-
delete reqHeaders['content-type']
|
|
351
|
+
delete reqHeaders['content-type'];
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
// Don't set the retry count header if it was already set or removed through default headers or by the
|
|
@@ -358,12 +358,12 @@ export abstract class APIClient {
|
|
|
358
358
|
getHeader(defaultHeaders, 'x-stainless-retry-count') === undefined &&
|
|
359
359
|
getHeader(headers, 'x-stainless-retry-count') === undefined
|
|
360
360
|
) {
|
|
361
|
-
reqHeaders['x-stainless-retry-count'] = String(retryCount)
|
|
361
|
+
reqHeaders['x-stainless-retry-count'] = String(retryCount);
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
this.validateHeaders(reqHeaders, headers)
|
|
364
|
+
this.validateHeaders(reqHeaders, headers);
|
|
365
365
|
|
|
366
|
-
return reqHeaders
|
|
366
|
+
return reqHeaders;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/**
|
|
@@ -388,7 +388,7 @@ export abstract class APIClient {
|
|
|
388
388
|
: Symbol.iterator in headers ?
|
|
389
389
|
Object.fromEntries(Array.from(headers as Iterable<string[]>).map((header) => [...header]))
|
|
390
390
|
: { ...headers }
|
|
391
|
-
)
|
|
391
|
+
);
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
protected makeStatusError(
|
|
@@ -397,101 +397,101 @@ export abstract class APIClient {
|
|
|
397
397
|
message: string | undefined,
|
|
398
398
|
headers: Headers | undefined,
|
|
399
399
|
): APIError {
|
|
400
|
-
return APIError.generate(status, error, message, headers)
|
|
400
|
+
return APIError.generate(status, error, message, headers);
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
request<Req, Rsp>(
|
|
404
404
|
options: PromiseOrValue<FinalRequestOptions<Req>>,
|
|
405
405
|
remainingRetries: number | null = null,
|
|
406
406
|
): APIPromise<Rsp> {
|
|
407
|
-
return new APIPromise(this.makeRequest(options, remainingRetries))
|
|
407
|
+
return new APIPromise(this.makeRequest(options, remainingRetries));
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
private async makeRequest<Req>(
|
|
411
411
|
optionsInput: PromiseOrValue<FinalRequestOptions<Req>>,
|
|
412
412
|
retriesRemaining: number | null,
|
|
413
413
|
): Promise<APIResponseProps> {
|
|
414
|
-
const options = await optionsInput
|
|
415
|
-
const maxRetries = options.maxRetries ?? this.maxRetries
|
|
414
|
+
const options = await optionsInput;
|
|
415
|
+
const maxRetries = options.maxRetries ?? this.maxRetries;
|
|
416
416
|
if (retriesRemaining == null) {
|
|
417
|
-
retriesRemaining = maxRetries
|
|
417
|
+
retriesRemaining = maxRetries;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
await this.prepareOptions(options)
|
|
420
|
+
await this.prepareOptions(options);
|
|
421
421
|
|
|
422
|
-
const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining })
|
|
422
|
+
const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
|
|
423
423
|
|
|
424
|
-
await this.prepareRequest(req, { url, options })
|
|
424
|
+
await this.prepareRequest(req, { url, options });
|
|
425
425
|
|
|
426
|
-
debug('request', url, options, req.headers)
|
|
426
|
+
debug('request', url, options, req.headers);
|
|
427
427
|
|
|
428
428
|
if (options.signal?.aborted) {
|
|
429
|
-
throw new APIUserAbortError()
|
|
429
|
+
throw new APIUserAbortError();
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
const controller = new AbortController()
|
|
433
|
-
const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError)
|
|
432
|
+
const controller = new AbortController();
|
|
433
|
+
const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError);
|
|
434
434
|
|
|
435
435
|
if (response instanceof Error) {
|
|
436
436
|
if (options.signal?.aborted) {
|
|
437
|
-
throw new APIUserAbortError()
|
|
437
|
+
throw new APIUserAbortError();
|
|
438
438
|
}
|
|
439
439
|
if (retriesRemaining) {
|
|
440
|
-
return this.retryRequest(options, retriesRemaining)
|
|
440
|
+
return this.retryRequest(options, retriesRemaining);
|
|
441
441
|
}
|
|
442
442
|
if (response.name === 'AbortError') {
|
|
443
|
-
throw new APIConnectionTimeoutError()
|
|
443
|
+
throw new APIConnectionTimeoutError();
|
|
444
444
|
}
|
|
445
|
-
throw new APIConnectionError({ cause: response })
|
|
445
|
+
throw new APIConnectionError({ cause: response });
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
-
const responseHeaders = createResponseHeaders(response.headers)
|
|
448
|
+
const responseHeaders = createResponseHeaders(response.headers);
|
|
449
449
|
|
|
450
450
|
if (!response.ok) {
|
|
451
451
|
if (retriesRemaining && this.shouldRetry(response)) {
|
|
452
|
-
const retryMessage = `retrying, ${retriesRemaining} attempts remaining
|
|
453
|
-
debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders)
|
|
454
|
-
return this.retryRequest(options, retriesRemaining, responseHeaders)
|
|
452
|
+
const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
|
|
453
|
+
debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders);
|
|
454
|
+
return this.retryRequest(options, retriesRemaining, responseHeaders);
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
const errText = await response.text().catch((e: any) => castToError(e).message)
|
|
458
|
-
const errJSON = safeJSON(errText)
|
|
459
|
-
const errMessage = errJSON ? undefined : errText
|
|
460
|
-
const retryMessage = retriesRemaining ? `(error; no more retries left)` : `(error; not retryable)
|
|
457
|
+
const errText = await response.text().catch((e: any) => castToError(e).message);
|
|
458
|
+
const errJSON = safeJSON(errText);
|
|
459
|
+
const errMessage = errJSON ? undefined : errText;
|
|
460
|
+
const retryMessage = retriesRemaining ? `(error; no more retries left)` : `(error; not retryable)`;
|
|
461
461
|
|
|
462
|
-
debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders, errMessage)
|
|
462
|
+
debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders, errMessage);
|
|
463
463
|
|
|
464
|
-
const err = this.makeStatusError(response.status, errJSON, errMessage, responseHeaders)
|
|
465
|
-
throw err
|
|
464
|
+
const err = this.makeStatusError(response.status, errJSON, errMessage, responseHeaders);
|
|
465
|
+
throw err;
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
-
return { response, options, controller }
|
|
468
|
+
return { response, options, controller };
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
requestAPIList<Item = unknown, PageClass extends AbstractPage<Item> = AbstractPage<Item>>(
|
|
472
472
|
Page: new (...args: ConstructorParameters<typeof AbstractPage>) => PageClass,
|
|
473
473
|
options: FinalRequestOptions,
|
|
474
474
|
): PagePromise<PageClass, Item> {
|
|
475
|
-
const request = this.makeRequest(options, null)
|
|
476
|
-
return new PagePromise<PageClass, Item>(this, request, Page)
|
|
475
|
+
const request = this.makeRequest(options, null);
|
|
476
|
+
return new PagePromise<PageClass, Item>(this, request, Page);
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
buildURL<Req>(path: string, query: Req | null | undefined): string {
|
|
480
480
|
const url =
|
|
481
481
|
isAbsoluteURL(path) ?
|
|
482
482
|
new URL(path)
|
|
483
|
-
: new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path))
|
|
483
|
+
: new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
|
|
484
484
|
|
|
485
|
-
const defaultQuery = this.defaultQuery()
|
|
485
|
+
const defaultQuery = this.defaultQuery();
|
|
486
486
|
if (!isEmptyObj(defaultQuery)) {
|
|
487
|
-
query = { ...defaultQuery, ...query } as Req
|
|
487
|
+
query = { ...defaultQuery, ...query } as Req;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
if (typeof query === 'object' && query && !Array.isArray(query)) {
|
|
491
|
-
url.search = this.stringifyQuery(query as Record<string, unknown>)
|
|
491
|
+
url.search = this.stringifyQuery(query as Record<string, unknown>);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
return url.toString()
|
|
494
|
+
return url.toString();
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
protected stringifyQuery(query: Record<string, unknown>): string {
|
|
@@ -499,16 +499,16 @@ export abstract class APIClient {
|
|
|
499
499
|
.filter(([_, value]) => typeof value !== 'undefined')
|
|
500
500
|
.map(([key, value]) => {
|
|
501
501
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
502
|
-
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}
|
|
502
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
503
503
|
}
|
|
504
504
|
if (value === null) {
|
|
505
|
-
return `${encodeURIComponent(key)}
|
|
505
|
+
return `${encodeURIComponent(key)}=`;
|
|
506
506
|
}
|
|
507
507
|
throw new LorikeetError(
|
|
508
508
|
`Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
|
|
509
|
-
)
|
|
509
|
+
);
|
|
510
510
|
})
|
|
511
|
-
.join('&')
|
|
511
|
+
.join('&');
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
async fetchWithTimeout(
|
|
@@ -517,46 +517,46 @@ export abstract class APIClient {
|
|
|
517
517
|
ms: number,
|
|
518
518
|
controller: AbortController,
|
|
519
519
|
): Promise<Response> {
|
|
520
|
-
const { signal, ...options } = init || {}
|
|
521
|
-
if (signal) signal.addEventListener('abort', () => controller.abort())
|
|
520
|
+
const { signal, ...options } = init || {};
|
|
521
|
+
if (signal) signal.addEventListener('abort', () => controller.abort());
|
|
522
522
|
|
|
523
|
-
const timeout = setTimeout(() => controller.abort(), ms)
|
|
523
|
+
const timeout = setTimeout(() => controller.abort(), ms);
|
|
524
524
|
|
|
525
525
|
return (
|
|
526
526
|
this.getRequestClient()
|
|
527
527
|
// use undefined this binding; fetch errors if bound to something else in browser/cloudflare
|
|
528
528
|
.fetch.call(undefined, url, { signal: controller.signal as any, ...options })
|
|
529
529
|
.finally(() => {
|
|
530
|
-
clearTimeout(timeout)
|
|
530
|
+
clearTimeout(timeout);
|
|
531
531
|
})
|
|
532
|
-
)
|
|
532
|
+
);
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
protected getRequestClient(): RequestClient {
|
|
536
|
-
return { fetch: this.fetch }
|
|
536
|
+
return { fetch: this.fetch };
|
|
537
537
|
}
|
|
538
538
|
|
|
539
539
|
private shouldRetry(response: Response): boolean {
|
|
540
540
|
// Note this is not a standard header.
|
|
541
|
-
const shouldRetryHeader = response.headers.get('x-should-retry')
|
|
541
|
+
const shouldRetryHeader = response.headers.get('x-should-retry');
|
|
542
542
|
|
|
543
543
|
// If the server explicitly says whether or not to retry, obey.
|
|
544
|
-
if (shouldRetryHeader === 'true') return true
|
|
545
|
-
if (shouldRetryHeader === 'false') return false
|
|
544
|
+
if (shouldRetryHeader === 'true') return true;
|
|
545
|
+
if (shouldRetryHeader === 'false') return false;
|
|
546
546
|
|
|
547
547
|
// Retry on request timeouts.
|
|
548
|
-
if (response.status === 408) return true
|
|
548
|
+
if (response.status === 408) return true;
|
|
549
549
|
|
|
550
550
|
// Retry on lock timeouts.
|
|
551
|
-
if (response.status === 409) return true
|
|
551
|
+
if (response.status === 409) return true;
|
|
552
552
|
|
|
553
553
|
// Retry on rate limits.
|
|
554
|
-
if (response.status === 429) return true
|
|
554
|
+
if (response.status === 429) return true;
|
|
555
555
|
|
|
556
556
|
// Retry internal errors.
|
|
557
|
-
if (response.status >= 500) return true
|
|
557
|
+
if (response.status >= 500) return true;
|
|
558
558
|
|
|
559
|
-
return false
|
|
559
|
+
return false;
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
private async retryRequest(
|
|
@@ -564,124 +564,124 @@ export abstract class APIClient {
|
|
|
564
564
|
retriesRemaining: number,
|
|
565
565
|
responseHeaders?: Headers | undefined,
|
|
566
566
|
): Promise<APIResponseProps> {
|
|
567
|
-
let timeoutMillis: number | undefined
|
|
567
|
+
let timeoutMillis: number | undefined;
|
|
568
568
|
|
|
569
569
|
// Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.
|
|
570
|
-
const retryAfterMillisHeader = responseHeaders?.['retry-after-ms']
|
|
570
|
+
const retryAfterMillisHeader = responseHeaders?.['retry-after-ms'];
|
|
571
571
|
if (retryAfterMillisHeader) {
|
|
572
|
-
const timeoutMs = parseFloat(retryAfterMillisHeader)
|
|
572
|
+
const timeoutMs = parseFloat(retryAfterMillisHeader);
|
|
573
573
|
if (!Number.isNaN(timeoutMs)) {
|
|
574
|
-
timeoutMillis = timeoutMs
|
|
574
|
+
timeoutMillis = timeoutMs;
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
// About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
|
579
|
-
const retryAfterHeader = responseHeaders?.['retry-after']
|
|
579
|
+
const retryAfterHeader = responseHeaders?.['retry-after'];
|
|
580
580
|
if (retryAfterHeader && !timeoutMillis) {
|
|
581
|
-
const timeoutSeconds = parseFloat(retryAfterHeader)
|
|
581
|
+
const timeoutSeconds = parseFloat(retryAfterHeader);
|
|
582
582
|
if (!Number.isNaN(timeoutSeconds)) {
|
|
583
|
-
timeoutMillis = timeoutSeconds * 1000
|
|
583
|
+
timeoutMillis = timeoutSeconds * 1000;
|
|
584
584
|
} else {
|
|
585
|
-
timeoutMillis = Date.parse(retryAfterHeader) - Date.now()
|
|
585
|
+
timeoutMillis = Date.parse(retryAfterHeader) - Date.now();
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
// If the API asks us to wait a certain amount of time (and it's a reasonable amount),
|
|
590
590
|
// just do what it says, but otherwise calculate a default
|
|
591
591
|
if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) {
|
|
592
|
-
const maxRetries = options.maxRetries ?? this.maxRetries
|
|
593
|
-
timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries)
|
|
592
|
+
const maxRetries = options.maxRetries ?? this.maxRetries;
|
|
593
|
+
timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
|
|
594
594
|
}
|
|
595
|
-
await sleep(timeoutMillis)
|
|
595
|
+
await sleep(timeoutMillis);
|
|
596
596
|
|
|
597
|
-
return this.makeRequest(options, retriesRemaining - 1)
|
|
597
|
+
return this.makeRequest(options, retriesRemaining - 1);
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number {
|
|
601
|
-
const initialRetryDelay = 0.5
|
|
602
|
-
const maxRetryDelay = 8.0
|
|
601
|
+
const initialRetryDelay = 0.5;
|
|
602
|
+
const maxRetryDelay = 8.0;
|
|
603
603
|
|
|
604
|
-
const numRetries = maxRetries - retriesRemaining
|
|
604
|
+
const numRetries = maxRetries - retriesRemaining;
|
|
605
605
|
|
|
606
606
|
// Apply exponential backoff, but not more than the max.
|
|
607
|
-
const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay)
|
|
607
|
+
const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay);
|
|
608
608
|
|
|
609
609
|
// Apply some jitter, take up to at most 25 percent of the retry time.
|
|
610
|
-
const jitter = 1 - Math.random() * 0.25
|
|
610
|
+
const jitter = 1 - Math.random() * 0.25;
|
|
611
611
|
|
|
612
|
-
return sleepSeconds * jitter * 1000
|
|
612
|
+
return sleepSeconds * jitter * 1000;
|
|
613
613
|
}
|
|
614
614
|
|
|
615
615
|
private getUserAgent(): string {
|
|
616
|
-
return `${this.constructor.name}/JS ${VERSION}
|
|
616
|
+
return `${this.constructor.name}/JS ${VERSION}`;
|
|
617
617
|
}
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
-
export type PageInfo = { url: URL } | { params: Record<string, unknown> | null }
|
|
620
|
+
export type PageInfo = { url: URL } | { params: Record<string, unknown> | null };
|
|
621
621
|
|
|
622
622
|
export abstract class AbstractPage<Item> implements AsyncIterable<Item> {
|
|
623
|
-
#client: APIClient
|
|
624
|
-
protected options: FinalRequestOptions
|
|
623
|
+
#client: APIClient;
|
|
624
|
+
protected options: FinalRequestOptions;
|
|
625
625
|
|
|
626
|
-
protected response: Response
|
|
627
|
-
protected body: unknown
|
|
626
|
+
protected response: Response;
|
|
627
|
+
protected body: unknown;
|
|
628
628
|
|
|
629
629
|
constructor(client: APIClient, response: Response, body: unknown, options: FinalRequestOptions) {
|
|
630
|
-
this.#client = client
|
|
631
|
-
this.options = options
|
|
632
|
-
this.response = response
|
|
633
|
-
this.body = body
|
|
630
|
+
this.#client = client;
|
|
631
|
+
this.options = options;
|
|
632
|
+
this.response = response;
|
|
633
|
+
this.body = body;
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
/**
|
|
637
637
|
* @deprecated Use nextPageInfo instead
|
|
638
638
|
*/
|
|
639
|
-
abstract nextPageParams(): Partial<Record<string, unknown>> | null
|
|
640
|
-
abstract nextPageInfo(): PageInfo | null
|
|
639
|
+
abstract nextPageParams(): Partial<Record<string, unknown>> | null;
|
|
640
|
+
abstract nextPageInfo(): PageInfo | null;
|
|
641
641
|
|
|
642
|
-
abstract getPaginatedItems(): Item[]
|
|
642
|
+
abstract getPaginatedItems(): Item[];
|
|
643
643
|
|
|
644
644
|
hasNextPage(): boolean {
|
|
645
|
-
const items = this.getPaginatedItems()
|
|
646
|
-
if (!items.length) return false
|
|
647
|
-
return this.nextPageInfo() != null
|
|
645
|
+
const items = this.getPaginatedItems();
|
|
646
|
+
if (!items.length) return false;
|
|
647
|
+
return this.nextPageInfo() != null;
|
|
648
648
|
}
|
|
649
649
|
|
|
650
650
|
async getNextPage(): Promise<this> {
|
|
651
|
-
const nextInfo = this.nextPageInfo()
|
|
651
|
+
const nextInfo = this.nextPageInfo();
|
|
652
652
|
if (!nextInfo) {
|
|
653
653
|
throw new LorikeetError(
|
|
654
654
|
'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.',
|
|
655
|
-
)
|
|
655
|
+
);
|
|
656
656
|
}
|
|
657
|
-
const nextOptions = { ...this.options }
|
|
657
|
+
const nextOptions = { ...this.options };
|
|
658
658
|
if ('params' in nextInfo && typeof nextOptions.query === 'object') {
|
|
659
|
-
nextOptions.query = { ...nextOptions.query, ...nextInfo.params }
|
|
659
|
+
nextOptions.query = { ...nextOptions.query, ...nextInfo.params };
|
|
660
660
|
} else if ('url' in nextInfo) {
|
|
661
|
-
const params = [...Object.entries(nextOptions.query || {}), ...nextInfo.url.searchParams.entries()]
|
|
661
|
+
const params = [...Object.entries(nextOptions.query || {}), ...nextInfo.url.searchParams.entries()];
|
|
662
662
|
for (const [key, value] of params) {
|
|
663
|
-
nextInfo.url.searchParams.set(key, value as any)
|
|
663
|
+
nextInfo.url.searchParams.set(key, value as any);
|
|
664
664
|
}
|
|
665
|
-
nextOptions.query = undefined
|
|
666
|
-
nextOptions.path = nextInfo.url.toString()
|
|
665
|
+
nextOptions.query = undefined;
|
|
666
|
+
nextOptions.path = nextInfo.url.toString();
|
|
667
667
|
}
|
|
668
|
-
return await this.#client.requestAPIList(this.constructor as any, nextOptions)
|
|
668
|
+
return await this.#client.requestAPIList(this.constructor as any, nextOptions);
|
|
669
669
|
}
|
|
670
670
|
|
|
671
671
|
async *iterPages(): AsyncGenerator<this> {
|
|
672
672
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
673
|
-
let page: this = this
|
|
674
|
-
yield page
|
|
673
|
+
let page: this = this;
|
|
674
|
+
yield page;
|
|
675
675
|
while (page.hasNextPage()) {
|
|
676
|
-
page = await page.getNextPage()
|
|
677
|
-
yield page
|
|
676
|
+
page = await page.getNextPage();
|
|
677
|
+
yield page;
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
|
|
681
681
|
async *[Symbol.asyncIterator](): AsyncGenerator<Item> {
|
|
682
682
|
for await (const page of this.iterPages()) {
|
|
683
683
|
for (const item of page.getPaginatedItems()) {
|
|
684
|
-
yield item
|
|
684
|
+
yield item;
|
|
685
685
|
}
|
|
686
686
|
}
|
|
687
687
|
}
|
|
@@ -711,7 +711,7 @@ export class PagePromise<
|
|
|
711
711
|
super(
|
|
712
712
|
request,
|
|
713
713
|
async (props) => new Page(client, props.response, await defaultParseResponse(props), props.options),
|
|
714
|
-
)
|
|
714
|
+
);
|
|
715
715
|
}
|
|
716
716
|
|
|
717
717
|
/**
|
|
@@ -722,9 +722,9 @@ export class PagePromise<
|
|
|
722
722
|
* }
|
|
723
723
|
*/
|
|
724
724
|
async *[Symbol.asyncIterator](): AsyncGenerator<Item> {
|
|
725
|
-
const page = await this
|
|
725
|
+
const page = await this;
|
|
726
726
|
for await (const item of page) {
|
|
727
|
-
yield item
|
|
727
|
+
yield item;
|
|
728
728
|
}
|
|
729
729
|
}
|
|
730
730
|
}
|
|
@@ -739,39 +739,39 @@ export const createResponseHeaders = (
|
|
|
739
739
|
),
|
|
740
740
|
{
|
|
741
741
|
get(target, name) {
|
|
742
|
-
const key = name.toString()
|
|
743
|
-
return target[key.toLowerCase()] || target[key]
|
|
742
|
+
const key = name.toString();
|
|
743
|
+
return target[key.toLowerCase()] || target[key];
|
|
744
744
|
},
|
|
745
745
|
},
|
|
746
|
-
)
|
|
747
|
-
}
|
|
746
|
+
);
|
|
747
|
+
};
|
|
748
748
|
|
|
749
|
-
type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
749
|
+
type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
750
750
|
|
|
751
|
-
export type RequestClient = { fetch: Fetch }
|
|
752
|
-
export type Headers = Record<string, string | null | undefined
|
|
753
|
-
export type DefaultQuery = Record<string, string | undefined
|
|
754
|
-
export type KeysEnum<T> = { [P in keyof Required<T>]: true }
|
|
751
|
+
export type RequestClient = { fetch: Fetch };
|
|
752
|
+
export type Headers = Record<string, string | null | undefined>;
|
|
753
|
+
export type DefaultQuery = Record<string, string | undefined>;
|
|
754
|
+
export type KeysEnum<T> = { [P in keyof Required<T>]: true };
|
|
755
755
|
|
|
756
756
|
export type RequestOptions<
|
|
757
757
|
Req = unknown | Record<string, unknown> | Readable | BlobLike | ArrayBufferView | ArrayBuffer,
|
|
758
758
|
> = {
|
|
759
|
-
method?: HTTPMethod
|
|
760
|
-
path?: string
|
|
761
|
-
query?: Req | undefined
|
|
762
|
-
body?: Req | null | undefined
|
|
763
|
-
headers?: Headers | undefined
|
|
764
|
-
|
|
765
|
-
maxRetries?: number
|
|
766
|
-
stream?: boolean | undefined
|
|
767
|
-
timeout?: number
|
|
768
|
-
httpAgent?: Agent
|
|
769
|
-
signal?: AbortSignal | undefined | null
|
|
770
|
-
idempotencyKey?: string
|
|
771
|
-
|
|
772
|
-
__binaryRequest?: boolean | undefined
|
|
773
|
-
__binaryResponse?: boolean | undefined
|
|
774
|
-
}
|
|
759
|
+
method?: HTTPMethod;
|
|
760
|
+
path?: string;
|
|
761
|
+
query?: Req | undefined;
|
|
762
|
+
body?: Req | null | undefined;
|
|
763
|
+
headers?: Headers | undefined;
|
|
764
|
+
|
|
765
|
+
maxRetries?: number;
|
|
766
|
+
stream?: boolean | undefined;
|
|
767
|
+
timeout?: number;
|
|
768
|
+
httpAgent?: Agent;
|
|
769
|
+
signal?: AbortSignal | undefined | null;
|
|
770
|
+
idempotencyKey?: string;
|
|
771
|
+
|
|
772
|
+
__binaryRequest?: boolean | undefined;
|
|
773
|
+
__binaryResponse?: boolean | undefined;
|
|
774
|
+
};
|
|
775
775
|
|
|
776
776
|
// This is required so that we can determine if a given object matches the RequestOptions
|
|
777
777
|
// type at runtime. While this requires duplication, it is enforced by the TypeScript
|
|
@@ -792,7 +792,7 @@ const requestOptionsKeys: KeysEnum<RequestOptions> = {
|
|
|
792
792
|
|
|
793
793
|
__binaryRequest: true,
|
|
794
794
|
__binaryResponse: true,
|
|
795
|
-
}
|
|
795
|
+
};
|
|
796
796
|
|
|
797
797
|
export const isRequestOptions = (obj: unknown): obj is RequestOptions => {
|
|
798
798
|
return (
|
|
@@ -800,18 +800,18 @@ export const isRequestOptions = (obj: unknown): obj is RequestOptions => {
|
|
|
800
800
|
obj !== null &&
|
|
801
801
|
!isEmptyObj(obj) &&
|
|
802
802
|
Object.keys(obj).every((k) => hasOwn(requestOptionsKeys, k))
|
|
803
|
-
)
|
|
804
|
-
}
|
|
803
|
+
);
|
|
804
|
+
};
|
|
805
805
|
|
|
806
806
|
export type FinalRequestOptions<Req = unknown | Record<string, unknown> | Readable | DataView> =
|
|
807
807
|
RequestOptions<Req> & {
|
|
808
|
-
method: HTTPMethod
|
|
809
|
-
path: string
|
|
810
|
-
}
|
|
808
|
+
method: HTTPMethod;
|
|
809
|
+
path: string;
|
|
810
|
+
};
|
|
811
811
|
|
|
812
|
-
declare const Deno: any
|
|
813
|
-
declare const EdgeRuntime: any
|
|
814
|
-
type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown'
|
|
812
|
+
declare const Deno: any;
|
|
813
|
+
declare const EdgeRuntime: any;
|
|
814
|
+
type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown';
|
|
815
815
|
type PlatformName =
|
|
816
816
|
| 'MacOS'
|
|
817
817
|
| 'Linux'
|
|
@@ -821,16 +821,16 @@ type PlatformName =
|
|
|
821
821
|
| 'iOS'
|
|
822
822
|
| 'Android'
|
|
823
823
|
| `Other:${string}`
|
|
824
|
-
| 'Unknown'
|
|
825
|
-
type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari'
|
|
824
|
+
| 'Unknown';
|
|
825
|
+
type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari';
|
|
826
826
|
type PlatformProperties = {
|
|
827
|
-
'X-Stainless-Lang': 'js'
|
|
828
|
-
'X-Stainless-Package-Version': string
|
|
829
|
-
'X-Stainless-OS': PlatformName
|
|
830
|
-
'X-Stainless-Arch': Arch
|
|
831
|
-
'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown'
|
|
832
|
-
'X-Stainless-Runtime-Version': string
|
|
833
|
-
}
|
|
827
|
+
'X-Stainless-Lang': 'js';
|
|
828
|
+
'X-Stainless-Package-Version': string;
|
|
829
|
+
'X-Stainless-OS': PlatformName;
|
|
830
|
+
'X-Stainless-Arch': Arch;
|
|
831
|
+
'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown';
|
|
832
|
+
'X-Stainless-Runtime-Version': string;
|
|
833
|
+
};
|
|
834
834
|
const getPlatformProperties = (): PlatformProperties => {
|
|
835
835
|
if (typeof Deno !== 'undefined' && Deno.build != null) {
|
|
836
836
|
return {
|
|
@@ -841,7 +841,7 @@ const getPlatformProperties = (): PlatformProperties => {
|
|
|
841
841
|
'X-Stainless-Runtime': 'deno',
|
|
842
842
|
'X-Stainless-Runtime-Version':
|
|
843
843
|
typeof Deno.version === 'string' ? Deno.version : (Deno.version?.deno ?? 'unknown'),
|
|
844
|
-
}
|
|
844
|
+
};
|
|
845
845
|
}
|
|
846
846
|
if (typeof EdgeRuntime !== 'undefined') {
|
|
847
847
|
return {
|
|
@@ -851,7 +851,7 @@ const getPlatformProperties = (): PlatformProperties => {
|
|
|
851
851
|
'X-Stainless-Arch': `other:${EdgeRuntime}`,
|
|
852
852
|
'X-Stainless-Runtime': 'edge',
|
|
853
853
|
'X-Stainless-Runtime-Version': process.version,
|
|
854
|
-
}
|
|
854
|
+
};
|
|
855
855
|
}
|
|
856
856
|
// Check if Node.js
|
|
857
857
|
if (Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]') {
|
|
@@ -862,10 +862,10 @@ const getPlatformProperties = (): PlatformProperties => {
|
|
|
862
862
|
'X-Stainless-Arch': normalizeArch(process.arch),
|
|
863
863
|
'X-Stainless-Runtime': 'node',
|
|
864
864
|
'X-Stainless-Runtime-Version': process.version,
|
|
865
|
-
}
|
|
865
|
+
};
|
|
866
866
|
}
|
|
867
867
|
|
|
868
|
-
const browserInfo = getBrowserInfo()
|
|
868
|
+
const browserInfo = getBrowserInfo();
|
|
869
869
|
if (browserInfo) {
|
|
870
870
|
return {
|
|
871
871
|
'X-Stainless-Lang': 'js',
|
|
@@ -874,7 +874,7 @@ const getPlatformProperties = (): PlatformProperties => {
|
|
|
874
874
|
'X-Stainless-Arch': 'unknown',
|
|
875
875
|
'X-Stainless-Runtime': `browser:${browserInfo.browser}`,
|
|
876
876
|
'X-Stainless-Runtime-Version': browserInfo.version,
|
|
877
|
-
}
|
|
877
|
+
};
|
|
878
878
|
}
|
|
879
879
|
|
|
880
880
|
// TODO add support for Cloudflare workers, etc.
|
|
@@ -885,20 +885,20 @@ const getPlatformProperties = (): PlatformProperties => {
|
|
|
885
885
|
'X-Stainless-Arch': 'unknown',
|
|
886
886
|
'X-Stainless-Runtime': 'unknown',
|
|
887
887
|
'X-Stainless-Runtime-Version': 'unknown',
|
|
888
|
-
}
|
|
889
|
-
}
|
|
888
|
+
};
|
|
889
|
+
};
|
|
890
890
|
|
|
891
891
|
type BrowserInfo = {
|
|
892
|
-
browser: Browser
|
|
893
|
-
version: string
|
|
894
|
-
}
|
|
892
|
+
browser: Browser;
|
|
893
|
+
version: string;
|
|
894
|
+
};
|
|
895
895
|
|
|
896
|
-
declare const navigator: { userAgent: string } | undefined
|
|
896
|
+
declare const navigator: { userAgent: string } | undefined;
|
|
897
897
|
|
|
898
898
|
// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts
|
|
899
899
|
function getBrowserInfo(): BrowserInfo | null {
|
|
900
900
|
if (typeof navigator === 'undefined' || !navigator) {
|
|
901
|
-
return null
|
|
901
|
+
return null;
|
|
902
902
|
}
|
|
903
903
|
|
|
904
904
|
// NOTE: The order matters here!
|
|
@@ -909,21 +909,21 @@ function getBrowserInfo(): BrowserInfo | null {
|
|
|
909
909
|
{ key: 'chrome' as const, pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
910
910
|
{ key: 'firefox' as const, pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
911
911
|
{ key: 'safari' as const, pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ },
|
|
912
|
-
]
|
|
912
|
+
];
|
|
913
913
|
|
|
914
914
|
// Find the FIRST matching browser
|
|
915
915
|
for (const { key, pattern } of browserPatterns) {
|
|
916
|
-
const match = pattern.exec(navigator.userAgent)
|
|
916
|
+
const match = pattern.exec(navigator.userAgent);
|
|
917
917
|
if (match) {
|
|
918
|
-
const major = match[1] || 0
|
|
919
|
-
const minor = match[2] || 0
|
|
920
|
-
const patch = match[3] || 0
|
|
918
|
+
const major = match[1] || 0;
|
|
919
|
+
const minor = match[2] || 0;
|
|
920
|
+
const patch = match[3] || 0;
|
|
921
921
|
|
|
922
|
-
return { browser: key, version: `${major}.${minor}.${patch}` }
|
|
922
|
+
return { browser: key, version: `${major}.${minor}.${patch}` };
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
925
|
|
|
926
|
-
return null
|
|
926
|
+
return null;
|
|
927
927
|
}
|
|
928
928
|
|
|
929
929
|
const normalizeArch = (arch: string): Arch => {
|
|
@@ -931,13 +931,13 @@ const normalizeArch = (arch: string): Arch => {
|
|
|
931
931
|
// - https://nodejs.org/api/process.html#processarch
|
|
932
932
|
// Deno docs:
|
|
933
933
|
// - https://doc.deno.land/deno/stable/~/Deno.build
|
|
934
|
-
if (arch === 'x32') return 'x32'
|
|
935
|
-
if (arch === 'x86_64' || arch === 'x64') return 'x64'
|
|
936
|
-
if (arch === 'arm') return 'arm'
|
|
937
|
-
if (arch === 'aarch64' || arch === 'arm64') return 'arm64'
|
|
938
|
-
if (arch) return `other:${arch}
|
|
939
|
-
return 'unknown'
|
|
940
|
-
}
|
|
934
|
+
if (arch === 'x32') return 'x32';
|
|
935
|
+
if (arch === 'x86_64' || arch === 'x64') return 'x64';
|
|
936
|
+
if (arch === 'arm') return 'arm';
|
|
937
|
+
if (arch === 'aarch64' || arch === 'arm64') return 'arm64';
|
|
938
|
+
if (arch) return `other:${arch}`;
|
|
939
|
+
return 'unknown';
|
|
940
|
+
};
|
|
941
941
|
|
|
942
942
|
const normalizePlatform = (platform: string): PlatformName => {
|
|
943
943
|
// Node platforms:
|
|
@@ -946,68 +946,68 @@ const normalizePlatform = (platform: string): PlatformName => {
|
|
|
946
946
|
// - https://doc.deno.land/deno/stable/~/Deno.build
|
|
947
947
|
// - https://github.com/denoland/deno/issues/14799
|
|
948
948
|
|
|
949
|
-
platform = platform.toLowerCase()
|
|
949
|
+
platform = platform.toLowerCase();
|
|
950
950
|
|
|
951
951
|
// NOTE: this iOS check is untested and may not work
|
|
952
952
|
// Node does not work natively on IOS, there is a fork at
|
|
953
953
|
// https://github.com/nodejs-mobile/nodejs-mobile
|
|
954
954
|
// however it is unknown at the time of writing how to detect if it is running
|
|
955
|
-
if (platform.includes('ios')) return 'iOS'
|
|
956
|
-
if (platform === 'android') return 'Android'
|
|
957
|
-
if (platform === 'darwin') return 'MacOS'
|
|
958
|
-
if (platform === 'win32') return 'Windows'
|
|
959
|
-
if (platform === 'freebsd') return 'FreeBSD'
|
|
960
|
-
if (platform === 'openbsd') return 'OpenBSD'
|
|
961
|
-
if (platform === 'linux') return 'Linux'
|
|
962
|
-
if (platform) return `Other:${platform}
|
|
963
|
-
return 'Unknown'
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
let _platformHeaders: PlatformProperties
|
|
955
|
+
if (platform.includes('ios')) return 'iOS';
|
|
956
|
+
if (platform === 'android') return 'Android';
|
|
957
|
+
if (platform === 'darwin') return 'MacOS';
|
|
958
|
+
if (platform === 'win32') return 'Windows';
|
|
959
|
+
if (platform === 'freebsd') return 'FreeBSD';
|
|
960
|
+
if (platform === 'openbsd') return 'OpenBSD';
|
|
961
|
+
if (platform === 'linux') return 'Linux';
|
|
962
|
+
if (platform) return `Other:${platform}`;
|
|
963
|
+
return 'Unknown';
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
let _platformHeaders: PlatformProperties;
|
|
967
967
|
const getPlatformHeaders = () => {
|
|
968
|
-
return (_platformHeaders ??= getPlatformProperties())
|
|
969
|
-
}
|
|
968
|
+
return (_platformHeaders ??= getPlatformProperties());
|
|
969
|
+
};
|
|
970
970
|
|
|
971
971
|
export const safeJSON = (text: string) => {
|
|
972
972
|
try {
|
|
973
|
-
return JSON.parse(text)
|
|
973
|
+
return JSON.parse(text);
|
|
974
974
|
} catch (err) {
|
|
975
|
-
return undefined
|
|
975
|
+
return undefined;
|
|
976
976
|
}
|
|
977
|
-
}
|
|
977
|
+
};
|
|
978
978
|
|
|
979
979
|
// https://stackoverflow.com/a/19709846
|
|
980
|
-
const startsWithSchemeRegexp = new RegExp('^(?:[a-z]+:)?//', 'i')
|
|
980
|
+
const startsWithSchemeRegexp = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
981
981
|
const isAbsoluteURL = (url: string): boolean => {
|
|
982
|
-
return startsWithSchemeRegexp.test(url)
|
|
983
|
-
}
|
|
982
|
+
return startsWithSchemeRegexp.test(url);
|
|
983
|
+
};
|
|
984
984
|
|
|
985
|
-
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
985
|
+
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
986
986
|
|
|
987
987
|
const validatePositiveInteger = (name: string, n: unknown): number => {
|
|
988
988
|
if (typeof n !== 'number' || !Number.isInteger(n)) {
|
|
989
|
-
throw new LorikeetError(`${name} must be an integer`)
|
|
989
|
+
throw new LorikeetError(`${name} must be an integer`);
|
|
990
990
|
}
|
|
991
991
|
if (n < 0) {
|
|
992
|
-
throw new LorikeetError(`${name} must be a positive integer`)
|
|
992
|
+
throw new LorikeetError(`${name} must be a positive integer`);
|
|
993
993
|
}
|
|
994
|
-
return n
|
|
995
|
-
}
|
|
994
|
+
return n;
|
|
995
|
+
};
|
|
996
996
|
|
|
997
997
|
export const castToError = (err: any): Error => {
|
|
998
|
-
if (err instanceof Error) return err
|
|
998
|
+
if (err instanceof Error) return err;
|
|
999
999
|
if (typeof err === 'object' && err !== null) {
|
|
1000
1000
|
try {
|
|
1001
|
-
return new Error(JSON.stringify(err))
|
|
1001
|
+
return new Error(JSON.stringify(err));
|
|
1002
1002
|
} catch {}
|
|
1003
1003
|
}
|
|
1004
|
-
return new Error(err)
|
|
1005
|
-
}
|
|
1004
|
+
return new Error(err);
|
|
1005
|
+
};
|
|
1006
1006
|
|
|
1007
1007
|
export const ensurePresent = <T>(value: T | null | undefined): T => {
|
|
1008
|
-
if (value == null) throw new LorikeetError(`Expected a value to be given but received ${value} instead.`)
|
|
1009
|
-
return value
|
|
1010
|
-
}
|
|
1008
|
+
if (value == null) throw new LorikeetError(`Expected a value to be given but received ${value} instead.`);
|
|
1009
|
+
return value;
|
|
1010
|
+
};
|
|
1011
1011
|
|
|
1012
1012
|
/**
|
|
1013
1013
|
* Read an environment variable.
|
|
@@ -1018,65 +1018,65 @@ export const ensurePresent = <T>(value: T | null | undefined): T => {
|
|
|
1018
1018
|
*/
|
|
1019
1019
|
export const readEnv = (env: string): string | undefined => {
|
|
1020
1020
|
if (typeof process !== 'undefined') {
|
|
1021
|
-
return process.env?.[env]?.trim() ?? undefined
|
|
1021
|
+
return process.env?.[env]?.trim() ?? undefined;
|
|
1022
1022
|
}
|
|
1023
1023
|
if (typeof Deno !== 'undefined') {
|
|
1024
|
-
return Deno.env?.get?.(env)?.trim()
|
|
1024
|
+
return Deno.env?.get?.(env)?.trim();
|
|
1025
1025
|
}
|
|
1026
|
-
return undefined
|
|
1027
|
-
}
|
|
1026
|
+
return undefined;
|
|
1027
|
+
};
|
|
1028
1028
|
|
|
1029
1029
|
export const coerceInteger = (value: unknown): number => {
|
|
1030
|
-
if (typeof value === 'number') return Math.round(value)
|
|
1031
|
-
if (typeof value === 'string') return parseInt(value, 10)
|
|
1030
|
+
if (typeof value === 'number') return Math.round(value);
|
|
1031
|
+
if (typeof value === 'string') return parseInt(value, 10);
|
|
1032
1032
|
|
|
1033
|
-
throw new LorikeetError(`Could not coerce ${value} (type: ${typeof value}) into a number`)
|
|
1034
|
-
}
|
|
1033
|
+
throw new LorikeetError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
|
|
1034
|
+
};
|
|
1035
1035
|
|
|
1036
1036
|
export const coerceFloat = (value: unknown): number => {
|
|
1037
|
-
if (typeof value === 'number') return value
|
|
1038
|
-
if (typeof value === 'string') return parseFloat(value)
|
|
1037
|
+
if (typeof value === 'number') return value;
|
|
1038
|
+
if (typeof value === 'string') return parseFloat(value);
|
|
1039
1039
|
|
|
1040
|
-
throw new LorikeetError(`Could not coerce ${value} (type: ${typeof value}) into a number`)
|
|
1041
|
-
}
|
|
1040
|
+
throw new LorikeetError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
|
|
1041
|
+
};
|
|
1042
1042
|
|
|
1043
1043
|
export const coerceBoolean = (value: unknown): boolean => {
|
|
1044
|
-
if (typeof value === 'boolean') return value
|
|
1045
|
-
if (typeof value === 'string') return value === 'true'
|
|
1046
|
-
return Boolean(value)
|
|
1047
|
-
}
|
|
1044
|
+
if (typeof value === 'boolean') return value;
|
|
1045
|
+
if (typeof value === 'string') return value === 'true';
|
|
1046
|
+
return Boolean(value);
|
|
1047
|
+
};
|
|
1048
1048
|
|
|
1049
1049
|
export const maybeCoerceInteger = (value: unknown): number | undefined => {
|
|
1050
1050
|
if (value === undefined) {
|
|
1051
|
-
return undefined
|
|
1051
|
+
return undefined;
|
|
1052
1052
|
}
|
|
1053
|
-
return coerceInteger(value)
|
|
1054
|
-
}
|
|
1053
|
+
return coerceInteger(value);
|
|
1054
|
+
};
|
|
1055
1055
|
|
|
1056
1056
|
export const maybeCoerceFloat = (value: unknown): number | undefined => {
|
|
1057
1057
|
if (value === undefined) {
|
|
1058
|
-
return undefined
|
|
1058
|
+
return undefined;
|
|
1059
1059
|
}
|
|
1060
|
-
return coerceFloat(value)
|
|
1061
|
-
}
|
|
1060
|
+
return coerceFloat(value);
|
|
1061
|
+
};
|
|
1062
1062
|
|
|
1063
1063
|
export const maybeCoerceBoolean = (value: unknown): boolean | undefined => {
|
|
1064
1064
|
if (value === undefined) {
|
|
1065
|
-
return undefined
|
|
1065
|
+
return undefined;
|
|
1066
1066
|
}
|
|
1067
|
-
return coerceBoolean(value)
|
|
1068
|
-
}
|
|
1067
|
+
return coerceBoolean(value);
|
|
1068
|
+
};
|
|
1069
1069
|
|
|
1070
1070
|
// https://stackoverflow.com/a/34491287
|
|
1071
1071
|
export function isEmptyObj(obj: Object | null | undefined): boolean {
|
|
1072
|
-
if (!obj) return true
|
|
1073
|
-
for (const _k in obj) return false
|
|
1074
|
-
return true
|
|
1072
|
+
if (!obj) return true;
|
|
1073
|
+
for (const _k in obj) return false;
|
|
1074
|
+
return true;
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
1077
|
// https://eslint.org/docs/latest/rules/no-prototype-builtins
|
|
1078
1078
|
export function hasOwn(obj: Object, key: string): boolean {
|
|
1079
|
-
return Object.prototype.hasOwnProperty.call(obj, key)
|
|
1079
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
1082
|
/**
|
|
@@ -1087,23 +1087,23 @@ export function hasOwn(obj: Object, key: string): boolean {
|
|
|
1087
1087
|
*/
|
|
1088
1088
|
function applyHeadersMut(targetHeaders: Headers, newHeaders: Headers): void {
|
|
1089
1089
|
for (const k in newHeaders) {
|
|
1090
|
-
if (!hasOwn(newHeaders, k)) continue
|
|
1091
|
-
const lowerKey = k.toLowerCase()
|
|
1092
|
-
if (!lowerKey) continue
|
|
1090
|
+
if (!hasOwn(newHeaders, k)) continue;
|
|
1091
|
+
const lowerKey = k.toLowerCase();
|
|
1092
|
+
if (!lowerKey) continue;
|
|
1093
1093
|
|
|
1094
|
-
const val = newHeaders[k]
|
|
1094
|
+
const val = newHeaders[k];
|
|
1095
1095
|
|
|
1096
1096
|
if (val === null) {
|
|
1097
|
-
delete targetHeaders[lowerKey]
|
|
1097
|
+
delete targetHeaders[lowerKey];
|
|
1098
1098
|
} else if (val !== undefined) {
|
|
1099
|
-
targetHeaders[lowerKey] = val
|
|
1099
|
+
targetHeaders[lowerKey] = val;
|
|
1100
1100
|
}
|
|
1101
1101
|
}
|
|
1102
1102
|
}
|
|
1103
1103
|
|
|
1104
1104
|
export function debug(action: string, ...args: any[]) {
|
|
1105
1105
|
if (typeof process !== 'undefined' && process?.env?.['DEBUG'] === 'true') {
|
|
1106
|
-
console.log(`Lorikeet:DEBUG:${action}`, ...args)
|
|
1106
|
+
console.log(`Lorikeet:DEBUG:${action}`, ...args);
|
|
1107
1107
|
}
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
@@ -1112,11 +1112,11 @@ export function debug(action: string, ...args: any[]) {
|
|
|
1112
1112
|
*/
|
|
1113
1113
|
const uuid4 = () => {
|
|
1114
1114
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
1115
|
-
const r = (Math.random() * 16) | 0
|
|
1116
|
-
const v = c === 'x' ? r : (r & 0x3) | 0x8
|
|
1117
|
-
return v.toString(16)
|
|
1118
|
-
})
|
|
1119
|
-
}
|
|
1115
|
+
const r = (Math.random() * 16) | 0;
|
|
1116
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
1117
|
+
return v.toString(16);
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1120
1120
|
|
|
1121
1121
|
export const isRunningInBrowser = () => {
|
|
1122
1122
|
return (
|
|
@@ -1126,37 +1126,37 @@ export const isRunningInBrowser = () => {
|
|
|
1126
1126
|
typeof window.document !== 'undefined' &&
|
|
1127
1127
|
// @ts-ignore
|
|
1128
1128
|
typeof navigator !== 'undefined'
|
|
1129
|
-
)
|
|
1130
|
-
}
|
|
1129
|
+
);
|
|
1130
|
+
};
|
|
1131
1131
|
|
|
1132
1132
|
export interface HeadersProtocol {
|
|
1133
|
-
get: (header: string) => string | null | undefined
|
|
1133
|
+
get: (header: string) => string | null | undefined;
|
|
1134
1134
|
}
|
|
1135
|
-
export type HeadersLike = Record<string, string | string[] | undefined> | HeadersProtocol
|
|
1135
|
+
export type HeadersLike = Record<string, string | string[] | undefined> | HeadersProtocol;
|
|
1136
1136
|
|
|
1137
1137
|
export const isHeadersProtocol = (headers: any): headers is HeadersProtocol => {
|
|
1138
|
-
return typeof headers?.get === 'function'
|
|
1139
|
-
}
|
|
1138
|
+
return typeof headers?.get === 'function';
|
|
1139
|
+
};
|
|
1140
1140
|
|
|
1141
1141
|
export const getRequiredHeader = (headers: HeadersLike | Headers, header: string): string => {
|
|
1142
|
-
const foundHeader = getHeader(headers, header)
|
|
1142
|
+
const foundHeader = getHeader(headers, header);
|
|
1143
1143
|
if (foundHeader === undefined) {
|
|
1144
|
-
throw new Error(`Could not find ${header} header`)
|
|
1144
|
+
throw new Error(`Could not find ${header} header`);
|
|
1145
1145
|
}
|
|
1146
|
-
return foundHeader
|
|
1147
|
-
}
|
|
1146
|
+
return foundHeader;
|
|
1147
|
+
};
|
|
1148
1148
|
|
|
1149
1149
|
export const getHeader = (headers: HeadersLike | Headers, header: string): string | undefined => {
|
|
1150
|
-
const lowerCasedHeader = header.toLowerCase()
|
|
1150
|
+
const lowerCasedHeader = header.toLowerCase();
|
|
1151
1151
|
if (isHeadersProtocol(headers)) {
|
|
1152
1152
|
// to deal with the case where the header looks like Stainless-Event-Id
|
|
1153
1153
|
const intercapsHeader =
|
|
1154
1154
|
header[0]?.toUpperCase() +
|
|
1155
|
-
header.substring(1).replace(/([^\w])(\w)/g, (_m, g1, g2) => g1 + g2.toUpperCase())
|
|
1155
|
+
header.substring(1).replace(/([^\w])(\w)/g, (_m, g1, g2) => g1 + g2.toUpperCase());
|
|
1156
1156
|
for (const key of [header, lowerCasedHeader, header.toUpperCase(), intercapsHeader]) {
|
|
1157
|
-
const value = headers.get(key)
|
|
1157
|
+
const value = headers.get(key);
|
|
1158
1158
|
if (value) {
|
|
1159
|
-
return value
|
|
1159
|
+
return value;
|
|
1160
1160
|
}
|
|
1161
1161
|
}
|
|
1162
1162
|
}
|
|
@@ -1164,33 +1164,33 @@ export const getHeader = (headers: HeadersLike | Headers, header: string): strin
|
|
|
1164
1164
|
for (const [key, value] of Object.entries(headers)) {
|
|
1165
1165
|
if (key.toLowerCase() === lowerCasedHeader) {
|
|
1166
1166
|
if (Array.isArray(value)) {
|
|
1167
|
-
if (value.length <= 1) return value[0]
|
|
1168
|
-
console.warn(`Received ${value.length} entries for the ${header} header, using the first entry.`)
|
|
1169
|
-
return value[0]
|
|
1167
|
+
if (value.length <= 1) return value[0];
|
|
1168
|
+
console.warn(`Received ${value.length} entries for the ${header} header, using the first entry.`);
|
|
1169
|
+
return value[0];
|
|
1170
1170
|
}
|
|
1171
|
-
return value
|
|
1171
|
+
return value;
|
|
1172
1172
|
}
|
|
1173
1173
|
}
|
|
1174
1174
|
|
|
1175
|
-
return undefined
|
|
1176
|
-
}
|
|
1175
|
+
return undefined;
|
|
1176
|
+
};
|
|
1177
1177
|
|
|
1178
1178
|
/**
|
|
1179
1179
|
* Encodes a string to Base64 format.
|
|
1180
1180
|
*/
|
|
1181
1181
|
export const toBase64 = (str: string | null | undefined): string => {
|
|
1182
|
-
if (!str) return ''
|
|
1182
|
+
if (!str) return '';
|
|
1183
1183
|
if (typeof Buffer !== 'undefined') {
|
|
1184
|
-
return Buffer.from(str).toString('base64')
|
|
1184
|
+
return Buffer.from(str).toString('base64');
|
|
1185
1185
|
}
|
|
1186
1186
|
|
|
1187
1187
|
if (typeof btoa !== 'undefined') {
|
|
1188
|
-
return btoa(str)
|
|
1188
|
+
return btoa(str);
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
|
-
throw new LorikeetError('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined')
|
|
1192
|
-
}
|
|
1191
|
+
throw new LorikeetError('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined');
|
|
1192
|
+
};
|
|
1193
1193
|
|
|
1194
1194
|
export function isObj(obj: unknown): obj is Record<string, unknown> {
|
|
1195
|
-
return obj != null && typeof obj === 'object' && !Array.isArray(obj)
|
|
1195
|
+
return obj != null && typeof obj === 'object' && !Array.isArray(obj);
|
|
1196
1196
|
}
|