diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 46cb1a0..8429b81 100755 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: f42cb8e6-e2ce-4565-b975-5a9f38b94d5a management: - docChecksum: 0ecb1bbbef6c9805d6a3edcc0afd098a + docChecksum: 8f61558efc4b5f259235cd82bdada98b docVersion: 1.0.51 - speakeasyVersion: 1.422.1 - generationVersion: 2.438.15 - releaseVersion: 0.18.2 - configChecksum: ca25abf7161ef39bf7793f0e79caf68e + speakeasyVersion: 1.434.0 + generationVersion: 2.452.0 + releaseVersion: 0.19.0 + configChecksum: b7c53bc2e1f4357c0716df75b288465b repoURL: https://github.com/Unstructured-IO/unstructured-js-client.git repoSubDirectory: . installationURL: https://github.com/Unstructured-IO/unstructured-js-client @@ -15,10 +15,10 @@ features: typescript: additionalDependencies: 0.1.0 constsAndDefaults: 0.1.11 - core: 3.18.2 + core: 3.18.3 defaultEnabledRetries: 0.1.0 enumUnions: 0.1.0 - envVarSecurityUsage: 0.1.1 + envVarSecurityUsage: 0.1.2 examples: 2.81.4 globalSecurity: 2.82.11 globalSecurityCallbacks: 0.1.0 @@ -28,9 +28,10 @@ features: openEnums: 0.1.1 responseFormat: 0.2.3 retries: 2.83.0 - sdkHooks: 0.1.0 + sdkHooks: 0.2.0 serverIDs: 2.81.2 - unions: 2.85.7 + tests: 0.7.0 + unions: 2.85.8 uploadStreams: 0.1.0 generatedFiles: - .eslintrc.cjs diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 599276d..b45c887 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,9 +1,9 @@ -speakeasyVersion: 1.422.1 +speakeasyVersion: 1.434.0 sources: my-source: sourceNamespace: my-source - sourceRevisionDigest: sha256:7904e30126e3e30e77b56f6d901513e7009cbdc1d433dd7f6baa7da71a31b79a - sourceBlobDigest: sha256:4747157b44baaa540a909f6947d16b08b096845a816819d9b0d35d074561fb68 + sourceRevisionDigest: sha256:86e05e7313dc5802b296e2705f3db7d10470533bd2a42bee265fc9dcb53de154 + sourceBlobDigest: sha256:778eadb59907bc1b92c94743cb32e4cb939c9e64b6546cd33aec0cc2b38ad400 tags: - latest - main @@ -11,8 +11,8 @@ targets: unstructed-typescript: source: my-source sourceNamespace: my-source - sourceRevisionDigest: sha256:7904e30126e3e30e77b56f6d901513e7009cbdc1d433dd7f6baa7da71a31b79a - sourceBlobDigest: sha256:4747157b44baaa540a909f6947d16b08b096845a816819d9b0d35d074561fb68 + sourceRevisionDigest: sha256:86e05e7313dc5802b296e2705f3db7d10470533bd2a42bee265fc9dcb53de154 + sourceBlobDigest: sha256:778eadb59907bc1b92c94743cb32e4cb939c9e64b6546cd33aec0cc2b38ad400 workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/RELEASES.md b/RELEASES.md index 96ee920..04a9cc0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -504,4 +504,14 @@ Based on: ### Generated - [typescript v0.18.2] . ### Releases -- [NPM v0.18.2] https://www.npmjs.com/package/unstructured-client/v/0.18.2 - . \ No newline at end of file +- [NPM v0.18.2] https://www.npmjs.com/package/unstructured-client/v/0.18.2 - . + +## 2024-11-07 00:27:00 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.434.0 (2.452.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [typescript v0.19.0] . +### Releases +- [NPM v0.19.0] https://www.npmjs.com/package/unstructured-client/v/0.19.0 - . \ No newline at end of file diff --git a/gen.yaml b/gen.yaml index 81c95b8..0b660a3 100644 --- a/gen.yaml +++ b/gen.yaml @@ -9,8 +9,9 @@ generation: requestResponseComponentNamesFeb2024: false auth: oAuth2ClientCredentialsEnabled: false + oAuth2PasswordEnabled: false typescript: - version: 0.18.2 + version: 0.19.0 additionalDependencies: dependencies: async: ^3.2.5 diff --git a/jsr.json b/jsr.json index d7e408d..bfa1ace 100644 --- a/jsr.json +++ b/jsr.json @@ -2,7 +2,7 @@ { "name": "unstructured-client", - "version": "0.18.2", + "version": "0.19.0", "exports": { ".": "./src/index.ts", "./sdk/models/errors": "./src/sdk/models/errors/index.ts", diff --git a/package-lock.json b/package-lock.json index 05cf794..cc79569 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "unstructured-client", - "version": "0.18.2", + "version": "0.19.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "unstructured-client", - "version": "0.18.2", + "version": "0.19.0", "dependencies": { "async": "^3.2.5", "pdf-lib": "^1.17.1" diff --git a/package.json b/package.json index 4c243d2..f5695c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unstructured-client", - "version": "0.18.2", + "version": "0.19.0", "author": "Unstructured", "main": "./index.js", "sideEffects": false, diff --git a/src/funcs/generalPartition.ts b/src/funcs/generalPartition.ts index fb40638..bef2bfc 100644 --- a/src/funcs/generalPartition.ts +++ b/src/funcs/generalPartition.ts @@ -255,12 +255,27 @@ export async function generalPartition( }); const securityInput = await extractSecurity(client._options.security); + const requestSecurity = resolveGlobalSecurity(securityInput); + const context = { operationID: "partition", oAuth2Scopes: [], securitySource: client._options.security, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 3000, + maxInterval: 720000, + exponent: 1.88, + maxElapsedTime: 1800000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["502", "503", "504"], }; - const requestSecurity = resolveGlobalSecurity(securityInput); const requestRes = client._createRequest(context, { security: requestSecurity, @@ -278,19 +293,8 @@ export async function generalPartition( const doResult = await client._do(req, { context, errorCodes: ["422", "4XX", "5XX"], - retryConfig: options?.retries - || client._options.retryConfig - || { - strategy: "backoff", - backoff: { - initialInterval: 3000, - maxInterval: 720000, - exponent: 1.88, - maxElapsedTime: 1800000, - }, - retryConnectionErrors: true, - }, - retryCodes: options?.retryCodes || ["502", "503", "504"], + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, }); if (!doResult.ok) { return doResult; diff --git a/src/hooks/types.ts b/src/hooks/types.ts index 656b9e3..4079cc9 100644 --- a/src/hooks/types.ts +++ b/src/hooks/types.ts @@ -3,11 +3,13 @@ */ import { HTTPClient, RequestInput } from "../lib/http.js"; +import { RetryConfig } from "../lib/retries.js"; export type HookContext = { operationID: string; oAuth2Scopes?: string[]; securitySource?: any | (() => Promise); + retryConfig: RetryConfig; }; export type Awaitable = T | Promise; diff --git a/src/lib/config.ts b/src/lib/config.ts index 0b5354a..bcf607d 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -69,8 +69,8 @@ export function serverURLFromOptions(options: SDKOptions): URL | null { export const SDK_METADATA = { language: "typescript", openapiDocVersion: "1.0.51", - sdkVersion: "0.18.2", - genVersion: "2.438.15", + sdkVersion: "0.19.0", + genVersion: "2.452.0", userAgent: - "speakeasy-sdk/typescript 0.18.2 2.438.15 1.0.51 unstructured-client", + "speakeasy-sdk/typescript 0.19.0 2.452.0 1.0.51 unstructured-client", } as const; diff --git a/src/lib/retries.ts b/src/lib/retries.ts index df3e0bc..93ebc8d 100644 --- a/src/lib/retries.ts +++ b/src/lib/retries.ts @@ -26,23 +26,44 @@ export type RetryConfig = retryConnectionErrors?: boolean; }; -class PermanentError extends Error { - inner: unknown; +/** + * PermanentError is an error that is not recoverable. Throwing this error will + * cause a retry loop to terminate. + */ +export class PermanentError extends Error { + /** The underlying cause of the error. */ + override readonly cause: unknown; + + constructor(message: string, options?: { cause?: unknown }) { + let msg = message; + if (options?.cause) { + msg += `: ${options.cause}`; + } - constructor(inner: unknown) { - super("Permanent error"); - this.inner = inner; + super(msg, options); + this.name = "PermanentError"; + // In older runtimes, the cause field would not have been assigned through + // the super() call. + if (typeof this.cause === "undefined") { + this.cause = options?.cause; + } Object.setPrototypeOf(this, PermanentError.prototype); } } -class TemporaryError extends Error { - res: Response; +/** + * TemporaryError is an error is used to signal that an HTTP request can be + * retried as part of a retry loop. If retry attempts are exhausted and this + * error is thrown, the response will be returned to the caller. + */ +export class TemporaryError extends Error { + response: Response; - constructor(res: Response) { - super("Temporary error"); - this.res = res; + constructor(message: string, response: Response) { + super(message); + this.response = response; + this.name = "TemporaryError"; Object.setPrototypeOf(this, TemporaryError.prototype); } @@ -80,11 +101,14 @@ function wrapFetcher( try { const res = await fn(); if (isRetryableResponse(res, options.statusCodes)) { - throw new TemporaryError(res); + throw new TemporaryError( + "Response failed with retryable status code", + res, + ); } return res; - } catch (err) { + } catch (err: unknown) { if (err instanceof TemporaryError) { throw err; } @@ -96,7 +120,7 @@ function wrapFetcher( throw err; } - throw new PermanentError(err); + throw new PermanentError("Permanent error", { cause: err }); } }; } @@ -139,37 +163,25 @@ async function retryBackoff( try { const res = await fn(); return res; - } catch (err) { + } catch (err: unknown) { if (err instanceof PermanentError) { - throw err.inner; + throw err.cause; } const elapsed = Date.now() - start; if (elapsed > maxElapsedTime) { if (err instanceof TemporaryError) { - return err.res; + return err.response; } throw err; } let retryInterval = 0; - if (err instanceof TemporaryError && err.res && err.res.headers) { - const retryVal = err.res.headers.get("retry-after") || ""; - if (retryVal != "") { - const parsedNumber = Number(retryVal); - if (!isNaN(parsedNumber) && Number.isInteger(parsedNumber)) { - retryInterval = parsedNumber * 1000; - } else { - const parsedDate = Date.parse(retryVal); - if (!isNaN(parsedDate)) { - const deltaMS = parsedDate - Date.now(); - retryInterval = deltaMS > 0 ? Math.ceil(deltaMS) : 0; - } - } - } + if (err instanceof TemporaryError) { + retryInterval = retryIntervalFromResponse(err.response); } - if (retryInterval == 0) { + if (retryInterval <= 0) { retryInterval = initialInterval * Math.pow(x, exponent) + Math.random() * 1000; } @@ -182,6 +194,26 @@ async function retryBackoff( } } +function retryIntervalFromResponse(res: Response): number { + const retryVal = res.headers.get("retry-after") || ""; + if (!retryVal) { + return 0; + } + + const parsedNumber = Number(retryVal); + if (Number.isInteger(parsedNumber)) { + return parsedNumber * 1000; + } + + const parsedDate = Date.parse(retryVal); + if (Number.isInteger(parsedDate)) { + const deltaMS = parsedDate - Date.now(); + return deltaMS > 0 ? Math.ceil(deltaMS) : 0; + } + + return 0; +} + async function delay(delay: number): Promise { return new Promise((resolve) => setTimeout(resolve, delay)); } diff --git a/src/lib/sdks.ts b/src/lib/sdks.ts index bfbe7a5..14e2d93 100644 --- a/src/lib/sdks.ts +++ b/src/lib/sdks.ts @@ -220,8 +220,8 @@ export class ClientSDK { options: { context: HookContext; errorCodes: number | string | (number | string)[]; - retryConfig?: RetryConfig | undefined; - retryCodes?: string[] | undefined; + retryConfig: RetryConfig; + retryCodes: string[]; }, ): Promise< Result< @@ -233,8 +233,6 @@ export class ClientSDK { > > { const { context, errorCodes } = options; - const retryConfig = options.retryConfig || { strategy: "none" }; - const retryCodes = options.retryCodes || []; return retry( async () => { @@ -245,22 +243,28 @@ export class ClientSDK { let response = await this.#httpClient.request(req); - if (matchStatusCode(response, errorCodes)) { - const result = await this.#hooks.afterError(context, response, null); - if (result.error) { - throw result.error; + try { + if (matchStatusCode(response, errorCodes)) { + const result = await this.#hooks.afterError( + context, + response, + null, + ); + if (result.error) { + throw result.error; + } + response = result.response || response; + } else { + response = await this.#hooks.afterSuccess(context, response); } - response = result.response || response; - } else { - response = await this.#hooks.afterSuccess(context, response); + } finally { + await logResponse(this.#logger, response, req) + .catch(e => this.#logger?.log("Failed to log response:", e)); } - await logResponse(this.#logger, response, req) - .catch(e => this.#logger?.log("Failed to log response:", e)); - return response; }, - { config: retryConfig, statusCodes: retryCodes }, + { config: options.retryConfig, statusCodes: options.retryCodes }, ).then( (r) => OK(r), (err) => {