-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support ESM and web platform runtimes (#25)
We have added or improved support for: - EcmaScript Modules (ESM) for Node and others (in addition to CJS) - Cloudflare Workers - Vercel Edge Runtime - Deno - Browser (tested with webpack) - though you should not expose your secret key. In Node, we still use libraries like `node-fetch` and `formdata-node`; in others, we use the native APIs. Co-authored-by: Stainless Bot <[email protected]>
- Loading branch information
1 parent
e74e156
commit d5552a5
Showing
36 changed files
with
1,276 additions
and
693 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
node_modules | ||
yarn-error.log | ||
dist | ||
/*.tgz |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
CHANGELOG.md | ||
/ecosystem-tests | ||
/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import KeepAliveAgent from 'agentkeepalive'; | ||
import type { Agent } from 'node:http'; | ||
import { AbortController as AbortControllerPolyfill } from 'abort-controller'; | ||
|
||
const defaultHttpAgent: Agent = new KeepAliveAgent({ keepAlive: true, timeout: 5 * 60 * 1000 }); | ||
const defaultHttpsAgent: Agent = new KeepAliveAgent.HttpsAgent({ keepAlive: true, timeout: 5 * 60 * 1000 }); | ||
|
||
// Polyfill global object if needed. | ||
if (typeof AbortController === 'undefined') { | ||
AbortController = AbortControllerPolyfill as any as typeof AbortController; | ||
} | ||
|
||
export const getDefaultAgent = (url: string): Agent | undefined => { | ||
if (defaultHttpsAgent && url.startsWith('https')) return defaultHttpsAgent; | ||
return defaultHttpAgent; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
* | ||
* This is a stub for non-node environments. | ||
* In node environments, it gets replaced agent.node.ts by the package export map | ||
*/ | ||
|
||
import type { Agent } from 'node:http'; | ||
|
||
export const getDefaultAgent = (url: string): Agent | undefined => { | ||
return undefined; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import fetch, { Request, Response, Headers } from 'node-fetch'; | ||
import type { RequestInfo, RequestInit, BodyInit } from 'node-fetch'; | ||
|
||
export { fetch, Request, Response, Headers }; | ||
export type { RequestInfo, RequestInit, BodyInit }; | ||
|
||
export const isPolyfilled = true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import type * as nf from 'node-fetch'; | ||
|
||
const _fetch: typeof nf.default = | ||
// @ts-ignore | ||
fetch as any; | ||
type _fetch = typeof nf.default; | ||
const _Request: typeof nf.Request = | ||
// @ts-ignore | ||
Request as any; | ||
type _Request = nf.Request; | ||
const _Response: typeof nf.Response = | ||
// @ts-ignore | ||
Response as any; | ||
type _Response = nf.Response; | ||
const _Headers: typeof nf.Headers = | ||
// @ts-ignore | ||
Headers as any; | ||
type _Headers = nf.Headers; | ||
|
||
export const isPolyfilled = false; | ||
|
||
export { _fetch as fetch, _Request as Request, _Response as Response, _Headers as Headers }; | ||
|
||
type _RequestInfo = nf.RequestInfo; | ||
type _RequestInit = nf.RequestInit; | ||
type _BodyInit = nf.BodyInit; | ||
|
||
export type { _RequestInit as RequestInit, _RequestInfo as RequestInfo, _BodyInit as BodyInit }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import type { FilePropertyBag } from 'formdata-node'; | ||
import { fileFromPath as _fileFromPath } from 'formdata-node/file-from-path'; | ||
import type { File } from './formdata.node'; | ||
|
||
export type FileFromPathOptions = Omit<FilePropertyBag, 'lastModified'>; | ||
|
||
let warned = false; | ||
|
||
/** | ||
* @deprecated use fs.createReadStream('./my/file.txt') instead | ||
*/ | ||
export async function fileFromPath(path: string): Promise<File>; | ||
export async function fileFromPath(path: string, filename?: string): Promise<File>; | ||
export async function fileFromPath(path: string, options?: FileFromPathOptions): Promise<File>; | ||
export async function fileFromPath( | ||
path: string, | ||
filename?: string, | ||
options?: FileFromPathOptions, | ||
): Promise<File>; | ||
export async function fileFromPath(path: string, ...args: any[]): Promise<File> { | ||
if (!warned) { | ||
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path)}) instead`); | ||
warned = true; | ||
} | ||
return await _fileFromPath(path, ...args); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
* | ||
* This is a stub that gets replaced by fileFromPath.node.js for node environments | ||
* in the package export map | ||
*/ | ||
|
||
import type { FilePropertyBag } from 'formdata-node'; | ||
import type { File } from './formdata'; | ||
|
||
export type FileFromPathOptions = Omit<FilePropertyBag, 'lastModified'>; | ||
|
||
/** | ||
* This is a stub for non-node environments that just throws an error. | ||
* In node environments, this module will be replaced by util/node/fileFromPath by the | ||
* package import map. | ||
*/ | ||
export async function fileFromPath(path: string): Promise<File>; | ||
export async function fileFromPath(path: string, filename?: string): Promise<File>; | ||
export async function fileFromPath(path: string, options?: FileFromPathOptions): Promise<File>; | ||
export async function fileFromPath( | ||
path: string, | ||
filename?: string, | ||
options?: FileFromPathOptions, | ||
): Promise<File>; | ||
export async function fileFromPath(): Promise<File> { | ||
throw new Error( | ||
'The `fileFromPath` function is only supported in Node. See the README for more details: https://github.com/anthropics/anthropic-sdk-typescript#file-uploads', | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { FormData, File, Blob } from 'formdata-node'; | ||
|
||
export { FormData, File, Blob }; | ||
|
||
export const isPolyfilled = true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import type * as fd from 'formdata-node'; | ||
|
||
const _FormData: typeof fd.FormData = | ||
// @ts-ignore | ||
FormData as any; | ||
type _FormData = fd.FormData; | ||
const _File: typeof fd.File = | ||
// @ts-ignore | ||
File as any; | ||
type _File = fd.File; | ||
const _Blob: typeof fd.Blob = | ||
// @ts-ignore | ||
Blob as any; | ||
type _Blob = fd.Blob; | ||
|
||
export const isPolyfilled = false; | ||
|
||
export { _FormData as FormData, _File as File, _Blob as Blob }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { FormData } from './formdata.node'; | ||
import type { RequestOptions } from '../core'; | ||
import { Readable } from 'node:stream'; | ||
import { FormDataEncoder } from 'form-data-encoder'; | ||
|
||
export async function getMultipartRequestOptions<T extends {} = Record<string, unknown>>( | ||
form: FormData, | ||
opts: RequestOptions<T>, | ||
): Promise<RequestOptions<T>> { | ||
const encoder = new FormDataEncoder(form); | ||
const readable = Readable.from(encoder); | ||
const body = { __multipartBody__: readable }; | ||
const headers = { | ||
...opts.headers, | ||
...encoder.headers, | ||
'Content-Length': encoder.contentLength, | ||
}; | ||
|
||
return { ...opts, body: body as any, headers }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { FormData } from './formdata'; | ||
import type { RequestOptions } from '../core'; | ||
|
||
export async function getMultipartRequestOptions<T extends {} = Record<string, unknown>>( | ||
form: FormData, | ||
opts: RequestOptions<T>, | ||
): Promise<RequestOptions<T>> { | ||
return { ...opts, body: { __multipartBody__: form } as any }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { type BlobPart, type Uploadable, isResponseLike, isBlobLike } from './uploadable'; | ||
|
||
export type ToFileInput = Uploadable | Exclude<BlobPart, string> | AsyncIterable<BlobPart>; | ||
|
||
export type FilePropertyBag = { | ||
type?: string; | ||
lastModified?: number; | ||
}; | ||
|
||
export async function newFileArgs( | ||
value: ToFileInput, | ||
name?: string | null, | ||
options: FilePropertyBag = {}, | ||
): Promise<{ | ||
bits: BlobPart[]; | ||
name: string; | ||
options: FilePropertyBag; | ||
}> { | ||
if (isResponseLike(value)) { | ||
const blob = await value.blob(); | ||
name ||= new URL(value.url).pathname.split(/[\\/]/).pop() ?? 'unknown_file'; | ||
|
||
return { bits: [blob as any], name, options }; | ||
} | ||
|
||
const bits = await getBytes(value); | ||
|
||
name ||= getName(value) ?? 'unknown_file'; | ||
|
||
if (!options.type) { | ||
const type = (bits[0] as any)?.type; | ||
if (typeof type === 'string') { | ||
options = { ...options, type }; | ||
} | ||
} | ||
|
||
return { bits, name, options }; | ||
} | ||
|
||
async function getBytes(value: ToFileInput): Promise<Array<BlobPart>> { | ||
if (value instanceof Promise) return getBytes(await (value as any)); | ||
|
||
let parts: Array<BlobPart> = []; | ||
if ( | ||
typeof value === 'string' || | ||
ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc. | ||
value instanceof ArrayBuffer | ||
) { | ||
parts.push(value); | ||
} else if (isBlobLike(value)) { | ||
parts.push(await value.arrayBuffer()); | ||
} else if ( | ||
isAsyncIterableIterator(value) // includes Readable, ReadableStream, etc. | ||
) { | ||
for await (const chunk of value) { | ||
parts.push(chunk as BlobPart); // TODO, consider validating? | ||
} | ||
} else { | ||
throw new Error( | ||
`Unexpected data type: ${typeof value}; constructor: ${ | ||
value?.constructor?.name | ||
}; props: ${propsForError(value)}`, | ||
); | ||
} | ||
|
||
return parts; | ||
} | ||
|
||
function propsForError(value: any): string { | ||
const props = Object.getOwnPropertyNames(value); | ||
return `[${props.map((p) => `"${p}"`).join(', ')}]`; | ||
} | ||
|
||
function getName(value: any): string | undefined { | ||
return ( | ||
getStringFromMaybeBuffer(value.name) || | ||
getStringFromMaybeBuffer(value.filename) || | ||
// For fs.ReadStream | ||
getStringFromMaybeBuffer(value.path)?.split(/[\\/]/).pop() | ||
); | ||
} | ||
|
||
const getStringFromMaybeBuffer = (x: string | Buffer | unknown): string | undefined => { | ||
if (typeof x === 'string') return x; | ||
if (typeof Buffer !== 'undefined' && x instanceof Buffer) return String(x); | ||
return undefined; | ||
}; | ||
|
||
const isAsyncIterableIterator = (value: any): value is AsyncIterableIterator<unknown> => | ||
value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { File } from 'formdata-node'; | ||
import { type ToFileInput, newFileArgs, type FilePropertyBag } from './newFileArgs'; | ||
import type { Uploadable, BlobPart, FileLike } from './uploadable.node'; // eslint-disable-line | ||
|
||
/** | ||
* Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats | ||
* @param bits the raw content of the file. Can be an {@link Uploadable}, {@link BlobPart}, or {@link AsyncIterable} of {@link BlobPart}s | ||
* @param name the name of the file. If omitted, toFile will try to determine a file name from bits if possible | ||
* @param {Object=} options additional properties | ||
* @param {string=} options.type the MIME type of the content | ||
* @param {number=} options.lastModified the last modified timestamp | ||
* @returns a {@link File} with the given properties | ||
*/ | ||
export async function toFile( | ||
bits: ToFileInput, | ||
name?: string | null | undefined, | ||
options: FilePropertyBag | undefined = {}, | ||
): Promise<FileLike> { | ||
const args = await newFileArgs(bits, name, options); | ||
return new File(args.bits, args.name, args.options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/// <reference lib="dom" />; | ||
|
||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { type ToFileInput, newFileArgs, type FilePropertyBag } from './newFileArgs'; | ||
import type { FileLike, Uploadable } from './uploadable'; // eslint-disable-line | ||
|
||
/** | ||
* Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats | ||
* @param bits the raw content of the file. Can be an {@link Uploadable}, {@link BlobPart}, or {@link AsyncIterable} of {@link BlobPart}s | ||
* @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible | ||
* @param {Object=} options additional properties | ||
* @param {string=} options.type the MIME type of the content | ||
* @param {number=} options.lastModified the last modified timestamp | ||
* @returns a {@link File} with the given properties | ||
*/ | ||
export async function toFile( | ||
bits: ToFileInput, | ||
name?: string | null | undefined, | ||
options: FilePropertyBag | undefined = {}, | ||
): Promise<FileLike> { | ||
const args = await newFileArgs(bits, name, options); | ||
return new File(args.bits as BlobPart[], args.name, args.options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Disclaimer: modules in _shims aren't intended to be imported by SDK users. | ||
*/ | ||
|
||
import { ReadStream } from 'node:fs'; | ||
import { | ||
BlobPart, | ||
Uploadable, | ||
BlobLike, | ||
FileLike, | ||
ResponseLike, | ||
isBlobLike, | ||
isFileLike, | ||
isResponseLike, | ||
} from './uploadable'; | ||
|
||
export { BlobPart, BlobLike, FileLike, ResponseLike, isBlobLike, Uploadable }; | ||
|
||
export const isUploadable = (value: any): value is Uploadable => { | ||
return isFileLike(value) || isResponseLike(value) || value instanceof ReadStream; | ||
}; |
Oops, something went wrong.