core.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. "use strict";
  2. var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
  3. if (kind === "m") throw new TypeError("Private method is not writable");
  4. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
  5. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
  6. return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
  7. };
  8. var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
  9. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
  10. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) 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. var _AbstractPage_client;
  14. Object.defineProperty(exports, "__esModule", { value: true });
  15. exports.isObj = exports.toFloat32Array = exports.toBase64 = exports.getHeader = exports.getRequiredHeader = exports.isHeadersProtocol = exports.isRunningInBrowser = exports.debug = exports.hasOwn = exports.isEmptyObj = exports.maybeCoerceBoolean = exports.maybeCoerceFloat = exports.maybeCoerceInteger = exports.coerceBoolean = exports.coerceFloat = exports.coerceInteger = exports.readEnv = exports.ensurePresent = exports.castToError = exports.sleep = exports.safeJSON = exports.isRequestOptions = exports.createResponseHeaders = exports.PagePromise = exports.AbstractPage = exports.APIClient = exports.APIPromise = exports.createForm = exports.multipartFormRequestOptions = exports.maybeMultipartFormRequestOptions = void 0;
  16. const version_1 = require("./version.js");
  17. const streaming_1 = require("./streaming.js");
  18. const error_1 = require("./error.js");
  19. const index_1 = require("./_shims/index.js");
  20. // try running side effects outside of _shims/index to workaround https://github.com/vercel/next.js/issues/76881
  21. (0, index_1.init)();
  22. const uploads_1 = require("./uploads.js");
  23. var uploads_2 = require("./uploads.js");
  24. Object.defineProperty(exports, "maybeMultipartFormRequestOptions", { enumerable: true, get: function () { return uploads_2.maybeMultipartFormRequestOptions; } });
  25. Object.defineProperty(exports, "multipartFormRequestOptions", { enumerable: true, get: function () { return uploads_2.multipartFormRequestOptions; } });
  26. Object.defineProperty(exports, "createForm", { enumerable: true, get: function () { return uploads_2.createForm; } });
  27. async function defaultParseResponse(props) {
  28. const { response } = props;
  29. if (props.options.stream) {
  30. debug('response', response.status, response.url, response.headers, response.body);
  31. // Note: there is an invariant here that isn't represented in the type system
  32. // that if you set `stream: true` the response type must also be `Stream<T>`
  33. if (props.options.__streamClass) {
  34. return props.options.__streamClass.fromSSEResponse(response, props.controller);
  35. }
  36. return streaming_1.Stream.fromSSEResponse(response, props.controller);
  37. }
  38. // fetch refuses to read the body when the status code is 204.
  39. if (response.status === 204) {
  40. return null;
  41. }
  42. if (props.options.__binaryResponse) {
  43. return response;
  44. }
  45. const contentType = response.headers.get('content-type');
  46. const mediaType = contentType?.split(';')[0]?.trim();
  47. const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
  48. if (isJSON) {
  49. const json = await response.json();
  50. debug('response', response.status, response.url, response.headers, json);
  51. return _addRequestID(json, response);
  52. }
  53. const text = await response.text();
  54. debug('response', response.status, response.url, response.headers, text);
  55. // TODO handle blob, arraybuffer, other content types, etc.
  56. return text;
  57. }
  58. function _addRequestID(value, response) {
  59. if (!value || typeof value !== 'object' || Array.isArray(value)) {
  60. return value;
  61. }
  62. return Object.defineProperty(value, '_request_id', {
  63. value: response.headers.get('x-request-id'),
  64. enumerable: false,
  65. });
  66. }
  67. /**
  68. * A subclass of `Promise` providing additional helper methods
  69. * for interacting with the SDK.
  70. */
  71. class APIPromise extends Promise {
  72. constructor(responsePromise, parseResponse = defaultParseResponse) {
  73. super((resolve) => {
  74. // this is maybe a bit weird but this has to be a no-op to not implicitly
  75. // parse the response body; instead .then, .catch, .finally are overridden
  76. // to parse the response
  77. resolve(null);
  78. });
  79. this.responsePromise = responsePromise;
  80. this.parseResponse = parseResponse;
  81. }
  82. _thenUnwrap(transform) {
  83. return new APIPromise(this.responsePromise, async (props) => _addRequestID(transform(await this.parseResponse(props), props), props.response));
  84. }
  85. /**
  86. * Gets the raw `Response` instance instead of parsing the response
  87. * data.
  88. *
  89. * If you want to parse the response body but still get the `Response`
  90. * instance, you can use {@link withResponse()}.
  91. *
  92. * 👋 Getting the wrong TypeScript type for `Response`?
  93. * Try setting `"moduleResolution": "NodeNext"` if you can,
  94. * or add one of these imports before your first `import … from 'openai'`:
  95. * - `import 'openai/shims/node'` (if you're running on Node)
  96. * - `import 'openai/shims/web'` (otherwise)
  97. */
  98. asResponse() {
  99. return this.responsePromise.then((p) => p.response);
  100. }
  101. /**
  102. * Gets the parsed response data, the raw `Response` instance and the ID of the request,
  103. * returned via the X-Request-ID header which is useful for debugging requests and reporting
  104. * issues to OpenAI.
  105. *
  106. * If you just want to get the raw `Response` instance without parsing it,
  107. * you can use {@link asResponse()}.
  108. *
  109. *
  110. * 👋 Getting the wrong TypeScript type for `Response`?
  111. * Try setting `"moduleResolution": "NodeNext"` if you can,
  112. * or add one of these imports before your first `import … from 'openai'`:
  113. * - `import 'openai/shims/node'` (if you're running on Node)
  114. * - `import 'openai/shims/web'` (otherwise)
  115. */
  116. async withResponse() {
  117. const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
  118. return { data, response, request_id: response.headers.get('x-request-id') };
  119. }
  120. parse() {
  121. if (!this.parsedPromise) {
  122. this.parsedPromise = this.responsePromise.then(this.parseResponse);
  123. }
  124. return this.parsedPromise;
  125. }
  126. then(onfulfilled, onrejected) {
  127. return this.parse().then(onfulfilled, onrejected);
  128. }
  129. catch(onrejected) {
  130. return this.parse().catch(onrejected);
  131. }
  132. finally(onfinally) {
  133. return this.parse().finally(onfinally);
  134. }
  135. }
  136. exports.APIPromise = APIPromise;
  137. class APIClient {
  138. constructor({ baseURL, maxRetries = 2, timeout = 600000, // 10 minutes
  139. httpAgent, fetch: overriddenFetch, }) {
  140. this.baseURL = baseURL;
  141. this.maxRetries = validatePositiveInteger('maxRetries', maxRetries);
  142. this.timeout = validatePositiveInteger('timeout', timeout);
  143. this.httpAgent = httpAgent;
  144. this.fetch = overriddenFetch ?? index_1.fetch;
  145. }
  146. authHeaders(opts) {
  147. return {};
  148. }
  149. /**
  150. * Override this to add your own default headers, for example:
  151. *
  152. * {
  153. * ...super.defaultHeaders(),
  154. * Authorization: 'Bearer 123',
  155. * }
  156. */
  157. defaultHeaders(opts) {
  158. return {
  159. Accept: 'application/json',
  160. 'Content-Type': 'application/json',
  161. 'User-Agent': this.getUserAgent(),
  162. ...getPlatformHeaders(),
  163. ...this.authHeaders(opts),
  164. };
  165. }
  166. /**
  167. * Override this to add your own headers validation:
  168. */
  169. validateHeaders(headers, customHeaders) { }
  170. defaultIdempotencyKey() {
  171. return `stainless-node-retry-${uuid4()}`;
  172. }
  173. get(path, opts) {
  174. return this.methodRequest('get', path, opts);
  175. }
  176. post(path, opts) {
  177. return this.methodRequest('post', path, opts);
  178. }
  179. patch(path, opts) {
  180. return this.methodRequest('patch', path, opts);
  181. }
  182. put(path, opts) {
  183. return this.methodRequest('put', path, opts);
  184. }
  185. delete(path, opts) {
  186. return this.methodRequest('delete', path, opts);
  187. }
  188. methodRequest(method, path, opts) {
  189. return this.request(Promise.resolve(opts).then(async (opts) => {
  190. const body = opts && (0, uploads_1.isBlobLike)(opts?.body) ? new DataView(await opts.body.arrayBuffer())
  191. : opts?.body instanceof DataView ? opts.body
  192. : opts?.body instanceof ArrayBuffer ? new DataView(opts.body)
  193. : opts && ArrayBuffer.isView(opts?.body) ? new DataView(opts.body.buffer)
  194. : opts?.body;
  195. return { method, path, ...opts, body };
  196. }));
  197. }
  198. getAPIList(path, Page, opts) {
  199. return this.requestAPIList(Page, { method: 'get', path, ...opts });
  200. }
  201. calculateContentLength(body) {
  202. if (typeof body === 'string') {
  203. if (typeof Buffer !== 'undefined') {
  204. return Buffer.byteLength(body, 'utf8').toString();
  205. }
  206. if (typeof TextEncoder !== 'undefined') {
  207. const encoder = new TextEncoder();
  208. const encoded = encoder.encode(body);
  209. return encoded.length.toString();
  210. }
  211. }
  212. else if (ArrayBuffer.isView(body)) {
  213. return body.byteLength.toString();
  214. }
  215. return null;
  216. }
  217. buildRequest(inputOptions, { retryCount = 0 } = {}) {
  218. const options = { ...inputOptions };
  219. const { method, path, query, headers: headers = {} } = options;
  220. const body = ArrayBuffer.isView(options.body) || (options.__binaryRequest && typeof options.body === 'string') ?
  221. options.body
  222. : (0, uploads_1.isMultipartBody)(options.body) ? options.body.body
  223. : options.body ? JSON.stringify(options.body, null, 2)
  224. : null;
  225. const contentLength = this.calculateContentLength(body);
  226. const url = this.buildURL(path, query);
  227. if ('timeout' in options)
  228. validatePositiveInteger('timeout', options.timeout);
  229. options.timeout = options.timeout ?? this.timeout;
  230. const httpAgent = options.httpAgent ?? this.httpAgent ?? (0, index_1.getDefaultAgent)(url);
  231. const minAgentTimeout = options.timeout + 1000;
  232. if (typeof httpAgent?.options?.timeout === 'number' &&
  233. minAgentTimeout > (httpAgent.options.timeout ?? 0)) {
  234. // Allow any given request to bump our agent active socket timeout.
  235. // This may seem strange, but leaking active sockets should be rare and not particularly problematic,
  236. // and without mutating agent we would need to create more of them.
  237. // This tradeoff optimizes for performance.
  238. httpAgent.options.timeout = minAgentTimeout;
  239. }
  240. if (this.idempotencyHeader && method !== 'get') {
  241. if (!inputOptions.idempotencyKey)
  242. inputOptions.idempotencyKey = this.defaultIdempotencyKey();
  243. headers[this.idempotencyHeader] = inputOptions.idempotencyKey;
  244. }
  245. const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });
  246. const req = {
  247. method,
  248. ...(body && { body: body }),
  249. headers: reqHeaders,
  250. ...(httpAgent && { agent: httpAgent }),
  251. // @ts-ignore node-fetch uses a custom AbortSignal type that is
  252. // not compatible with standard web types
  253. signal: options.signal ?? null,
  254. };
  255. return { req, url, timeout: options.timeout };
  256. }
  257. buildHeaders({ options, headers, contentLength, retryCount, }) {
  258. const reqHeaders = {};
  259. if (contentLength) {
  260. reqHeaders['content-length'] = contentLength;
  261. }
  262. const defaultHeaders = this.defaultHeaders(options);
  263. applyHeadersMut(reqHeaders, defaultHeaders);
  264. applyHeadersMut(reqHeaders, headers);
  265. // let builtin fetch set the Content-Type for multipart bodies
  266. if ((0, uploads_1.isMultipartBody)(options.body) && index_1.kind !== 'node') {
  267. delete reqHeaders['content-type'];
  268. }
  269. // Don't set theses headers if they were already set or removed through default headers or by the caller.
  270. // We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to account
  271. // for the removal case.
  272. if ((0, exports.getHeader)(defaultHeaders, 'x-stainless-retry-count') === undefined &&
  273. (0, exports.getHeader)(headers, 'x-stainless-retry-count') === undefined) {
  274. reqHeaders['x-stainless-retry-count'] = String(retryCount);
  275. }
  276. if ((0, exports.getHeader)(defaultHeaders, 'x-stainless-timeout') === undefined &&
  277. (0, exports.getHeader)(headers, 'x-stainless-timeout') === undefined &&
  278. options.timeout) {
  279. reqHeaders['x-stainless-timeout'] = String(Math.trunc(options.timeout / 1000));
  280. }
  281. this.validateHeaders(reqHeaders, headers);
  282. return reqHeaders;
  283. }
  284. /**
  285. * Used as a callback for mutating the given `FinalRequestOptions` object.
  286. */
  287. async prepareOptions(options) { }
  288. /**
  289. * Used as a callback for mutating the given `RequestInit` object.
  290. *
  291. * This is useful for cases where you want to add certain headers based off of
  292. * the request properties, e.g. `method` or `url`.
  293. */
  294. async prepareRequest(request, { url, options }) { }
  295. parseHeaders(headers) {
  296. return (!headers ? {}
  297. : Symbol.iterator in headers ?
  298. Object.fromEntries(Array.from(headers).map((header) => [...header]))
  299. : { ...headers });
  300. }
  301. makeStatusError(status, error, message, headers) {
  302. return error_1.APIError.generate(status, error, message, headers);
  303. }
  304. request(options, remainingRetries = null) {
  305. return new APIPromise(this.makeRequest(options, remainingRetries));
  306. }
  307. async makeRequest(optionsInput, retriesRemaining) {
  308. const options = await optionsInput;
  309. const maxRetries = options.maxRetries ?? this.maxRetries;
  310. if (retriesRemaining == null) {
  311. retriesRemaining = maxRetries;
  312. }
  313. await this.prepareOptions(options);
  314. const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
  315. await this.prepareRequest(req, { url, options });
  316. debug('request', url, options, req.headers);
  317. if (options.signal?.aborted) {
  318. throw new error_1.APIUserAbortError();
  319. }
  320. const controller = new AbortController();
  321. const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(exports.castToError);
  322. if (response instanceof Error) {
  323. if (options.signal?.aborted) {
  324. throw new error_1.APIUserAbortError();
  325. }
  326. if (retriesRemaining) {
  327. return this.retryRequest(options, retriesRemaining);
  328. }
  329. if (response.name === 'AbortError') {
  330. throw new error_1.APIConnectionTimeoutError();
  331. }
  332. throw new error_1.APIConnectionError({ cause: response });
  333. }
  334. const responseHeaders = (0, exports.createResponseHeaders)(response.headers);
  335. if (!response.ok) {
  336. if (retriesRemaining && this.shouldRetry(response)) {
  337. const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
  338. debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders);
  339. return this.retryRequest(options, retriesRemaining, responseHeaders);
  340. }
  341. const errText = await response.text().catch((e) => (0, exports.castToError)(e).message);
  342. const errJSON = (0, exports.safeJSON)(errText);
  343. const errMessage = errJSON ? undefined : errText;
  344. const retryMessage = retriesRemaining ? `(error; no more retries left)` : `(error; not retryable)`;
  345. debug(`response (error; ${retryMessage})`, response.status, url, responseHeaders, errMessage);
  346. const err = this.makeStatusError(response.status, errJSON, errMessage, responseHeaders);
  347. throw err;
  348. }
  349. return { response, options, controller };
  350. }
  351. requestAPIList(Page, options) {
  352. const request = this.makeRequest(options, null);
  353. return new PagePromise(this, request, Page);
  354. }
  355. buildURL(path, query) {
  356. const url = isAbsoluteURL(path) ?
  357. new URL(path)
  358. : new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
  359. const defaultQuery = this.defaultQuery();
  360. if (!isEmptyObj(defaultQuery)) {
  361. query = { ...defaultQuery, ...query };
  362. }
  363. if (typeof query === 'object' && query && !Array.isArray(query)) {
  364. url.search = this.stringifyQuery(query);
  365. }
  366. return url.toString();
  367. }
  368. stringifyQuery(query) {
  369. return Object.entries(query)
  370. .filter(([_, value]) => typeof value !== 'undefined')
  371. .map(([key, value]) => {
  372. if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
  373. return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
  374. }
  375. if (value === null) {
  376. return `${encodeURIComponent(key)}=`;
  377. }
  378. throw new error_1.OpenAIError(`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.`);
  379. })
  380. .join('&');
  381. }
  382. async fetchWithTimeout(url, init, ms, controller) {
  383. const { signal, ...options } = init || {};
  384. if (signal)
  385. signal.addEventListener('abort', () => controller.abort());
  386. const timeout = setTimeout(() => controller.abort(), ms);
  387. const fetchOptions = {
  388. signal: controller.signal,
  389. ...options,
  390. };
  391. if (fetchOptions.method) {
  392. // Custom methods like 'patch' need to be uppercased
  393. // See https://github.com/nodejs/undici/issues/2294
  394. fetchOptions.method = fetchOptions.method.toUpperCase();
  395. }
  396. return (
  397. // use undefined this binding; fetch errors if bound to something else in browser/cloudflare
  398. this.fetch.call(undefined, url, fetchOptions).finally(() => {
  399. clearTimeout(timeout);
  400. }));
  401. }
  402. shouldRetry(response) {
  403. // Note this is not a standard header.
  404. const shouldRetryHeader = response.headers.get('x-should-retry');
  405. // If the server explicitly says whether or not to retry, obey.
  406. if (shouldRetryHeader === 'true')
  407. return true;
  408. if (shouldRetryHeader === 'false')
  409. return false;
  410. // Retry on request timeouts.
  411. if (response.status === 408)
  412. return true;
  413. // Retry on lock timeouts.
  414. if (response.status === 409)
  415. return true;
  416. // Retry on rate limits.
  417. if (response.status === 429)
  418. return true;
  419. // Retry internal errors.
  420. if (response.status >= 500)
  421. return true;
  422. return false;
  423. }
  424. async retryRequest(options, retriesRemaining, responseHeaders) {
  425. let timeoutMillis;
  426. // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.
  427. const retryAfterMillisHeader = responseHeaders?.['retry-after-ms'];
  428. if (retryAfterMillisHeader) {
  429. const timeoutMs = parseFloat(retryAfterMillisHeader);
  430. if (!Number.isNaN(timeoutMs)) {
  431. timeoutMillis = timeoutMs;
  432. }
  433. }
  434. // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
  435. const retryAfterHeader = responseHeaders?.['retry-after'];
  436. if (retryAfterHeader && !timeoutMillis) {
  437. const timeoutSeconds = parseFloat(retryAfterHeader);
  438. if (!Number.isNaN(timeoutSeconds)) {
  439. timeoutMillis = timeoutSeconds * 1000;
  440. }
  441. else {
  442. timeoutMillis = Date.parse(retryAfterHeader) - Date.now();
  443. }
  444. }
  445. // If the API asks us to wait a certain amount of time (and it's a reasonable amount),
  446. // just do what it says, but otherwise calculate a default
  447. if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) {
  448. const maxRetries = options.maxRetries ?? this.maxRetries;
  449. timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
  450. }
  451. await (0, exports.sleep)(timeoutMillis);
  452. return this.makeRequest(options, retriesRemaining - 1);
  453. }
  454. calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
  455. const initialRetryDelay = 0.5;
  456. const maxRetryDelay = 8.0;
  457. const numRetries = maxRetries - retriesRemaining;
  458. // Apply exponential backoff, but not more than the max.
  459. const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay);
  460. // Apply some jitter, take up to at most 25 percent of the retry time.
  461. const jitter = 1 - Math.random() * 0.25;
  462. return sleepSeconds * jitter * 1000;
  463. }
  464. getUserAgent() {
  465. return `${this.constructor.name}/JS ${version_1.VERSION}`;
  466. }
  467. }
  468. exports.APIClient = APIClient;
  469. class AbstractPage {
  470. constructor(client, response, body, options) {
  471. _AbstractPage_client.set(this, void 0);
  472. __classPrivateFieldSet(this, _AbstractPage_client, client, "f");
  473. this.options = options;
  474. this.response = response;
  475. this.body = body;
  476. }
  477. hasNextPage() {
  478. const items = this.getPaginatedItems();
  479. if (!items.length)
  480. return false;
  481. return this.nextPageInfo() != null;
  482. }
  483. async getNextPage() {
  484. const nextInfo = this.nextPageInfo();
  485. if (!nextInfo) {
  486. throw new error_1.OpenAIError('No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.');
  487. }
  488. const nextOptions = { ...this.options };
  489. if ('params' in nextInfo && typeof nextOptions.query === 'object') {
  490. nextOptions.query = { ...nextOptions.query, ...nextInfo.params };
  491. }
  492. else if ('url' in nextInfo) {
  493. const params = [...Object.entries(nextOptions.query || {}), ...nextInfo.url.searchParams.entries()];
  494. for (const [key, value] of params) {
  495. nextInfo.url.searchParams.set(key, value);
  496. }
  497. nextOptions.query = undefined;
  498. nextOptions.path = nextInfo.url.toString();
  499. }
  500. return await __classPrivateFieldGet(this, _AbstractPage_client, "f").requestAPIList(this.constructor, nextOptions);
  501. }
  502. async *iterPages() {
  503. // eslint-disable-next-line @typescript-eslint/no-this-alias
  504. let page = this;
  505. yield page;
  506. while (page.hasNextPage()) {
  507. page = await page.getNextPage();
  508. yield page;
  509. }
  510. }
  511. async *[(_AbstractPage_client = new WeakMap(), Symbol.asyncIterator)]() {
  512. for await (const page of this.iterPages()) {
  513. for (const item of page.getPaginatedItems()) {
  514. yield item;
  515. }
  516. }
  517. }
  518. }
  519. exports.AbstractPage = AbstractPage;
  520. /**
  521. * This subclass of Promise will resolve to an instantiated Page once the request completes.
  522. *
  523. * It also implements AsyncIterable to allow auto-paginating iteration on an unawaited list call, eg:
  524. *
  525. * for await (const item of client.items.list()) {
  526. * console.log(item)
  527. * }
  528. */
  529. class PagePromise extends APIPromise {
  530. constructor(client, request, Page) {
  531. super(request, async (props) => new Page(client, props.response, await defaultParseResponse(props), props.options));
  532. }
  533. /**
  534. * Allow auto-paginating iteration on an unawaited list call, eg:
  535. *
  536. * for await (const item of client.items.list()) {
  537. * console.log(item)
  538. * }
  539. */
  540. async *[Symbol.asyncIterator]() {
  541. const page = await this;
  542. for await (const item of page) {
  543. yield item;
  544. }
  545. }
  546. }
  547. exports.PagePromise = PagePromise;
  548. const createResponseHeaders = (headers) => {
  549. return new Proxy(Object.fromEntries(
  550. // @ts-ignore
  551. headers.entries()), {
  552. get(target, name) {
  553. const key = name.toString();
  554. return target[key.toLowerCase()] || target[key];
  555. },
  556. });
  557. };
  558. exports.createResponseHeaders = createResponseHeaders;
  559. // This is required so that we can determine if a given object matches the RequestOptions
  560. // type at runtime. While this requires duplication, it is enforced by the TypeScript
  561. // compiler such that any missing / extraneous keys will cause an error.
  562. const requestOptionsKeys = {
  563. method: true,
  564. path: true,
  565. query: true,
  566. body: true,
  567. headers: true,
  568. maxRetries: true,
  569. stream: true,
  570. timeout: true,
  571. httpAgent: true,
  572. signal: true,
  573. idempotencyKey: true,
  574. __metadata: true,
  575. __binaryRequest: true,
  576. __binaryResponse: true,
  577. __streamClass: true,
  578. };
  579. const isRequestOptions = (obj) => {
  580. return (typeof obj === 'object' &&
  581. obj !== null &&
  582. !isEmptyObj(obj) &&
  583. Object.keys(obj).every((k) => hasOwn(requestOptionsKeys, k)));
  584. };
  585. exports.isRequestOptions = isRequestOptions;
  586. const getPlatformProperties = () => {
  587. if (typeof Deno !== 'undefined' && Deno.build != null) {
  588. return {
  589. 'X-Stainless-Lang': 'js',
  590. 'X-Stainless-Package-Version': version_1.VERSION,
  591. 'X-Stainless-OS': normalizePlatform(Deno.build.os),
  592. 'X-Stainless-Arch': normalizeArch(Deno.build.arch),
  593. 'X-Stainless-Runtime': 'deno',
  594. 'X-Stainless-Runtime-Version': typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown',
  595. };
  596. }
  597. if (typeof EdgeRuntime !== 'undefined') {
  598. return {
  599. 'X-Stainless-Lang': 'js',
  600. 'X-Stainless-Package-Version': version_1.VERSION,
  601. 'X-Stainless-OS': 'Unknown',
  602. 'X-Stainless-Arch': `other:${EdgeRuntime}`,
  603. 'X-Stainless-Runtime': 'edge',
  604. 'X-Stainless-Runtime-Version': process.version,
  605. };
  606. }
  607. // Check if Node.js
  608. if (Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]') {
  609. return {
  610. 'X-Stainless-Lang': 'js',
  611. 'X-Stainless-Package-Version': version_1.VERSION,
  612. 'X-Stainless-OS': normalizePlatform(process.platform),
  613. 'X-Stainless-Arch': normalizeArch(process.arch),
  614. 'X-Stainless-Runtime': 'node',
  615. 'X-Stainless-Runtime-Version': process.version,
  616. };
  617. }
  618. const browserInfo = getBrowserInfo();
  619. if (browserInfo) {
  620. return {
  621. 'X-Stainless-Lang': 'js',
  622. 'X-Stainless-Package-Version': version_1.VERSION,
  623. 'X-Stainless-OS': 'Unknown',
  624. 'X-Stainless-Arch': 'unknown',
  625. 'X-Stainless-Runtime': `browser:${browserInfo.browser}`,
  626. 'X-Stainless-Runtime-Version': browserInfo.version,
  627. };
  628. }
  629. // TODO add support for Cloudflare workers, etc.
  630. return {
  631. 'X-Stainless-Lang': 'js',
  632. 'X-Stainless-Package-Version': version_1.VERSION,
  633. 'X-Stainless-OS': 'Unknown',
  634. 'X-Stainless-Arch': 'unknown',
  635. 'X-Stainless-Runtime': 'unknown',
  636. 'X-Stainless-Runtime-Version': 'unknown',
  637. };
  638. };
  639. // Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts
  640. function getBrowserInfo() {
  641. if (typeof navigator === 'undefined' || !navigator) {
  642. return null;
  643. }
  644. // NOTE: The order matters here!
  645. const browserPatterns = [
  646. { key: 'edge', pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
  647. { key: 'ie', pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
  648. { key: 'ie', pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ },
  649. { key: 'chrome', pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
  650. { key: 'firefox', pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
  651. { key: 'safari', pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ },
  652. ];
  653. // Find the FIRST matching browser
  654. for (const { key, pattern } of browserPatterns) {
  655. const match = pattern.exec(navigator.userAgent);
  656. if (match) {
  657. const major = match[1] || 0;
  658. const minor = match[2] || 0;
  659. const patch = match[3] || 0;
  660. return { browser: key, version: `${major}.${minor}.${patch}` };
  661. }
  662. }
  663. return null;
  664. }
  665. const normalizeArch = (arch) => {
  666. // Node docs:
  667. // - https://nodejs.org/api/process.html#processarch
  668. // Deno docs:
  669. // - https://doc.deno.land/deno/stable/~/Deno.build
  670. if (arch === 'x32')
  671. return 'x32';
  672. if (arch === 'x86_64' || arch === 'x64')
  673. return 'x64';
  674. if (arch === 'arm')
  675. return 'arm';
  676. if (arch === 'aarch64' || arch === 'arm64')
  677. return 'arm64';
  678. if (arch)
  679. return `other:${arch}`;
  680. return 'unknown';
  681. };
  682. const normalizePlatform = (platform) => {
  683. // Node platforms:
  684. // - https://nodejs.org/api/process.html#processplatform
  685. // Deno platforms:
  686. // - https://doc.deno.land/deno/stable/~/Deno.build
  687. // - https://github.com/denoland/deno/issues/14799
  688. platform = platform.toLowerCase();
  689. // NOTE: this iOS check is untested and may not work
  690. // Node does not work natively on IOS, there is a fork at
  691. // https://github.com/nodejs-mobile/nodejs-mobile
  692. // however it is unknown at the time of writing how to detect if it is running
  693. if (platform.includes('ios'))
  694. return 'iOS';
  695. if (platform === 'android')
  696. return 'Android';
  697. if (platform === 'darwin')
  698. return 'MacOS';
  699. if (platform === 'win32')
  700. return 'Windows';
  701. if (platform === 'freebsd')
  702. return 'FreeBSD';
  703. if (platform === 'openbsd')
  704. return 'OpenBSD';
  705. if (platform === 'linux')
  706. return 'Linux';
  707. if (platform)
  708. return `Other:${platform}`;
  709. return 'Unknown';
  710. };
  711. let _platformHeaders;
  712. const getPlatformHeaders = () => {
  713. return (_platformHeaders ?? (_platformHeaders = getPlatformProperties()));
  714. };
  715. const safeJSON = (text) => {
  716. try {
  717. return JSON.parse(text);
  718. }
  719. catch (err) {
  720. return undefined;
  721. }
  722. };
  723. exports.safeJSON = safeJSON;
  724. // https://url.spec.whatwg.org/#url-scheme-string
  725. const startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i;
  726. const isAbsoluteURL = (url) => {
  727. return startsWithSchemeRegexp.test(url);
  728. };
  729. const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  730. exports.sleep = sleep;
  731. const validatePositiveInteger = (name, n) => {
  732. if (typeof n !== 'number' || !Number.isInteger(n)) {
  733. throw new error_1.OpenAIError(`${name} must be an integer`);
  734. }
  735. if (n < 0) {
  736. throw new error_1.OpenAIError(`${name} must be a positive integer`);
  737. }
  738. return n;
  739. };
  740. const castToError = (err) => {
  741. if (err instanceof Error)
  742. return err;
  743. if (typeof err === 'object' && err !== null) {
  744. try {
  745. return new Error(JSON.stringify(err));
  746. }
  747. catch { }
  748. }
  749. return new Error(err);
  750. };
  751. exports.castToError = castToError;
  752. const ensurePresent = (value) => {
  753. if (value == null)
  754. throw new error_1.OpenAIError(`Expected a value to be given but received ${value} instead.`);
  755. return value;
  756. };
  757. exports.ensurePresent = ensurePresent;
  758. /**
  759. * Read an environment variable.
  760. *
  761. * Trims beginning and trailing whitespace.
  762. *
  763. * Will return undefined if the environment variable doesn't exist or cannot be accessed.
  764. */
  765. const readEnv = (env) => {
  766. if (typeof process !== 'undefined') {
  767. return process.env?.[env]?.trim() ?? undefined;
  768. }
  769. if (typeof Deno !== 'undefined') {
  770. return Deno.env?.get?.(env)?.trim();
  771. }
  772. return undefined;
  773. };
  774. exports.readEnv = readEnv;
  775. const coerceInteger = (value) => {
  776. if (typeof value === 'number')
  777. return Math.round(value);
  778. if (typeof value === 'string')
  779. return parseInt(value, 10);
  780. throw new error_1.OpenAIError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
  781. };
  782. exports.coerceInteger = coerceInteger;
  783. const coerceFloat = (value) => {
  784. if (typeof value === 'number')
  785. return value;
  786. if (typeof value === 'string')
  787. return parseFloat(value);
  788. throw new error_1.OpenAIError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
  789. };
  790. exports.coerceFloat = coerceFloat;
  791. const coerceBoolean = (value) => {
  792. if (typeof value === 'boolean')
  793. return value;
  794. if (typeof value === 'string')
  795. return value === 'true';
  796. return Boolean(value);
  797. };
  798. exports.coerceBoolean = coerceBoolean;
  799. const maybeCoerceInteger = (value) => {
  800. if (value === undefined) {
  801. return undefined;
  802. }
  803. return (0, exports.coerceInteger)(value);
  804. };
  805. exports.maybeCoerceInteger = maybeCoerceInteger;
  806. const maybeCoerceFloat = (value) => {
  807. if (value === undefined) {
  808. return undefined;
  809. }
  810. return (0, exports.coerceFloat)(value);
  811. };
  812. exports.maybeCoerceFloat = maybeCoerceFloat;
  813. const maybeCoerceBoolean = (value) => {
  814. if (value === undefined) {
  815. return undefined;
  816. }
  817. return (0, exports.coerceBoolean)(value);
  818. };
  819. exports.maybeCoerceBoolean = maybeCoerceBoolean;
  820. // https://stackoverflow.com/a/34491287
  821. function isEmptyObj(obj) {
  822. if (!obj)
  823. return true;
  824. for (const _k in obj)
  825. return false;
  826. return true;
  827. }
  828. exports.isEmptyObj = isEmptyObj;
  829. // https://eslint.org/docs/latest/rules/no-prototype-builtins
  830. function hasOwn(obj, key) {
  831. return Object.prototype.hasOwnProperty.call(obj, key);
  832. }
  833. exports.hasOwn = hasOwn;
  834. /**
  835. * Copies headers from "newHeaders" onto "targetHeaders",
  836. * using lower-case for all properties,
  837. * ignoring any keys with undefined values,
  838. * and deleting any keys with null values.
  839. */
  840. function applyHeadersMut(targetHeaders, newHeaders) {
  841. for (const k in newHeaders) {
  842. if (!hasOwn(newHeaders, k))
  843. continue;
  844. const lowerKey = k.toLowerCase();
  845. if (!lowerKey)
  846. continue;
  847. const val = newHeaders[k];
  848. if (val === null) {
  849. delete targetHeaders[lowerKey];
  850. }
  851. else if (val !== undefined) {
  852. targetHeaders[lowerKey] = val;
  853. }
  854. }
  855. }
  856. const SENSITIVE_HEADERS = new Set(['authorization', 'api-key']);
  857. function debug(action, ...args) {
  858. if (typeof process !== 'undefined' && process?.env?.['DEBUG'] === 'true') {
  859. const modifiedArgs = args.map((arg) => {
  860. if (!arg) {
  861. return arg;
  862. }
  863. // Check for sensitive headers in request body 'headers' object
  864. if (arg['headers']) {
  865. // clone so we don't mutate
  866. const modifiedArg = { ...arg, headers: { ...arg['headers'] } };
  867. for (const header in arg['headers']) {
  868. if (SENSITIVE_HEADERS.has(header.toLowerCase())) {
  869. modifiedArg['headers'][header] = 'REDACTED';
  870. }
  871. }
  872. return modifiedArg;
  873. }
  874. let modifiedArg = null;
  875. // Check for sensitive headers in headers object
  876. for (const header in arg) {
  877. if (SENSITIVE_HEADERS.has(header.toLowerCase())) {
  878. // avoid making a copy until we need to
  879. modifiedArg ?? (modifiedArg = { ...arg });
  880. modifiedArg[header] = 'REDACTED';
  881. }
  882. }
  883. return modifiedArg ?? arg;
  884. });
  885. console.log(`OpenAI:DEBUG:${action}`, ...modifiedArgs);
  886. }
  887. }
  888. exports.debug = debug;
  889. /**
  890. * https://stackoverflow.com/a/2117523
  891. */
  892. const uuid4 = () => {
  893. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
  894. const r = (Math.random() * 16) | 0;
  895. const v = c === 'x' ? r : (r & 0x3) | 0x8;
  896. return v.toString(16);
  897. });
  898. };
  899. const isRunningInBrowser = () => {
  900. return (
  901. // @ts-ignore
  902. typeof window !== 'undefined' &&
  903. // @ts-ignore
  904. typeof window.document !== 'undefined' &&
  905. // @ts-ignore
  906. typeof navigator !== 'undefined');
  907. };
  908. exports.isRunningInBrowser = isRunningInBrowser;
  909. const isHeadersProtocol = (headers) => {
  910. return typeof headers?.get === 'function';
  911. };
  912. exports.isHeadersProtocol = isHeadersProtocol;
  913. const getRequiredHeader = (headers, header) => {
  914. const foundHeader = (0, exports.getHeader)(headers, header);
  915. if (foundHeader === undefined) {
  916. throw new Error(`Could not find ${header} header`);
  917. }
  918. return foundHeader;
  919. };
  920. exports.getRequiredHeader = getRequiredHeader;
  921. const getHeader = (headers, header) => {
  922. const lowerCasedHeader = header.toLowerCase();
  923. if ((0, exports.isHeadersProtocol)(headers)) {
  924. // to deal with the case where the header looks like Stainless-Event-Id
  925. const intercapsHeader = header[0]?.toUpperCase() +
  926. header.substring(1).replace(/([^\w])(\w)/g, (_m, g1, g2) => g1 + g2.toUpperCase());
  927. for (const key of [header, lowerCasedHeader, header.toUpperCase(), intercapsHeader]) {
  928. const value = headers.get(key);
  929. if (value) {
  930. return value;
  931. }
  932. }
  933. }
  934. for (const [key, value] of Object.entries(headers)) {
  935. if (key.toLowerCase() === lowerCasedHeader) {
  936. if (Array.isArray(value)) {
  937. if (value.length <= 1)
  938. return value[0];
  939. console.warn(`Received ${value.length} entries for the ${header} header, using the first entry.`);
  940. return value[0];
  941. }
  942. return value;
  943. }
  944. }
  945. return undefined;
  946. };
  947. exports.getHeader = getHeader;
  948. /**
  949. * Encodes a string to Base64 format.
  950. */
  951. const toBase64 = (str) => {
  952. if (!str)
  953. return '';
  954. if (typeof Buffer !== 'undefined') {
  955. return Buffer.from(str).toString('base64');
  956. }
  957. if (typeof btoa !== 'undefined') {
  958. return btoa(str);
  959. }
  960. throw new error_1.OpenAIError('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined');
  961. };
  962. exports.toBase64 = toBase64;
  963. /**
  964. * Converts a Base64 encoded string to a Float32Array.
  965. * @param base64Str - The Base64 encoded string.
  966. * @returns An Array of numbers interpreted as Float32 values.
  967. */
  968. const toFloat32Array = (base64Str) => {
  969. if (typeof Buffer !== 'undefined') {
  970. // for Node.js environment
  971. const buf = Buffer.from(base64Str, 'base64');
  972. return Array.from(new Float32Array(buf.buffer, buf.byteOffset, buf.length / Float32Array.BYTES_PER_ELEMENT));
  973. }
  974. else {
  975. // for legacy web platform APIs
  976. const binaryStr = atob(base64Str);
  977. const len = binaryStr.length;
  978. const bytes = new Uint8Array(len);
  979. for (let i = 0; i < len; i++) {
  980. bytes[i] = binaryStr.charCodeAt(i);
  981. }
  982. return Array.from(new Float32Array(bytes.buffer));
  983. }
  984. };
  985. exports.toFloat32Array = toFloat32Array;
  986. function isObj(obj) {
  987. return obj != null && typeof obj === 'object' && !Array.isArray(obj);
  988. }
  989. exports.isObj = isObj;
  990. //# sourceMappingURL=core.js.map