@wabot-dev/framework 0.1.0-beta.11 → 0.1.0-beta.13
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/dist/src/ai/claude/ClaudeChatBotAdapter.js +115 -0
- package/dist/src/channels/whatsapp/WhatsApp.js +2 -2
- package/dist/src/channels/whatsapp/WhatsAppSender.js +10 -1
- package/dist/src/channels/whatsapp/WhatsAppSenderByCloudApi.js +3 -9
- package/dist/src/channels/whatsapp/WhatsAppSenderByDevConnection.js +6 -11
- package/dist/src/core/{Persistent.js → Entity.js} +24 -9
- package/dist/src/core/Storable.js +8 -0
- package/dist/src/core/chat/Chat.js +2 -2
- package/dist/src/core/chat/ChatItem.js +2 -2
- package/dist/src/core/user/User.js +2 -2
- package/dist/src/error/CustomError.js +1 -0
- package/dist/src/index.d.ts +250 -181
- package/dist/src/index.js +13 -9
- package/dist/src/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.js +221 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/client.js +540 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/core/api-promise.js +74 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/core/error.js +100 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/core/pagination.js +119 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/core/resource.js +8 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/core/streaming.js +283 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/constants.js +16 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.js +37 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/decoders/line.js +110 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/detect-platform.js +159 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/errors.js +37 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/headers.js +71 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/parse.js +53 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/request-options.js +11 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/shims.js +86 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/to-file.js +94 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/tslib.js +14 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/uploads.js +113 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/bytes.js +27 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/env.js +19 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/log.js +82 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/path.js +76 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/sleep.js +4 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/uuid.js +16 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/internal/utils/values.js +48 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.js +588 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/lib/MessageStream.js +582 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/beta/beta.js +19 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/beta/files.js +120 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.js +202 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.js +85 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/beta/models.js +58 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/completions.js +21 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/messages/batches.js +151 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/messages/messages.js +71 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/resources/models.js +43 -0
- package/dist/src/node_modules/@anthropic-ai/sdk/version.js +3 -0
- package/dist/src/pre-made/repository/chat/ram/RamChatRepository.js +1 -1
- package/dist/src/repository/pg/PgCrudRepository.js +2 -2
- package/dist/src/repository/pg/PgRepositoryBase.js +2 -2
- package/dist/src/rest-controller/metadata/@middleware.js +16 -0
- package/dist/src/rest-controller/metadata/RestControllerMetadataStore.js +13 -0
- package/dist/src/rest-controller/runRestControllers.js +30 -22
- package/dist/src/server/prepareChatContainer.js +2 -2
- package/dist/src/validation/metadata/@isModel.js +18 -0
- package/dist/src/validation/validators/validateModel.js +2 -6
- package/package.json +2 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { VERSION } from '../version.js';
|
|
2
|
+
|
|
3
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
4
|
+
const isRunningInBrowser = () => {
|
|
5
|
+
return (
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
typeof window !== 'undefined' &&
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
typeof window.document !== 'undefined' &&
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
typeof navigator !== 'undefined');
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Note this does not detect 'browser'; for that, use getBrowserInfo().
|
|
15
|
+
*/
|
|
16
|
+
function getDetectedPlatform() {
|
|
17
|
+
if (typeof Deno !== 'undefined' && Deno.build != null) {
|
|
18
|
+
return 'deno';
|
|
19
|
+
}
|
|
20
|
+
if (typeof EdgeRuntime !== 'undefined') {
|
|
21
|
+
return 'edge';
|
|
22
|
+
}
|
|
23
|
+
if (Object.prototype.toString.call(typeof globalThis.process !== 'undefined' ? globalThis.process : 0) === '[object process]') {
|
|
24
|
+
return 'node';
|
|
25
|
+
}
|
|
26
|
+
return 'unknown';
|
|
27
|
+
}
|
|
28
|
+
const getPlatformProperties = () => {
|
|
29
|
+
const detectedPlatform = getDetectedPlatform();
|
|
30
|
+
if (detectedPlatform === 'deno') {
|
|
31
|
+
return {
|
|
32
|
+
'X-Stainless-Lang': 'js',
|
|
33
|
+
'X-Stainless-Package-Version': VERSION,
|
|
34
|
+
'X-Stainless-OS': normalizePlatform(Deno.build.os),
|
|
35
|
+
'X-Stainless-Arch': normalizeArch(Deno.build.arch),
|
|
36
|
+
'X-Stainless-Runtime': 'deno',
|
|
37
|
+
'X-Stainless-Runtime-Version': typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (typeof EdgeRuntime !== 'undefined') {
|
|
41
|
+
return {
|
|
42
|
+
'X-Stainless-Lang': 'js',
|
|
43
|
+
'X-Stainless-Package-Version': VERSION,
|
|
44
|
+
'X-Stainless-OS': 'Unknown',
|
|
45
|
+
'X-Stainless-Arch': `other:${EdgeRuntime}`,
|
|
46
|
+
'X-Stainless-Runtime': 'edge',
|
|
47
|
+
'X-Stainless-Runtime-Version': globalThis.process.version,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Check if Node.js
|
|
51
|
+
if (detectedPlatform === 'node') {
|
|
52
|
+
return {
|
|
53
|
+
'X-Stainless-Lang': 'js',
|
|
54
|
+
'X-Stainless-Package-Version': VERSION,
|
|
55
|
+
'X-Stainless-OS': normalizePlatform(globalThis.process.platform ?? 'unknown'),
|
|
56
|
+
'X-Stainless-Arch': normalizeArch(globalThis.process.arch ?? 'unknown'),
|
|
57
|
+
'X-Stainless-Runtime': 'node',
|
|
58
|
+
'X-Stainless-Runtime-Version': globalThis.process.version ?? 'unknown',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const browserInfo = getBrowserInfo();
|
|
62
|
+
if (browserInfo) {
|
|
63
|
+
return {
|
|
64
|
+
'X-Stainless-Lang': 'js',
|
|
65
|
+
'X-Stainless-Package-Version': VERSION,
|
|
66
|
+
'X-Stainless-OS': 'Unknown',
|
|
67
|
+
'X-Stainless-Arch': 'unknown',
|
|
68
|
+
'X-Stainless-Runtime': `browser:${browserInfo.browser}`,
|
|
69
|
+
'X-Stainless-Runtime-Version': browserInfo.version,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// TODO add support for Cloudflare workers, etc.
|
|
73
|
+
return {
|
|
74
|
+
'X-Stainless-Lang': 'js',
|
|
75
|
+
'X-Stainless-Package-Version': VERSION,
|
|
76
|
+
'X-Stainless-OS': 'Unknown',
|
|
77
|
+
'X-Stainless-Arch': 'unknown',
|
|
78
|
+
'X-Stainless-Runtime': 'unknown',
|
|
79
|
+
'X-Stainless-Runtime-Version': 'unknown',
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts
|
|
83
|
+
function getBrowserInfo() {
|
|
84
|
+
if (typeof navigator === 'undefined' || !navigator) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// NOTE: The order matters here!
|
|
88
|
+
const browserPatterns = [
|
|
89
|
+
{ key: 'edge', pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
90
|
+
{ key: 'ie', pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
91
|
+
{ key: 'ie', pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
92
|
+
{ key: 'chrome', pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
93
|
+
{ key: 'firefox', pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
|
94
|
+
{ key: 'safari', pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ },
|
|
95
|
+
];
|
|
96
|
+
// Find the FIRST matching browser
|
|
97
|
+
for (const { key, pattern } of browserPatterns) {
|
|
98
|
+
const match = pattern.exec(navigator.userAgent);
|
|
99
|
+
if (match) {
|
|
100
|
+
const major = match[1] || 0;
|
|
101
|
+
const minor = match[2] || 0;
|
|
102
|
+
const patch = match[3] || 0;
|
|
103
|
+
return { browser: key, version: `${major}.${minor}.${patch}` };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const normalizeArch = (arch) => {
|
|
109
|
+
// Node docs:
|
|
110
|
+
// - https://nodejs.org/api/process.html#processarch
|
|
111
|
+
// Deno docs:
|
|
112
|
+
// - https://doc.deno.land/deno/stable/~/Deno.build
|
|
113
|
+
if (arch === 'x32')
|
|
114
|
+
return 'x32';
|
|
115
|
+
if (arch === 'x86_64' || arch === 'x64')
|
|
116
|
+
return 'x64';
|
|
117
|
+
if (arch === 'arm')
|
|
118
|
+
return 'arm';
|
|
119
|
+
if (arch === 'aarch64' || arch === 'arm64')
|
|
120
|
+
return 'arm64';
|
|
121
|
+
if (arch)
|
|
122
|
+
return `other:${arch}`;
|
|
123
|
+
return 'unknown';
|
|
124
|
+
};
|
|
125
|
+
const normalizePlatform = (platform) => {
|
|
126
|
+
// Node platforms:
|
|
127
|
+
// - https://nodejs.org/api/process.html#processplatform
|
|
128
|
+
// Deno platforms:
|
|
129
|
+
// - https://doc.deno.land/deno/stable/~/Deno.build
|
|
130
|
+
// - https://github.com/denoland/deno/issues/14799
|
|
131
|
+
platform = platform.toLowerCase();
|
|
132
|
+
// NOTE: this iOS check is untested and may not work
|
|
133
|
+
// Node does not work natively on IOS, there is a fork at
|
|
134
|
+
// https://github.com/nodejs-mobile/nodejs-mobile
|
|
135
|
+
// however it is unknown at the time of writing how to detect if it is running
|
|
136
|
+
if (platform.includes('ios'))
|
|
137
|
+
return 'iOS';
|
|
138
|
+
if (platform === 'android')
|
|
139
|
+
return 'Android';
|
|
140
|
+
if (platform === 'darwin')
|
|
141
|
+
return 'MacOS';
|
|
142
|
+
if (platform === 'win32')
|
|
143
|
+
return 'Windows';
|
|
144
|
+
if (platform === 'freebsd')
|
|
145
|
+
return 'FreeBSD';
|
|
146
|
+
if (platform === 'openbsd')
|
|
147
|
+
return 'OpenBSD';
|
|
148
|
+
if (platform === 'linux')
|
|
149
|
+
return 'Linux';
|
|
150
|
+
if (platform)
|
|
151
|
+
return `Other:${platform}`;
|
|
152
|
+
return 'Unknown';
|
|
153
|
+
};
|
|
154
|
+
let _platformHeaders;
|
|
155
|
+
const getPlatformHeaders = () => {
|
|
156
|
+
return (_platformHeaders ?? (_platformHeaders = getPlatformProperties()));
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export { getPlatformHeaders, isRunningInBrowser };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
|
+
function isAbortError(err) {
|
|
3
|
+
return (typeof err === 'object' &&
|
|
4
|
+
err !== null &&
|
|
5
|
+
// Spec-compliant fetch implementations
|
|
6
|
+
(('name' in err && err.name === 'AbortError') ||
|
|
7
|
+
// Expo fetch
|
|
8
|
+
('message' in err && String(err.message).includes('FetchRequestCanceledException'))));
|
|
9
|
+
}
|
|
10
|
+
const castToError = (err) => {
|
|
11
|
+
if (err instanceof Error)
|
|
12
|
+
return err;
|
|
13
|
+
if (typeof err === 'object' && err !== null) {
|
|
14
|
+
try {
|
|
15
|
+
if (Object.prototype.toString.call(err) === '[object Error]') {
|
|
16
|
+
// @ts-ignore - not all envs have native support for cause yet
|
|
17
|
+
const error = new Error(err.message, err.cause ? { cause: err.cause } : {});
|
|
18
|
+
if (err.stack)
|
|
19
|
+
error.stack = err.stack;
|
|
20
|
+
// @ts-ignore - not all envs have native support for cause yet
|
|
21
|
+
if (err.cause && !error.cause)
|
|
22
|
+
error.cause = err.cause;
|
|
23
|
+
if (err.name)
|
|
24
|
+
error.name = err.name;
|
|
25
|
+
return error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch { }
|
|
29
|
+
try {
|
|
30
|
+
return new Error(JSON.stringify(err));
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
}
|
|
34
|
+
return new Error(err);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { castToError, isAbortError };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { isReadonlyArray } from './utils/values.js';
|
|
2
|
+
|
|
3
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
4
|
+
const brand_privateNullableHeaders = Symbol.for('brand.privateNullableHeaders');
|
|
5
|
+
function* iterateHeaders(headers) {
|
|
6
|
+
if (!headers)
|
|
7
|
+
return;
|
|
8
|
+
if (brand_privateNullableHeaders in headers) {
|
|
9
|
+
const { values, nulls } = headers;
|
|
10
|
+
yield* values.entries();
|
|
11
|
+
for (const name of nulls) {
|
|
12
|
+
yield [name, null];
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
let shouldClear = false;
|
|
17
|
+
let iter;
|
|
18
|
+
if (headers instanceof Headers) {
|
|
19
|
+
iter = headers.entries();
|
|
20
|
+
}
|
|
21
|
+
else if (isReadonlyArray(headers)) {
|
|
22
|
+
iter = headers;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
shouldClear = true;
|
|
26
|
+
iter = Object.entries(headers ?? {});
|
|
27
|
+
}
|
|
28
|
+
for (let row of iter) {
|
|
29
|
+
const name = row[0];
|
|
30
|
+
if (typeof name !== 'string')
|
|
31
|
+
throw new TypeError('expected header name to be a string');
|
|
32
|
+
const values = isReadonlyArray(row[1]) ? row[1] : [row[1]];
|
|
33
|
+
let didClear = false;
|
|
34
|
+
for (const value of values) {
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
// Objects keys always overwrite older headers, they never append.
|
|
38
|
+
// Yield a null to clear the header before adding the new values.
|
|
39
|
+
if (shouldClear && !didClear) {
|
|
40
|
+
didClear = true;
|
|
41
|
+
yield [name, null];
|
|
42
|
+
}
|
|
43
|
+
yield [name, value];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const buildHeaders = (newHeaders) => {
|
|
48
|
+
const targetHeaders = new Headers();
|
|
49
|
+
const nullHeaders = new Set();
|
|
50
|
+
for (const headers of newHeaders) {
|
|
51
|
+
const seenHeaders = new Set();
|
|
52
|
+
for (const [name, value] of iterateHeaders(headers)) {
|
|
53
|
+
const lowerName = name.toLowerCase();
|
|
54
|
+
if (!seenHeaders.has(lowerName)) {
|
|
55
|
+
targetHeaders.delete(name);
|
|
56
|
+
seenHeaders.add(lowerName);
|
|
57
|
+
}
|
|
58
|
+
if (value === null) {
|
|
59
|
+
targetHeaders.delete(name);
|
|
60
|
+
nullHeaders.add(lowerName);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
targetHeaders.append(name, value);
|
|
64
|
+
nullHeaders.delete(lowerName);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { [brand_privateNullableHeaders]: true, values: targetHeaders, nulls: nullHeaders };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export { buildHeaders };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Stream } from '../core/streaming.js';
|
|
2
|
+
import { loggerFor, formatRequestDetails } from './utils/log.js';
|
|
3
|
+
|
|
4
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
5
|
+
async function defaultParseResponse(client, props) {
|
|
6
|
+
const { response, requestLogID, retryOfRequestLogID, startTime } = props;
|
|
7
|
+
const body = await (async () => {
|
|
8
|
+
if (props.options.stream) {
|
|
9
|
+
loggerFor(client).debug('response', response.status, response.url, response.headers, response.body);
|
|
10
|
+
// Note: there is an invariant here that isn't represented in the type system
|
|
11
|
+
// that if you set `stream: true` the response type must also be `Stream<T>`
|
|
12
|
+
if (props.options.__streamClass) {
|
|
13
|
+
return props.options.__streamClass.fromSSEResponse(response, props.controller);
|
|
14
|
+
}
|
|
15
|
+
return Stream.fromSSEResponse(response, props.controller);
|
|
16
|
+
}
|
|
17
|
+
// fetch refuses to read the body when the status code is 204.
|
|
18
|
+
if (response.status === 204) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (props.options.__binaryResponse) {
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
24
|
+
const contentType = response.headers.get('content-type');
|
|
25
|
+
const mediaType = contentType?.split(';')[0]?.trim();
|
|
26
|
+
const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
|
|
27
|
+
if (isJSON) {
|
|
28
|
+
const json = await response.json();
|
|
29
|
+
return addRequestID(json, response);
|
|
30
|
+
}
|
|
31
|
+
const text = await response.text();
|
|
32
|
+
return text;
|
|
33
|
+
})();
|
|
34
|
+
loggerFor(client).debug(`[${requestLogID}] response parsed`, formatRequestDetails({
|
|
35
|
+
retryOfRequestLogID,
|
|
36
|
+
url: response.url,
|
|
37
|
+
status: response.status,
|
|
38
|
+
body,
|
|
39
|
+
durationMs: Date.now() - startTime,
|
|
40
|
+
}));
|
|
41
|
+
return body;
|
|
42
|
+
}
|
|
43
|
+
function addRequestID(value, response) {
|
|
44
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
return Object.defineProperty(value, '_request_id', {
|
|
48
|
+
value: response.headers.get('request-id'),
|
|
49
|
+
enumerable: false,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { addRequestID, defaultParseResponse };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
|
+
const FallbackEncoder = ({ headers, body }) => {
|
|
3
|
+
return {
|
|
4
|
+
bodyHeaders: {
|
|
5
|
+
'content-type': 'application/json',
|
|
6
|
+
},
|
|
7
|
+
body: JSON.stringify(body),
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { FallbackEncoder };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
|
+
function getDefaultFetch() {
|
|
3
|
+
if (typeof fetch !== 'undefined') {
|
|
4
|
+
return fetch;
|
|
5
|
+
}
|
|
6
|
+
throw new Error('`fetch` is not defined as a global; Either pass `fetch` to the client, `new Anthropic({ fetch })` or polyfill the global, `globalThis.fetch = fetch`');
|
|
7
|
+
}
|
|
8
|
+
function makeReadableStream(...args) {
|
|
9
|
+
const ReadableStream = globalThis.ReadableStream;
|
|
10
|
+
if (typeof ReadableStream === 'undefined') {
|
|
11
|
+
// Note: All of the platforms / runtimes we officially support already define
|
|
12
|
+
// `ReadableStream` as a global, so this should only ever be hit on unsupported runtimes.
|
|
13
|
+
throw new Error('`ReadableStream` is not defined as a global; You will need to polyfill it, `globalThis.ReadableStream = ReadableStream`');
|
|
14
|
+
}
|
|
15
|
+
return new ReadableStream(...args);
|
|
16
|
+
}
|
|
17
|
+
function ReadableStreamFrom(iterable) {
|
|
18
|
+
let iter = Symbol.asyncIterator in iterable ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
|
|
19
|
+
return makeReadableStream({
|
|
20
|
+
start() { },
|
|
21
|
+
async pull(controller) {
|
|
22
|
+
const { done, value } = await iter.next();
|
|
23
|
+
if (done) {
|
|
24
|
+
controller.close();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
controller.enqueue(value);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async cancel() {
|
|
31
|
+
await iter.return?.();
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Most browsers don't yet have async iterable support for ReadableStream,
|
|
37
|
+
* and Node has a very different way of reading bytes from its "ReadableStream".
|
|
38
|
+
*
|
|
39
|
+
* This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490
|
|
40
|
+
*/
|
|
41
|
+
function ReadableStreamToAsyncIterable(stream) {
|
|
42
|
+
if (stream[Symbol.asyncIterator])
|
|
43
|
+
return stream;
|
|
44
|
+
const reader = stream.getReader();
|
|
45
|
+
return {
|
|
46
|
+
async next() {
|
|
47
|
+
try {
|
|
48
|
+
const result = await reader.read();
|
|
49
|
+
if (result?.done)
|
|
50
|
+
reader.releaseLock(); // release lock when stream becomes closed
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
reader.releaseLock(); // release lock when stream becomes errored
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async return() {
|
|
59
|
+
const cancelPromise = reader.cancel();
|
|
60
|
+
reader.releaseLock();
|
|
61
|
+
await cancelPromise;
|
|
62
|
+
return { done: true, value: undefined };
|
|
63
|
+
},
|
|
64
|
+
[Symbol.asyncIterator]() {
|
|
65
|
+
return this;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Cancels a ReadableStream we don't need to consume.
|
|
71
|
+
* See https://undici.nodejs.org/#/?id=garbage-collection
|
|
72
|
+
*/
|
|
73
|
+
async function CancelReadableStream(stream) {
|
|
74
|
+
if (stream === null || typeof stream !== 'object')
|
|
75
|
+
return;
|
|
76
|
+
if (stream[Symbol.asyncIterator]) {
|
|
77
|
+
await stream[Symbol.asyncIterator]().return?.();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const reader = stream.getReader();
|
|
81
|
+
const cancelPromise = reader.cancel();
|
|
82
|
+
reader.releaseLock();
|
|
83
|
+
await cancelPromise;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { CancelReadableStream, ReadableStreamFrom, ReadableStreamToAsyncIterable, getDefaultFetch, makeReadableStream };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { checkFileSupport, getName, makeFile, isAsyncIterable } from './uploads.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This check adds the arrayBuffer() method type because it is available and used at runtime
|
|
5
|
+
*/
|
|
6
|
+
const isBlobLike = (value) => value != null &&
|
|
7
|
+
typeof value === 'object' &&
|
|
8
|
+
typeof value.size === 'number' &&
|
|
9
|
+
typeof value.type === 'string' &&
|
|
10
|
+
typeof value.text === 'function' &&
|
|
11
|
+
typeof value.slice === 'function' &&
|
|
12
|
+
typeof value.arrayBuffer === 'function';
|
|
13
|
+
/**
|
|
14
|
+
* This check adds the arrayBuffer() method type because it is available and used at runtime
|
|
15
|
+
*/
|
|
16
|
+
const isFileLike = (value) => value != null &&
|
|
17
|
+
typeof value === 'object' &&
|
|
18
|
+
typeof value.name === 'string' &&
|
|
19
|
+
typeof value.lastModified === 'number' &&
|
|
20
|
+
isBlobLike(value);
|
|
21
|
+
const isResponseLike = (value) => value != null &&
|
|
22
|
+
typeof value === 'object' &&
|
|
23
|
+
typeof value.url === 'string' &&
|
|
24
|
+
typeof value.blob === 'function';
|
|
25
|
+
/**
|
|
26
|
+
* Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
|
|
27
|
+
* @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
|
|
28
|
+
* @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
|
|
29
|
+
* @param {Object=} options additional properties
|
|
30
|
+
* @param {string=} options.type the MIME type of the content
|
|
31
|
+
* @param {number=} options.lastModified the last modified timestamp
|
|
32
|
+
* @returns a {@link File} with the given properties
|
|
33
|
+
*/
|
|
34
|
+
async function toFile(value, name, options) {
|
|
35
|
+
checkFileSupport();
|
|
36
|
+
// If it's a promise, resolve it.
|
|
37
|
+
value = await value;
|
|
38
|
+
name || (name = getName(value));
|
|
39
|
+
// If we've been given a `File` we don't need to do anything if the name / options
|
|
40
|
+
// have not been customised.
|
|
41
|
+
if (isFileLike(value)) {
|
|
42
|
+
if (value instanceof File && name == null && options == null) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
return makeFile([await value.arrayBuffer()], name ?? value.name, {
|
|
46
|
+
type: value.type,
|
|
47
|
+
lastModified: value.lastModified,
|
|
48
|
+
...options,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (isResponseLike(value)) {
|
|
52
|
+
const blob = await value.blob();
|
|
53
|
+
name || (name = new URL(value.url).pathname.split(/[\\/]/).pop());
|
|
54
|
+
return makeFile(await getBytes(blob), name, options);
|
|
55
|
+
}
|
|
56
|
+
const parts = await getBytes(value);
|
|
57
|
+
if (!options?.type) {
|
|
58
|
+
const type = parts.find((part) => typeof part === 'object' && 'type' in part && part.type);
|
|
59
|
+
if (typeof type === 'string') {
|
|
60
|
+
options = { ...options, type };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return makeFile(parts, name, options);
|
|
64
|
+
}
|
|
65
|
+
async function getBytes(value) {
|
|
66
|
+
let parts = [];
|
|
67
|
+
if (typeof value === 'string' ||
|
|
68
|
+
ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc.
|
|
69
|
+
value instanceof ArrayBuffer) {
|
|
70
|
+
parts.push(value);
|
|
71
|
+
}
|
|
72
|
+
else if (isBlobLike(value)) {
|
|
73
|
+
parts.push(value instanceof Blob ? value : await value.arrayBuffer());
|
|
74
|
+
}
|
|
75
|
+
else if (isAsyncIterable(value) // includes Readable, ReadableStream, etc.
|
|
76
|
+
) {
|
|
77
|
+
for await (const chunk of value) {
|
|
78
|
+
parts.push(...(await getBytes(chunk))); // TODO, consider validating?
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const constructor = value?.constructor?.name;
|
|
83
|
+
throw new Error(`Unexpected data type: ${typeof value}${constructor ? `; constructor: ${constructor}` : ''}${propsForError(value)}`);
|
|
84
|
+
}
|
|
85
|
+
return parts;
|
|
86
|
+
}
|
|
87
|
+
function propsForError(value) {
|
|
88
|
+
if (typeof value !== 'object' || value === null)
|
|
89
|
+
return '';
|
|
90
|
+
const props = Object.getOwnPropertyNames(value);
|
|
91
|
+
return `; props: [${props.map((p) => `"${p}"`).join(', ')}]`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { toFile };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
2
|
+
if (typeof state === "function" ? receiver !== state || true : !state.has(receiver))
|
|
3
|
+
throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
4
|
+
return state.set(receiver, value), value;
|
|
5
|
+
}
|
|
6
|
+
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
7
|
+
if (kind === "a" && !f)
|
|
8
|
+
throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
|
|
10
|
+
throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { __classPrivateFieldGet, __classPrivateFieldSet };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ReadableStreamFrom } from './shims.js';
|
|
2
|
+
|
|
3
|
+
const checkFileSupport = () => {
|
|
4
|
+
if (typeof File === 'undefined') {
|
|
5
|
+
const { process } = globalThis;
|
|
6
|
+
const isOldNode = typeof process?.versions?.node === 'string' && parseInt(process.versions.node.split('.')) < 20;
|
|
7
|
+
throw new Error('`File` is not defined as a global, which is required for file uploads.' +
|
|
8
|
+
(isOldNode ?
|
|
9
|
+
" Update to Node 20 LTS or newer, or set `globalThis.File` to `import('node:buffer').File`."
|
|
10
|
+
: ''));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Construct a `File` instance. This is used to ensure a helpful error is thrown
|
|
15
|
+
* for environments that don't define a global `File` yet.
|
|
16
|
+
*/
|
|
17
|
+
function makeFile(fileBits, fileName, options) {
|
|
18
|
+
checkFileSupport();
|
|
19
|
+
return new File(fileBits, fileName ?? 'unknown_file', options);
|
|
20
|
+
}
|
|
21
|
+
function getName(value) {
|
|
22
|
+
return (((typeof value === 'object' &&
|
|
23
|
+
value !== null &&
|
|
24
|
+
(('name' in value && value.name && String(value.name)) ||
|
|
25
|
+
('url' in value && value.url && String(value.url)) ||
|
|
26
|
+
('filename' in value && value.filename && String(value.filename)) ||
|
|
27
|
+
('path' in value && value.path && String(value.path)))) ||
|
|
28
|
+
'')
|
|
29
|
+
.split(/[\\/]/)
|
|
30
|
+
.pop() || undefined);
|
|
31
|
+
}
|
|
32
|
+
const isAsyncIterable = (value) => value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function';
|
|
33
|
+
const multipartFormRequestOptions = async (opts, fetch) => {
|
|
34
|
+
return { ...opts, body: await createForm(opts.body, fetch) };
|
|
35
|
+
};
|
|
36
|
+
const supportsFormDataMap = /* @__PURE__ */ new WeakMap();
|
|
37
|
+
/**
|
|
38
|
+
* node-fetch doesn't support the global FormData object in recent node versions. Instead of sending
|
|
39
|
+
* properly-encoded form data, it just stringifies the object, resulting in a request body of "[object FormData]".
|
|
40
|
+
* This function detects if the fetch function provided supports the global FormData object to avoid
|
|
41
|
+
* confusing error messages later on.
|
|
42
|
+
*/
|
|
43
|
+
function supportsFormData(fetchObject) {
|
|
44
|
+
const fetch = typeof fetchObject === 'function' ? fetchObject : fetchObject.fetch;
|
|
45
|
+
const cached = supportsFormDataMap.get(fetch);
|
|
46
|
+
if (cached)
|
|
47
|
+
return cached;
|
|
48
|
+
const promise = (async () => {
|
|
49
|
+
try {
|
|
50
|
+
const FetchResponse = ('Response' in fetch ?
|
|
51
|
+
fetch.Response
|
|
52
|
+
: (await fetch('data:,')).constructor);
|
|
53
|
+
const data = new FormData();
|
|
54
|
+
if (data.toString() === (await new FetchResponse(data).text())) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// avoid false negatives
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
supportsFormDataMap.set(fetch, promise);
|
|
65
|
+
return promise;
|
|
66
|
+
}
|
|
67
|
+
const createForm = async (body, fetch) => {
|
|
68
|
+
if (!(await supportsFormData(fetch))) {
|
|
69
|
+
throw new TypeError('The provided fetch function does not support file uploads with the current global FormData class.');
|
|
70
|
+
}
|
|
71
|
+
const form = new FormData();
|
|
72
|
+
await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value)));
|
|
73
|
+
return form;
|
|
74
|
+
};
|
|
75
|
+
// We check for Blob not File because Bun.File doesn't inherit from File,
|
|
76
|
+
// but they both inherit from Blob and have a `name` property at runtime.
|
|
77
|
+
const isNamedBlob = (value) => value instanceof Blob && 'name' in value;
|
|
78
|
+
const addFormValue = async (form, key, value) => {
|
|
79
|
+
if (value === undefined)
|
|
80
|
+
return;
|
|
81
|
+
if (value == null) {
|
|
82
|
+
throw new TypeError(`Received null for "${key}"; to pass null in FormData, you must use the string 'null'`);
|
|
83
|
+
}
|
|
84
|
+
// TODO: make nested formats configurable
|
|
85
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
86
|
+
form.append(key, String(value));
|
|
87
|
+
}
|
|
88
|
+
else if (value instanceof Response) {
|
|
89
|
+
let options = {};
|
|
90
|
+
const contentType = value.headers.get('Content-Type');
|
|
91
|
+
if (contentType) {
|
|
92
|
+
options = { type: contentType };
|
|
93
|
+
}
|
|
94
|
+
form.append(key, makeFile([await value.blob()], getName(value), options));
|
|
95
|
+
}
|
|
96
|
+
else if (isAsyncIterable(value)) {
|
|
97
|
+
form.append(key, makeFile([await new Response(ReadableStreamFrom(value)).blob()], getName(value)));
|
|
98
|
+
}
|
|
99
|
+
else if (isNamedBlob(value)) {
|
|
100
|
+
form.append(key, makeFile([value], getName(value), { type: value.type }));
|
|
101
|
+
}
|
|
102
|
+
else if (Array.isArray(value)) {
|
|
103
|
+
await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry)));
|
|
104
|
+
}
|
|
105
|
+
else if (typeof value === 'object') {
|
|
106
|
+
await Promise.all(Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
throw new TypeError(`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export { checkFileSupport, createForm, getName, isAsyncIterable, makeFile, multipartFormRequestOptions };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function concatBytes(buffers) {
|
|
2
|
+
let length = 0;
|
|
3
|
+
for (const buffer of buffers) {
|
|
4
|
+
length += buffer.length;
|
|
5
|
+
}
|
|
6
|
+
const output = new Uint8Array(length);
|
|
7
|
+
let index = 0;
|
|
8
|
+
for (const buffer of buffers) {
|
|
9
|
+
output.set(buffer, index);
|
|
10
|
+
index += buffer.length;
|
|
11
|
+
}
|
|
12
|
+
return output;
|
|
13
|
+
}
|
|
14
|
+
let encodeUTF8_;
|
|
15
|
+
function encodeUTF8(str) {
|
|
16
|
+
let encoder;
|
|
17
|
+
return (encodeUTF8_ ??
|
|
18
|
+
((encoder = new globalThis.TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder))))(str);
|
|
19
|
+
}
|
|
20
|
+
let decodeUTF8_;
|
|
21
|
+
function decodeUTF8(bytes) {
|
|
22
|
+
let decoder;
|
|
23
|
+
return (decodeUTF8_ ??
|
|
24
|
+
((decoder = new globalThis.TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder))))(bytes);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { concatBytes, decodeUTF8, encodeUTF8 };
|