diff --git a/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts b/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts index 2d572c75324f..d4c3cd0fa394 100644 --- a/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts +++ b/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts @@ -58,9 +58,6 @@ export const TypescriptCustomConfigSchema = z.strictObject({ useBrandedStringAliases: z.optional(z.boolean()), offsetSemantics: z.optional(z.enum(["item-index", "page-index"])), - // OAuth token override configuration - oauthTokenOverride: z.optional(z.boolean()), - // beta (not in docs) includeContentHeadersOnFileDownloadResponse: z.optional(z.boolean()), includeUtilsOnUnionMembers: z.optional(z.boolean()), diff --git a/generators/typescript/sdk/cli/src/SdkGeneratorCli.ts b/generators/typescript/sdk/cli/src/SdkGeneratorCli.ts index c4fa77f3447b..c23554550293 100644 --- a/generators/typescript/sdk/cli/src/SdkGeneratorCli.ts +++ b/generators/typescript/sdk/cli/src/SdkGeneratorCli.ts @@ -96,8 +96,7 @@ export class SdkGeneratorCli extends AbstractGeneratorCli { linter: parsed?.linter ?? "biome", formatter: parsed?.formatter ?? "biome", generateSubpackageExports: parsed?.generateSubpackageExports ?? false, - offsetSemantics: parsed?.offsetSemantics ?? "item-index", - oauthTokenOverride: parsed?.oauthTokenOverride ?? false + offsetSemantics: parsed?.offsetSemantics ?? "item-index" }; if (parsed?.noSerdeLayer === false && typeof parsed?.enableInlineTypes === "undefined") { @@ -243,8 +242,7 @@ export class SdkGeneratorCli extends AbstractGeneratorCli { formatter: customConfig.formatter, linter: customConfig.linter, generateSubpackageExports: customConfig.generateSubpackageExports ?? false, - offsetSemantics: customConfig.offsetSemantics, - oauthTokenOverride: customConfig.oauthTokenOverride ?? false + offsetSemantics: customConfig.offsetSemantics } }); const typescriptProject = await sdkGenerator.generate(); diff --git a/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts b/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts index 88726928a5e5..2e6803490b99 100644 --- a/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts +++ b/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts @@ -60,5 +60,4 @@ export interface SdkCustomConfig { parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; generateSubpackageExports: boolean | undefined; offsetSemantics: "item-index" | "page-index"; - oauthTokenOverride: boolean | undefined; } diff --git a/generators/typescript/sdk/client-class-generator/src/AuthProvidersGenerator.ts b/generators/typescript/sdk/client-class-generator/src/AuthProvidersGenerator.ts index 7e60e00089e0..351f8e3fc9f5 100644 --- a/generators/typescript/sdk/client-class-generator/src/AuthProvidersGenerator.ts +++ b/generators/typescript/sdk/client-class-generator/src/AuthProvidersGenerator.ts @@ -19,19 +19,12 @@ export declare namespace AuthProvidersGenerator { authScheme: AuthScheme | { type: "any" }; neverThrowErrors: boolean; includeSerdeLayer: boolean; - oauthTokenOverride: boolean; } } export class AuthProvidersGenerator implements GeneratedFile { private readonly authProviderGenerator: AuthProviderGenerator | undefined; - constructor({ - ir, - authScheme, - neverThrowErrors, - includeSerdeLayer, - oauthTokenOverride - }: AuthProvidersGenerator.Init) { + constructor({ ir, authScheme, neverThrowErrors, includeSerdeLayer }: AuthProvidersGenerator.Init) { this.authProviderGenerator = (() => { switch (authScheme.type) { case "any": @@ -64,8 +57,7 @@ export class AuthProvidersGenerator implements GeneratedFile { ir, authScheme, neverThrowErrors, - includeSerdeLayer, - oauthTokenOverride + includeSerdeLayer }); default: assertNever(authScheme); diff --git a/generators/typescript/sdk/client-class-generator/src/BaseClientTypeGenerator.ts b/generators/typescript/sdk/client-class-generator/src/BaseClientTypeGenerator.ts index c4508121c173..74a5aa9a5b9a 100644 --- a/generators/typescript/sdk/client-class-generator/src/BaseClientTypeGenerator.ts +++ b/generators/typescript/sdk/client-class-generator/src/BaseClientTypeGenerator.ts @@ -10,7 +10,6 @@ export declare namespace BaseClientTypeGenerator { generateIdempotentRequestOptions: boolean; ir: FernIr.IntermediateRepresentation; omitFernHeaders: boolean; - oauthTokenOverride: boolean; } } @@ -21,18 +20,11 @@ export class BaseClientTypeGenerator { private readonly generateIdempotentRequestOptions: boolean; private readonly ir: FernIr.IntermediateRepresentation; private readonly omitFernHeaders: boolean; - private readonly oauthTokenOverride: boolean; - - constructor({ - generateIdempotentRequestOptions, - ir, - omitFernHeaders, - oauthTokenOverride - }: BaseClientTypeGenerator.Init) { + + constructor({ generateIdempotentRequestOptions, ir, omitFernHeaders }: BaseClientTypeGenerator.Init) { this.generateIdempotentRequestOptions = generateIdempotentRequestOptions; this.ir = ir; this.omitFernHeaders = omitFernHeaders; - this.oauthTokenOverride = oauthTokenOverride; } public writeToFile(context: SdkContext): void { @@ -281,10 +273,9 @@ export type NormalizedClientOptionsWithAuth = Norma namedImports: ["OAuthAuthProvider"] }); providerImports.push("OAuthAuthProvider"); - const oauthCreation = this.oauthTokenOverride - ? "if (OAuthAuthProvider.canCreate(normalizedWithNoOpAuthProvider)) { authProviders.push(OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider)); }" - : "if (OAuthAuthProvider.canCreate(normalizedWithNoOpAuthProvider)) { authProviders.push(new OAuthAuthProvider(normalizedWithNoOpAuthProvider)); }"; - providerInstantiations.push(oauthCreation); + providerInstantiations.push( + "if (OAuthAuthProvider.canCreate(normalizedWithNoOpAuthProvider)) { authProviders.push(OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider)); }" + ); } } @@ -321,9 +312,7 @@ export type NormalizedClientOptionsWithAuth = Norma moduleSpecifier: "./auth/OAuthAuthProvider.js", namedImports: ["OAuthAuthProvider"] }); - authProviderCreation = this.oauthTokenOverride - ? "OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider)" - : "new OAuthAuthProvider(normalizedWithNoOpAuthProvider)"; + authProviderCreation = "OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider)"; break; } else if (authScheme.type === "inferred") { context.sourceFile.addImportDeclaration({ diff --git a/generators/typescript/sdk/client-class-generator/src/GeneratedSdkClientClassImpl.ts b/generators/typescript/sdk/client-class-generator/src/GeneratedSdkClientClassImpl.ts index 3cfd68739536..8c0faa3e309a 100644 --- a/generators/typescript/sdk/client-class-generator/src/GeneratedSdkClientClassImpl.ts +++ b/generators/typescript/sdk/client-class-generator/src/GeneratedSdkClientClassImpl.ts @@ -93,7 +93,6 @@ export declare namespace GeneratedSdkClientClassImpl { generateEndpointMetadata: boolean; parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; offsetSemantics: "item-index" | "page-index"; - oauthTokenOverride: boolean; } } @@ -137,7 +136,6 @@ export class GeneratedSdkClientClassImpl implements GeneratedSdkClientClass { private readonly anyEndpointWithAuth: boolean; private readonly generateEndpointMetadata: boolean; private readonly offsetSemantics: "item-index" | "page-index"; - private readonly oauthTokenOverride: boolean; constructor({ isRoot, @@ -164,8 +162,7 @@ export class GeneratedSdkClientClassImpl implements GeneratedSdkClientClass { formDataSupport, generateEndpointMetadata, parameterNaming, - offsetSemantics, - oauthTokenOverride + offsetSemantics }: GeneratedSdkClientClassImpl.Init) { this.isRoot = isRoot; this.intermediateRepresentation = intermediateRepresentation; @@ -185,7 +182,6 @@ export class GeneratedSdkClientClassImpl implements GeneratedSdkClientClass { this.generateEndpointMetadata = generateEndpointMetadata; this.parameterNaming = parameterNaming; this.offsetSemantics = offsetSemantics; - this.oauthTokenOverride = oauthTokenOverride; const package_ = packageResolver.resolvePackage(packageId); this.package_ = package_; diff --git a/generators/typescript/sdk/client-class-generator/src/SdkClientClassGenerator.ts b/generators/typescript/sdk/client-class-generator/src/SdkClientClassGenerator.ts index 8df6c9c6c495..558345cb8f34 100644 --- a/generators/typescript/sdk/client-class-generator/src/SdkClientClassGenerator.ts +++ b/generators/typescript/sdk/client-class-generator/src/SdkClientClassGenerator.ts @@ -31,7 +31,6 @@ export declare namespace SdkClientClassGenerator { generateEndpointMetadata: boolean; parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; offsetSemantics: "item-index" | "page-index"; - oauthTokenOverride: boolean; } export namespace generateService { @@ -69,7 +68,6 @@ export class SdkClientClassGenerator { private readonly generateEndpointMetadata: boolean; private readonly parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; private readonly offsetSemantics: "item-index" | "page-index"; - private readonly oauthTokenOverride: boolean; constructor({ intermediateRepresentation, @@ -95,8 +93,7 @@ export class SdkClientClassGenerator { useDefaultRequestParameterValues, generateEndpointMetadata, parameterNaming, - offsetSemantics, - oauthTokenOverride + offsetSemantics }: SdkClientClassGenerator.Init) { this.intermediateRepresentation = intermediateRepresentation; this.errorResolver = errorResolver; @@ -122,7 +119,6 @@ export class SdkClientClassGenerator { this.generateEndpointMetadata = generateEndpointMetadata; this.parameterNaming = parameterNaming; this.offsetSemantics = offsetSemantics; - this.oauthTokenOverride = oauthTokenOverride; } public generateService({ @@ -158,8 +154,7 @@ export class SdkClientClassGenerator { useDefaultRequestParameterValues: this.useDefaultRequestParameterValues, generateEndpointMetadata: this.generateEndpointMetadata, parameterNaming: this.parameterNaming, - offsetSemantics: this.offsetSemantics, - oauthTokenOverride: this.oauthTokenOverride + offsetSemantics: this.offsetSemantics }); } } diff --git a/generators/typescript/sdk/client-class-generator/src/auth-provider/OAuthAuthProviderGenerator.ts b/generators/typescript/sdk/client-class-generator/src/auth-provider/OAuthAuthProviderGenerator.ts index e2df6dd22d58..f0e0f1bd0986 100644 --- a/generators/typescript/sdk/client-class-generator/src/auth-provider/OAuthAuthProviderGenerator.ts +++ b/generators/typescript/sdk/client-class-generator/src/auth-provider/OAuthAuthProviderGenerator.ts @@ -11,7 +11,6 @@ export declare namespace OAuthAuthProviderGenerator { authScheme: FernIr.OAuthScheme; neverThrowErrors: boolean; includeSerdeLayer: boolean; - oauthTokenOverride: boolean; } } @@ -42,14 +41,12 @@ export class OAuthAuthProviderGenerator implements AuthProviderGenerator { private readonly authScheme: FernIr.OAuthScheme; private readonly neverThrowErrors: boolean; private readonly includeSerdeLayer: boolean; - private readonly oauthTokenOverride: boolean; constructor(init: OAuthAuthProviderGenerator.Init) { this.ir = init.ir; this.authScheme = init.authScheme; this.neverThrowErrors = init.neverThrowErrors; this.includeSerdeLayer = init.includeSerdeLayer; - this.oauthTokenOverride = init.oauthTokenOverride; } public getFilePath(): ExportedFilePath { @@ -134,7 +131,7 @@ export class OAuthAuthProviderGenerator implements AuthProviderGenerator { } public instantiate(constructorArgs: ts.Expression[]): ts.Expression { - const hasTokenOverride = this.oauthTokenOverride; + const hasTokenOverride = true; if (hasTokenOverride) { return ts.factory.createCallExpression( ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(CLASS_NAME), "createInstance"), @@ -146,7 +143,7 @@ export class OAuthAuthProviderGenerator implements AuthProviderGenerator { } public writeToFile(context: SdkContext): void { - const hasTokenOverride = this.oauthTokenOverride; + const hasTokenOverride = true; this.writeClass(context); if (hasTokenOverride) { this.writeTokenOverrideClass(context); @@ -219,7 +216,7 @@ export class OAuthAuthProviderGenerator implements AuthProviderGenerator { context.genericAPISdkError.getReferenceToGenericAPISdkError().getExpression() ); - const hasTokenOverride = this.oauthTokenOverride; + const hasTokenOverride = true; const constructorOptionsType = hasTokenOverride ? `${CLASS_NAME}.${OPTIONS_TYPE_NAME} & ${CLASS_NAME}.AuthOptions.ClientCredentials` @@ -800,7 +797,7 @@ export class OAuthAuthProviderGenerator implements AuthProviderGenerator { return; } - const hasTokenOverride = this.oauthTokenOverride; + const hasTokenOverride = true; // Import BaseClientOptions for Options to extend // OAuthAuthProvider.Options needs to extend BaseClientOptions because it creates an AuthClient diff --git a/generators/typescript/sdk/generator/src/SdkGenerator.ts b/generators/typescript/sdk/generator/src/SdkGenerator.ts index 6563ce5a7435..bac7c240f804 100644 --- a/generators/typescript/sdk/generator/src/SdkGenerator.ts +++ b/generators/typescript/sdk/generator/src/SdkGenerator.ts @@ -160,7 +160,6 @@ export declare namespace SdkGenerator { formatter: "prettier" | "biome" | "oxfmt"; generateSubpackageExports: boolean; offsetSemantics: "item-index" | "page-index"; - oauthTokenOverride: boolean; } } @@ -378,8 +377,7 @@ export class SdkGenerator { requireDefaultEnvironment: config.requireDefaultEnvironment, retainOriginalCasing: config.retainOriginalCasing, parameterNaming: config.parameterNaming, - baseClientTypeDeclarationReferencer: this.baseClientTypeDeclarationReferencer, - oauthTokenOverride: config.oauthTokenOverride + baseClientTypeDeclarationReferencer: this.baseClientTypeDeclarationReferencer }); this.genericAPISdkErrorDeclarationReferencer = new GenericAPISdkErrorDeclarationReferencer({ containingDirectory: [], @@ -473,14 +471,12 @@ export class SdkGenerator { useDefaultRequestParameterValues: config.useDefaultRequestParameterValues, generateEndpointMetadata: config.generateEndpointMetadata, parameterNaming: config.parameterNaming, - offsetSemantics: config.offsetSemantics, - oauthTokenOverride: config.oauthTokenOverride + offsetSemantics: config.offsetSemantics }); this.baseClientTypeGenerator = new BaseClientTypeGenerator({ ir: intermediateRepresentation, generateIdempotentRequestOptions: this.hasIdempotentEndpoints(), - omitFernHeaders: config.omitFernHeaders, - oauthTokenOverride: config.oauthTokenOverride + omitFernHeaders: config.omitFernHeaders }); this.websocketGenerator = new WebsocketClassGenerator({ intermediateRepresentation, @@ -528,8 +524,7 @@ export class SdkGenerator { endpointSnippets: this.endpointSnippets, fileResponseType: this.config.fileResponseType, fetchSupport: this.config.fetchSupport, - generateSubpackageExports: this.config.generateSubpackageExports, - oauthTokenOverride: this.config.oauthTokenOverride + generateSubpackageExports: this.config.generateSubpackageExports }), ir: intermediateRepresentation }); @@ -1396,8 +1391,7 @@ export class SdkGenerator { ir: this.intermediateRepresentation, authScheme, neverThrowErrors: this.config.neverThrowErrors, - includeSerdeLayer: this.config.includeSerdeLayer, - oauthTokenOverride: this.config.oauthTokenOverride + includeSerdeLayer: this.config.includeSerdeLayer }); if (!authProvidersGenerator.shouldWriteFile()) { continue; @@ -1416,8 +1410,7 @@ export class SdkGenerator { ir: this.intermediateRepresentation, authScheme: { type: "any" }, neverThrowErrors: this.config.neverThrowErrors, - includeSerdeLayer: this.config.includeSerdeLayer, - oauthTokenOverride: this.config.oauthTokenOverride + includeSerdeLayer: this.config.includeSerdeLayer }); this.withSourceFile({ filepath: anyAuthProvidersGenerator.getFilePath(), @@ -1433,8 +1426,7 @@ export class SdkGenerator { ir: this.intermediateRepresentation, authScheme, neverThrowErrors: this.config.neverThrowErrors, - includeSerdeLayer: this.config.includeSerdeLayer, - oauthTokenOverride: this.config.oauthTokenOverride + includeSerdeLayer: this.config.includeSerdeLayer }); if (!authProvidersGenerator.shouldWriteFile()) { continue; diff --git a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts index 0f811bb8d2a4..eb6351d2c992 100644 --- a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts +++ b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts @@ -22,7 +22,6 @@ export declare namespace BaseClientContextImpl { generateIdempotentRequestOptions: boolean; parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; baseClientTypeDeclarationReferencer: BaseClientTypeDeclarationReferencer; - oauthTokenOverride: boolean; } } const OPTIONS_INTERFACE_NAME = "BaseClientOptions"; @@ -43,7 +42,6 @@ export class BaseClientContextImpl implements BaseClientContext { private readonly parameterNaming: "originalName" | "wireValue" | "camelCase" | "snakeCase" | "default"; private readonly generateIdempotentRequestOptions: boolean; private readonly baseClientTypeDeclarationReferencer: BaseClientTypeDeclarationReferencer; - private readonly oauthTokenOverride: boolean; public static readonly OPTIONS_INTERFACE_NAME = OPTIONS_INTERFACE_NAME; @@ -68,8 +66,7 @@ export class BaseClientContextImpl implements BaseClientContext { retainOriginalCasing, generateIdempotentRequestOptions, parameterNaming, - baseClientTypeDeclarationReferencer, - oauthTokenOverride + baseClientTypeDeclarationReferencer }: BaseClientContextImpl.Init) { this.intermediateRepresentation = intermediateRepresentation; this.allowCustomFetcher = allowCustomFetcher; @@ -78,7 +75,6 @@ export class BaseClientContextImpl implements BaseClientContext { this.generateIdempotentRequestOptions = generateIdempotentRequestOptions; this.parameterNaming = parameterNaming; this.baseClientTypeDeclarationReferencer = baseClientTypeDeclarationReferencer; - this.oauthTokenOverride = oauthTokenOverride; this.authHeaders = []; for (const authScheme of intermediateRepresentation.auth.schemes) { diff --git a/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts b/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts index 84000beb8351..f0ed89d37d98 100644 --- a/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts +++ b/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts @@ -15,26 +15,22 @@ export class ReadmeConfigBuilder { private readonly fileResponseType: "stream" | "binary-response"; private readonly fetchSupport: "node-fetch" | "native"; private readonly generateSubpackageExports: boolean; - private readonly oauthTokenOverride: boolean; constructor({ endpointSnippets, fileResponseType, fetchSupport, - generateSubpackageExports, - oauthTokenOverride + generateSubpackageExports }: { endpointSnippets: FernGeneratorExec.Endpoint[]; fileResponseType: "stream" | "binary-response"; fetchSupport: "node-fetch" | "native"; generateSubpackageExports: boolean; - oauthTokenOverride: boolean; }) { this.endpointSnippets = endpointSnippets; this.fileResponseType = fileResponseType; this.fetchSupport = fetchSupport; this.generateSubpackageExports = generateSubpackageExports; - this.oauthTokenOverride = oauthTokenOverride; } public build({ @@ -50,8 +46,7 @@ export class ReadmeConfigBuilder { context, endpointSnippets: this.endpointSnippets, fileResponseType: this.fileResponseType, - generateSubpackageExports: this.generateSubpackageExports, - oauthTokenOverride: this.oauthTokenOverride + generateSubpackageExports: this.generateSubpackageExports }); const snippets = readmeSnippetBuilder.buildReadmeSnippets(); const addendums = readmeSnippetBuilder.buildReadmeAddendums(); diff --git a/generators/typescript/sdk/generator/src/readme/ReadmeSnippetBuilder.ts b/generators/typescript/sdk/generator/src/readme/ReadmeSnippetBuilder.ts index d5939eeea745..4c5a3701402e 100644 --- a/generators/typescript/sdk/generator/src/readme/ReadmeSnippetBuilder.ts +++ b/generators/typescript/sdk/generator/src/readme/ReadmeSnippetBuilder.ts @@ -41,7 +41,6 @@ export class ReadmeSnippetBuilder extends AbstractReadmeSnippetBuilder { private readonly context: SdkContext; private readonly isPaginationEnabled: boolean; private readonly generateSubpackageExports: boolean; - private readonly oauthTokenOverride: boolean; private readonly endpoints: Record = {}; private readonly snippets: Record = {}; private readonly defaultEndpointId: EndpointId; @@ -55,21 +54,18 @@ export class ReadmeSnippetBuilder extends AbstractReadmeSnippetBuilder { context, endpointSnippets, fileResponseType, - generateSubpackageExports, - oauthTokenOverride + generateSubpackageExports }: { context: SdkContext; endpointSnippets: FernGeneratorExec.Endpoint[]; fileResponseType: "stream" | "binary-response"; generateSubpackageExports: boolean; - oauthTokenOverride: boolean; }) { super({ endpointSnippets }); this.context = context; this.fileResponseType = fileResponseType; this.isPaginationEnabled = context.config.generatePaginatedClients ?? false; this.generateSubpackageExports = generateSubpackageExports; - this.oauthTokenOverride = oauthTokenOverride; this.endpoints = this.buildEndpoints(); this.snippets = this.buildSnippets(endpointSnippets); @@ -477,11 +473,6 @@ const ${this.clientVariableName} = new ${this.rootClientConstructorName}({ } public buildAuthenticationDescription(): string | undefined { - // Only show authentication section when OAuth token override is enabled - if (!this.oauthTokenOverride) { - return undefined; - } - const oauthScheme = this.context.ir.auth.schemes.find((scheme) => scheme.type === "oauth"); if (oauthScheme == null) { return undefined; diff --git a/generators/typescript/sdk/versions.yml b/generators/typescript/sdk/versions.yml index db4c456d1f30..aa70acf71e51 100644 --- a/generators/typescript/sdk/versions.yml +++ b/generators/typescript/sdk/versions.yml @@ -1,4 +1,13 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.37.4 + changelogEntry: + - summary: | + Remove the `oauthTokenOverride` configuration flag. OAuth token override is now always enabled, + allowing users to provide a pre-generated bearer token instead of client ID/secret without any configuration. + type: fix + createdAt: "2025-12-09" + irVersion: 62 + - version: 3.37.3 changelogEntry: - summary: Update generator-cli to 0.5.0. diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.fern/metadata.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.fern/metadata.json deleted file mode 100644 index a63ae087b8ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.fern/metadata.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "cliVersion": "DUMMY", - "generatorName": "fernapi/fern-typescript-sdk", - "generatorVersion": "latest", - "generatorConfig": { - "oauthTokenOverride": true - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.github/workflows/ci.yml b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.github/workflows/ci.yml deleted file mode 100644 index 836106996595..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.github/workflows/ci.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: ci - -on: [push] - -jobs: - compile: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Compile - run: pnpm build - - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Test - run: pnpm test - - publish: - needs: [ compile, test ] - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Publish to npm - run: | - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} - publish() { # use latest npm to ensure OIDC support - npx -y npm@latest publish "$@" - } - if [[ ${GITHUB_REF} == *alpha* ]]; then - publish --access public --tag alpha - elif [[ ${GITHUB_REF} == *beta* ]]; then - publish --access public --tag beta - else - publish --access public - fi - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.gitignore b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.gitignore deleted file mode 100644 index 72271e049c02..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -.DS_Store -/dist \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/CONTRIBUTING.md b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/CONTRIBUTING.md deleted file mode 100644 index fe5bc2f77e0b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/CONTRIBUTING.md +++ /dev/null @@ -1,133 +0,0 @@ -# Contributing - -Thanks for your interest in contributing to this SDK! This document provides guidelines for contributing to the project. - -## Getting Started - -### Prerequisites - -- Node.js 20 or higher -- pnpm package manager - -### Installation - -Install the project dependencies: - -```bash -pnpm install -``` - -### Building - -Build the project: - -```bash -pnpm build -``` - -### Testing - -Run the test suite: - -```bash -pnpm test -``` - -Run specific test types: -- `pnpm test:unit` - Run unit tests -- `pnpm test:wire` - Run wire/integration tests - -### Linting and Formatting - -Check code style: - -```bash -pnpm run lint -pnpm run format:check -``` - -Fix code style issues: - -```bash -pnpm run lint:fix -pnpm run format:fix -``` - -Or use the combined check command: - -```bash -pnpm run check:fix -``` - -## About Generated Code - -**Important**: Most files in this SDK are automatically generated by [Fern](https://buildwithfern.com) from the API definition. Direct modifications to generated files will be overwritten the next time the SDK is generated. - -### Generated Files - -The following directories contain generated code: -- `src/api/` - API client classes and types -- `src/serialization/` - Serialization/deserialization logic -- Most TypeScript files in `src/` - -### How to Customize - -If you need to customize the SDK, you have two options: - -#### Option 1: Use `.fernignore` - -For custom code that should persist across SDK regenerations: - -1. Create a `.fernignore` file in the project root -2. Add file patterns for files you want to preserve (similar to `.gitignore` syntax) -3. Add your custom code to those files - -Files listed in `.fernignore` will not be overwritten when the SDK is regenerated. - -For more information, see the [Fern documentation on custom code](https://buildwithfern.com/learn/sdks/overview/custom-code). - -#### Option 2: Contribute to the Generator - -If you want to change how code is generated for all users of this SDK: - -1. The TypeScript SDK generator lives in the [Fern repository](https://github.com/fern-api/fern) -2. Generator code is located at `generators/typescript/sdk/` -3. Follow the [Fern contributing guidelines](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md) -4. Submit a pull request with your changes to the generator - -This approach is best for: -- Bug fixes in generated code -- New features that would benefit all users -- Improvements to code generation patterns - -## Making Changes - -### Workflow - -1. Create a new branch for your changes -2. Make your modifications -3. Run tests to ensure nothing breaks: `pnpm test` -4. Run linting and formatting: `pnpm run check:fix` -5. Build the project: `pnpm build` -6. Commit your changes with a clear commit message -7. Push your branch and create a pull request - -### Commit Messages - -Write clear, descriptive commit messages that explain what changed and why. - -### Code Style - -This project uses automated code formatting and linting. Run `pnpm run check:fix` before committing to ensure your code meets the project's style guidelines. - -## Questions or Issues? - -If you have questions or run into issues: - -1. Check the [Fern documentation](https://buildwithfern.com) -2. Search existing [GitHub issues](https://github.com/fern-api/fern/issues) -3. Open a new issue if your question hasn't been addressed - -## License - -By contributing to this project, you agree that your contributions will be licensed under the same license as the project. diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/README.md b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/README.md deleted file mode 100644 index 426a4df05b2a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# Seed TypeScript Library - -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) -[![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials-environment-variables)](https://www.npmjs.com/package/@fern/oauth-client-credentials-environment-variables) - -The Seed TypeScript library provides convenient access to the Seed APIs from TypeScript. - -## Table of Contents - -- [Installation](#installation) -- [Reference](#reference) -- [Usage](#usage) -- [Authentication](#authentication) -- [Request and Response Types](#request-and-response-types) -- [Exception Handling](#exception-handling) -- [Advanced](#advanced) - - [Additional Headers](#additional-headers) - - [Additional Query String Parameters](#additional-query-string-parameters) - - [Retries](#retries) - - [Timeouts](#timeouts) - - [Aborting Requests](#aborting-requests) - - [Access Raw Response Data](#access-raw-response-data) - - [Logging](#logging) - - [Runtime Compatibility](#runtime-compatibility) -- [Contributing](#contributing) - -## Installation - -```sh -npm i -s @fern/oauth-client-credentials-environment-variables -``` - -## Reference - -A full reference for this library is available [here](./reference.md). - -## Usage - -Instantiate and use the client with the following: - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "@fern/oauth-client-credentials-environment-variables"; - -const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: "YOUR_BASE_URL", clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET" }); -await client.auth.getTokenWithClientCredentials({ - client_id: "client_id", - client_secret: "client_secret", - scope: "scope" -}); -``` - -## Authentication - -The SDK supports OAuth authentication with two options: - -**Option 1: OAuth Client Credentials Flow** - -Use this when you want the SDK to automatically handle OAuth token retrieval and refreshing: - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "@fern/oauth-client-credentials-environment-variables"; - -const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - clientId: "YOUR_CLIENT_ID", - clientSecret: "YOUR_CLIENT_SECRET", - ... -}); -``` - -**Option 2: Token Override** - -Use this when you already have a valid bearer token and want to skip the OAuth flow: - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "@fern/oauth-client-credentials-environment-variables"; - -const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - token: "my-pre-generated-bearer-token", - ... -}); -``` - -## Request and Response Types - -The SDK exports all request and response types as TypeScript interfaces. Simply import them with the -following namespace: - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariables } from "@fern/oauth-client-credentials-environment-variables"; - -const request: SeedOauthClientCredentialsEnvironmentVariables.GetTokenRequest = { - ... -}; -``` - -## Exception Handling - -When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error -will be thrown. - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesError } from "@fern/oauth-client-credentials-environment-variables"; - -try { - await client.auth.getTokenWithClientCredentials(...); -} catch (err) { - if (err instanceof SeedOauthClientCredentialsEnvironmentVariablesError) { - console.log(err.statusCode); - console.log(err.message); - console.log(err.body); - console.log(err.rawResponse); - } -} -``` - -## Advanced - -### Additional Headers - -If you would like to send additional headers as part of the request, use the `headers` request option. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - headers: { - 'X-Custom-Header': 'custom value' - } -}); -``` - -### Additional Query String Parameters - -If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - queryParams: { - 'customQueryParamKey': 'custom query param value' - } -}); -``` - -### Retries - -The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long -as the request is deemed retryable and the number of retry attempts has not grown larger than the configured -retry limit (default: 2). - -A request is deemed retryable when any of the following HTTP status codes is returned: - -- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) -- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) -- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) - -Use the `maxRetries` request option to configure this behavior. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - maxRetries: 0 // override maxRetries at the request level -}); -``` - -### Timeouts - -The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - timeoutInSeconds: 30 // override timeout to 30s -}); -``` - -### Aborting Requests - -The SDK allows users to abort requests at any point by passing in an abort signal. - -```typescript -const controller = new AbortController(); -const response = await client.auth.getTokenWithClientCredentials(..., { - abortSignal: controller.signal -}); -controller.abort(); // aborts the request -``` - -### Access Raw Response Data - -The SDK provides access to raw response data, including headers, through the `.withRawResponse()` method. -The `.withRawResponse()` method returns a promise that results to an object with a `data` and a `rawResponse` property. - -```typescript -const { data, rawResponse } = await client.auth.getTokenWithClientCredentials(...).withRawResponse(); - -console.log(data); -console.log(rawResponse.headers['X-My-Header']); -``` - -### Logging - -The SDK supports logging. You can configure the logger by passing in a `logging` object to the client options. - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesClient, logging } from "@fern/oauth-client-credentials-environment-variables"; - -const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - ... - logging: { - level: logging.LogLevel.Debug, // defaults to logging.LogLevel.Info - logger: new logging.ConsoleLogger(), // defaults to ConsoleLogger - silent: false, // defaults to true, set to false to enable logging - } -}); -``` -The `logging` object can have the following properties: -- `level`: The log level to use. Defaults to `logging.LogLevel.Info`. -- `logger`: The logger to use. Defaults to a `logging.ConsoleLogger`. -- `silent`: Whether to silence the logger. Defaults to `true`. - -The `level` property can be one of the following values: -- `logging.LogLevel.Debug` -- `logging.LogLevel.Info` -- `logging.LogLevel.Warn` -- `logging.LogLevel.Error` - -To provide a custom logger, you can pass in an object that implements the `logging.ILogger` interface. - -
-Custom logger examples - -Here's an example using the popular `winston` logging library. -```ts -import winston from 'winston'; - -const winstonLogger = winston.createLogger({...}); - -const logger: logging.ILogger = { - debug: (msg, ...args) => winstonLogger.debug(msg, ...args), - info: (msg, ...args) => winstonLogger.info(msg, ...args), - warn: (msg, ...args) => winstonLogger.warn(msg, ...args), - error: (msg, ...args) => winstonLogger.error(msg, ...args), -}; -``` - -Here's an example using the popular `pino` logging library. - -```ts -import pino from 'pino'; - -const pinoLogger = pino({...}); - -const logger: logging.ILogger = { - debug: (msg, ...args) => pinoLogger.debug(args, msg), - info: (msg, ...args) => pinoLogger.info(args, msg), - warn: (msg, ...args) => pinoLogger.warn(args, msg), - error: (msg, ...args) => pinoLogger.error(args, msg), -}; -``` -
- - -### Runtime Compatibility - - -The SDK works in the following runtimes: - - - -- Node.js 18+ -- Vercel -- Cloudflare Workers -- Deno v1.25+ -- Bun 1.0+ -- React Native - -### Customizing Fetch Client - -The SDK provides a way for you to customize the underlying HTTP client / Fetch function. If you're running in an -unsupported environment, this provides a way for you to break glass and ensure the SDK works. - -```typescript -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "@fern/oauth-client-credentials-environment-variables"; - -const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - ... - fetcher: // provide your implementation here -}); -``` - -## Contributing - -While we value open-source contributions to this SDK, this library is generated programmatically. -Additions made directly to this library would have to be moved over to our generation code, -otherwise they would be overwritten upon the next generated release. Feel free to open a PR as -a proof of concept, but know that we will not be able to merge it as-is. We suggest opening -an issue first to discuss with us! - -On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/biome.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/biome.json deleted file mode 100644 index a777468e4ae2..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/biome.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json", - "root": true, - "vcs": { - "enabled": false - }, - "files": { - "ignoreUnknown": true, - "includes": [ - "**", - "!!dist", - "!!**/dist", - "!!lib", - "!!**/lib", - "!!_tmp_*", - "!!**/_tmp_*", - "!!*.tmp", - "!!**/*.tmp", - "!!.tmp/", - "!!**/.tmp/", - "!!*.log", - "!!**/*.log", - "!!**/.DS_Store", - "!!**/Thumbs.db" - ] - }, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 4, - "lineWidth": 120 - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } - } - }, - "linter": { - "rules": { - "style": { - "useNodejsImportProtocol": "off" - }, - "suspicious": { - "noAssignInExpressions": "warn", - "noUselessEscapeInString": { - "level": "warn", - "fix": "none", - "options": {} - }, - "noThenProperty": "warn", - "useIterableCallbackReturn": "warn", - "noShadowRestrictedNames": "warn", - "noTsIgnore": { - "level": "warn", - "fix": "none", - "options": {} - }, - "noConfusingVoidType": { - "level": "warn", - "fix": "none", - "options": {} - } - } - } - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/package.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/package.json deleted file mode 100644 index de5c501bb992..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "@fern/oauth-client-credentials-environment-variables", - "version": "0.0.1", - "private": false, - "repository": "github:oauth-client-credentials-environment-variables/fern", - "type": "commonjs", - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.mjs", - "types": "./dist/cjs/index.d.ts", - "exports": { - ".": { - "types": "./dist/cjs/index.d.ts", - "import": { - "types": "./dist/esm/index.d.mts", - "default": "./dist/esm/index.mjs" - }, - "require": { - "types": "./dist/cjs/index.d.ts", - "default": "./dist/cjs/index.js" - }, - "default": "./dist/cjs/index.js" - }, - "./package.json": "./package.json" - }, - "files": [ - "dist", - "reference.md", - "README.md", - "LICENSE" - ], - "scripts": { - "format": "biome format --write --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "format:check": "biome format --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "lint": "biome lint --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "lint:fix": "biome lint --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "check": "biome check --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "check:fix": "biome check --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "build": "pnpm build:cjs && pnpm build:esm", - "build:cjs": "tsc --project ./tsconfig.cjs.json", - "build:esm": "tsc --project ./tsconfig.esm.json && node scripts/rename-to-esm-files.js dist/esm", - "test": "vitest", - "test:unit": "vitest --project unit", - "test:wire": "vitest --project wire" - }, - "dependencies": {}, - "devDependencies": { - "webpack": "^5.97.1", - "ts-loader": "^9.5.1", - "vitest": "^3.2.4", - "msw": "2.11.2", - "@types/node": "^18.19.70", - "typescript": "~5.7.2", - "@biomejs/biome": "2.3.1" - }, - "browser": { - "fs": false, - "os": false, - "path": false, - "stream": false - }, - "packageManager": "pnpm@10.20.0", - "engines": { - "node": ">=18.0.0" - }, - "sideEffects": false -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/pnpm-workspace.yaml b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/pnpm-workspace.yaml deleted file mode 100644 index 6e4c395107df..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/pnpm-workspace.yaml +++ /dev/null @@ -1 +0,0 @@ -packages: ['.'] \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/reference.md b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/reference.md deleted file mode 100644 index 968c81af2d5a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/reference.md +++ /dev/null @@ -1,234 +0,0 @@ -# Reference -## Auth -
client.auth.getTokenWithClientCredentials({ ...params }) -> SeedOauthClientCredentialsEnvironmentVariables.TokenResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.auth.getTokenWithClientCredentials({ - client_id: "client_id", - client_secret: "client_secret", - scope: "scope" -}); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `SeedOauthClientCredentialsEnvironmentVariables.GetTokenRequest` - -
-
- -
-
- -**requestOptions:** `AuthClient.RequestOptions` - -
-
-
-
- - -
-
-
- -
client.auth.refreshToken({ ...params }) -> SeedOauthClientCredentialsEnvironmentVariables.TokenResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.auth.refreshToken({ - client_id: "client_id", - client_secret: "client_secret", - refresh_token: "refresh_token", - scope: "scope" -}); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `SeedOauthClientCredentialsEnvironmentVariables.RefreshTokenRequest` - -
-
- -
-
- -**requestOptions:** `AuthClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## NestedNoAuth Api -
client.nestedNoAuth.api.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.nestedNoAuth.api.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `ApiClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## Nested Api -
client.nested.api.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.nested.api.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `ApiClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## Simple -
client.simple.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.simple.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `SimpleClient.RequestOptions` - -
-
-
-
- - -
-
-
diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/scripts/rename-to-esm-files.js b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/scripts/rename-to-esm-files.js deleted file mode 100644 index dc1df1cbbacb..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/scripts/rename-to-esm-files.js +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env node - -const fs = require("fs").promises; -const path = require("path"); - -const extensionMap = { - ".js": ".mjs", - ".d.ts": ".d.mts", -}; -const oldExtensions = Object.keys(extensionMap); - -async function findFiles(rootPath) { - const files = []; - - async function scan(directory) { - const entries = await fs.readdir(directory, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(directory, entry.name); - - if (entry.isDirectory()) { - if (entry.name !== "node_modules" && !entry.name.startsWith(".")) { - await scan(fullPath); - } - } else if (entry.isFile()) { - if (oldExtensions.some((ext) => entry.name.endsWith(ext))) { - files.push(fullPath); - } - } - } - } - - await scan(rootPath); - return files; -} - -async function updateFiles(files) { - const updatedFiles = []; - for (const file of files) { - const updated = await updateFileContents(file); - updatedFiles.push(updated); - } - - console.log(`Updated imports in ${updatedFiles.length} files.`); -} - -async function updateFileContents(file) { - const content = await fs.readFile(file, "utf8"); - - let newContent = content; - // Update each extension type defined in the map - for (const [oldExt, newExt] of Object.entries(extensionMap)) { - // Handle static imports/exports - const staticRegex = new RegExp(`(import|export)(.+from\\s+['"])(\\.\\.?\\/[^'"]+)(\\${oldExt})(['"])`, "g"); - newContent = newContent.replace(staticRegex, `$1$2$3${newExt}$5`); - - // Handle dynamic imports (yield import, await import, regular import()) - const dynamicRegex = new RegExp( - `(yield\\s+import|await\\s+import|import)\\s*\\(\\s*['"](\\.\\.\?\\/[^'"]+)(\\${oldExt})['"]\\s*\\)`, - "g", - ); - newContent = newContent.replace(dynamicRegex, `$1("$2${newExt}")`); - } - - if (content !== newContent) { - await fs.writeFile(file, newContent, "utf8"); - return true; - } - return false; -} - -async function renameFiles(files) { - let counter = 0; - for (const file of files) { - const ext = oldExtensions.find((ext) => file.endsWith(ext)); - const newExt = extensionMap[ext]; - - if (newExt) { - const newPath = file.slice(0, -ext.length) + newExt; - await fs.rename(file, newPath); - counter++; - } - } - - console.log(`Renamed ${counter} files.`); -} - -async function main() { - try { - const targetDir = process.argv[2]; - if (!targetDir) { - console.error("Please provide a target directory"); - process.exit(1); - } - - const targetPath = path.resolve(targetDir); - const targetStats = await fs.stat(targetPath); - - if (!targetStats.isDirectory()) { - console.error("The provided path is not a directory"); - process.exit(1); - } - - console.log(`Scanning directory: ${targetDir}`); - - const files = await findFiles(targetDir); - - if (files.length === 0) { - console.log("No matching files found."); - process.exit(0); - } - - console.log(`Found ${files.length} files.`); - await updateFiles(files); - await renameFiles(files); - console.log("\nDone!"); - } catch (error) { - console.error("An error occurred:", error.message); - process.exit(1); - } -} - -main(); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/snippet.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/snippet.json deleted file mode 100644 index da0ee934b7fb..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/snippet.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "endpoints": [ - { - "id": { - "path": "/token", - "method": "POST", - "identifier_override": "endpoint_auth.getTokenWithClientCredentials" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsEnvironmentVariablesClient } from \"@fern/oauth-client-credentials-environment-variables\";\n\nconst client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.auth.getTokenWithClientCredentials({\n client_id: \"client_id\",\n client_secret: \"client_secret\",\n scope: \"scope\"\n});\n" - } - }, - { - "id": { - "path": "/token", - "method": "POST", - "identifier_override": "endpoint_auth.refreshToken" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsEnvironmentVariablesClient } from \"@fern/oauth-client-credentials-environment-variables\";\n\nconst client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.auth.refreshToken({\n client_id: \"client_id\",\n client_secret: \"client_secret\",\n refresh_token: \"refresh_token\",\n scope: \"scope\"\n});\n" - } - }, - { - "id": { - "path": "/nested-no-auth/get-something", - "method": "GET", - "identifier_override": "endpoint_nested-no-auth/api.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsEnvironmentVariablesClient } from \"@fern/oauth-client-credentials-environment-variables\";\n\nconst client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.nestedNoAuth.api.getSomething();\n" - } - }, - { - "id": { - "path": "/nested/get-something", - "method": "GET", - "identifier_override": "endpoint_nested/api.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsEnvironmentVariablesClient } from \"@fern/oauth-client-credentials-environment-variables\";\n\nconst client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.nested.api.getSomething();\n" - } - }, - { - "id": { - "path": "/get-something", - "method": "GET", - "identifier_override": "endpoint_simple.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsEnvironmentVariablesClient } from \"@fern/oauth-client-credentials-environment-variables\";\n\nconst client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.simple.getSomething();\n" - } - } - ], - "types": {} -} \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/BaseClient.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/BaseClient.ts deleted file mode 100644 index 1f7c6b8795b9..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/BaseClient.ts +++ /dev/null @@ -1,81 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { OAuthAuthProvider } from "./auth/OAuthAuthProvider.js"; -import { mergeHeaders } from "./core/headers.js"; -import * as core from "./core/index.js"; - -export type BaseClientOptions = { - environment: core.Supplier; - /** Specify a custom URL to connect the client to. */ - baseUrl?: core.Supplier; - /** Additional headers to include in requests. */ - headers?: Record | null | undefined>; - /** The default maximum time to wait for a response in seconds. */ - timeoutInSeconds?: number; - /** The default number of times to retry the request. Defaults to 2. */ - maxRetries?: number; - /** Provide a custom fetch implementation. Useful for platforms that don't have a built-in fetch or need a custom implementation. */ - fetch?: typeof fetch; - /** Configure logging for the client. */ - logging?: core.logging.LogConfig | core.logging.Logger; -} & OAuthAuthProvider.AuthOptions; - -export interface BaseRequestOptions { - /** The maximum time to wait for a response in seconds. */ - timeoutInSeconds?: number; - /** The number of times to retry the request. Defaults to 2. */ - maxRetries?: number; - /** A hook to abort the request. */ - abortSignal?: AbortSignal; - /** Additional query string parameters to include in the request. */ - queryParams?: Record; - /** Additional headers to include in the request. */ - headers?: Record | null | undefined>; -} - -export type NormalizedClientOptions = T & { - logging: core.logging.Logger; - authProvider?: core.AuthProvider; -}; - -export type NormalizedClientOptionsWithAuth = NormalizedClientOptions & { - authProvider: core.AuthProvider; -}; - -export function normalizeClientOptions(options: T): NormalizedClientOptions { - const headers = mergeHeaders( - { - "X-Fern-Language": "JavaScript", - "X-Fern-SDK-Name": "@fern/oauth-client-credentials-environment-variables", - "X-Fern-SDK-Version": "0.0.1", - "User-Agent": "@fern/oauth-client-credentials-environment-variables/0.0.1", - "X-Fern-Runtime": core.RUNTIME.type, - "X-Fern-Runtime-Version": core.RUNTIME.version, - }, - options?.headers, - ); - - return { - ...options, - logging: core.logging.createLogger(options?.logging), - headers, - } as NormalizedClientOptions; -} - -export function normalizeClientOptionsWithAuth( - options: T, -): NormalizedClientOptionsWithAuth { - const normalized = normalizeClientOptions(options) as NormalizedClientOptionsWithAuth; - const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized); - normalized.authProvider ??= OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider); - return normalized; -} - -function withNoOpAuthProvider( - options: NormalizedClientOptions, -): NormalizedClientOptionsWithAuth { - return { - ...options, - authProvider: new core.NoOpAuthProvider(), - }; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/Client.ts deleted file mode 100644 index 18defb873720..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/Client.ts +++ /dev/null @@ -1,42 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { AuthClient } from "./api/resources/auth/client/Client.js"; -import { NestedClient } from "./api/resources/nested/client/Client.js"; -import { NestedNoAuthClient } from "./api/resources/nestedNoAuth/client/Client.js"; -import { SimpleClient } from "./api/resources/simple/client/Client.js"; -import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "./BaseClient.js"; - -export declare namespace SeedOauthClientCredentialsEnvironmentVariablesClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class SeedOauthClientCredentialsEnvironmentVariablesClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - protected _auth: AuthClient | undefined; - protected _nestedNoAuth: NestedNoAuthClient | undefined; - protected _nested: NestedClient | undefined; - protected _simple: SimpleClient | undefined; - - constructor(options: SeedOauthClientCredentialsEnvironmentVariablesClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - public get auth(): AuthClient { - return (this._auth ??= new AuthClient(this._options)); - } - - public get nestedNoAuth(): NestedNoAuthClient { - return (this._nestedNoAuth ??= new NestedNoAuthClient(this._options)); - } - - public get nested(): NestedClient { - return (this._nested ??= new NestedClient(this._options)); - } - - public get simple(): SimpleClient { - return (this._simple ??= new SimpleClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/index.ts deleted file mode 100644 index e445af0d831e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/Client.ts deleted file mode 100644 index 773fa59b13ec..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/Client.ts +++ /dev/null @@ -1,142 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../core/headers.js"; -import * as core from "../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../errors/index.js"; -import type * as SeedOauthClientCredentialsEnvironmentVariables from "../../../index.js"; - -export declare namespace AuthClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class AuthClient { - protected readonly _options: NormalizedClientOptions; - - constructor(options: AuthClient.Options) { - this._options = normalizeClientOptions(options); - } - - /** - * @param {SeedOauthClientCredentialsEnvironmentVariables.GetTokenRequest} request - * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.auth.getTokenWithClientCredentials({ - * client_id: "client_id", - * client_secret: "client_secret", - * scope: "scope" - * }) - */ - public getTokenWithClientCredentials( - request: SeedOauthClientCredentialsEnvironmentVariables.GetTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getTokenWithClientCredentials(request, requestOptions)); - } - - private async __getTokenWithClientCredentials( - request: SeedOauthClientCredentialsEnvironmentVariables.GetTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/token", - ), - method: "POST", - headers: _headers, - contentType: "application/json", - queryParameters: requestOptions?.queryParams, - requestType: "json", - body: { ...request, audience: "https://api.example.com", grant_type: "client_credentials" }, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { - data: _response.body as SeedOauthClientCredentialsEnvironmentVariables.TokenResponse, - rawResponse: _response.rawResponse, - }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/token"); - } - - /** - * @param {SeedOauthClientCredentialsEnvironmentVariables.RefreshTokenRequest} request - * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.auth.refreshToken({ - * client_id: "client_id", - * client_secret: "client_secret", - * refresh_token: "refresh_token", - * scope: "scope" - * }) - */ - public refreshToken( - request: SeedOauthClientCredentialsEnvironmentVariables.RefreshTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__refreshToken(request, requestOptions)); - } - - private async __refreshToken( - request: SeedOauthClientCredentialsEnvironmentVariables.RefreshTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/token", - ), - method: "POST", - headers: _headers, - contentType: "application/json", - queryParameters: requestOptions?.queryParams, - requestType: "json", - body: { ...request, audience: "https://api.example.com", grant_type: "refresh_token" }, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { - data: _response.body as SeedOauthClientCredentialsEnvironmentVariables.TokenResponse, - rawResponse: _response.rawResponse, - }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/token"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/index.ts deleted file mode 100644 index 195f9aa8a846..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./requests/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts deleted file mode 100644 index ffedfd9bf691..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts +++ /dev/null @@ -1,15 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * @example - * { - * client_id: "client_id", - * client_secret: "client_secret", - * scope: "scope" - * } - */ -export interface GetTokenRequest { - client_id: string; - client_secret: string; - scope?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts deleted file mode 100644 index f25316235ce7..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * @example - * { - * client_id: "client_id", - * client_secret: "client_secret", - * refresh_token: "refresh_token", - * scope: "scope" - * } - */ -export interface RefreshTokenRequest { - client_id: string; - client_secret: string; - refresh_token: string; - scope?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/index.ts deleted file mode 100644 index a5e87cc70d74..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/client/requests/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { GetTokenRequest } from "./GetTokenRequest.js"; -export type { RefreshTokenRequest } from "./RefreshTokenRequest.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/index.ts deleted file mode 100644 index d9adb1af9a93..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./types/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/TokenResponse.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/TokenResponse.ts deleted file mode 100644 index b410f59491c1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/TokenResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * An OAuth token response. - */ -export interface TokenResponse { - access_token: string; - expires_in: number; - refresh_token?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/index.ts deleted file mode 100644 index 67937a969229..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/auth/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./TokenResponse.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/index.ts deleted file mode 100644 index 951213c3d797..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./auth/client/requests/index.js"; -export * as auth from "./auth/index.js"; -export * from "./auth/types/index.js"; -export * as nested from "./nested/index.js"; -export * as nestedNoAuth from "./nestedNoAuth/index.js"; -export * as simple from "./simple/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/Client.ts deleted file mode 100644 index 07288b1cd312..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/Client.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; -import { ApiClient } from "../resources/api/client/Client.js"; - -export declare namespace NestedClient { - export type Options = BaseClientOptions; -} - -export class NestedClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - protected _api: ApiClient | undefined; - - constructor(options: NestedClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - public get api(): ApiClient { - return (this._api ??= new ApiClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/index.ts deleted file mode 100644 index 9eb1192dcc32..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/Client.ts deleted file mode 100644 index 10bfd8e3a359..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/Client.ts +++ /dev/null @@ -1,69 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../../../core/headers.js"; -import * as core from "../../../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../../../errors/index.js"; - -export declare namespace ApiClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class ApiClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - - constructor(options: ApiClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - /** - * @param {ApiClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.nested.api.getSomething() - */ - public getSomething(requestOptions?: ApiClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: ApiClient.RequestOptions): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/nested/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/nested/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/index.ts deleted file mode 100644 index e2d4d5d890ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nested/resources/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as api from "./api/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/Client.ts deleted file mode 100644 index 32241ef71181..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/Client.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; -import { ApiClient } from "../resources/api/client/Client.js"; - -export declare namespace NestedNoAuthClient { - export type Options = BaseClientOptions; -} - -export class NestedNoAuthClient { - protected readonly _options: NormalizedClientOptions; - protected _api: ApiClient | undefined; - - constructor(options: NestedNoAuthClient.Options) { - this._options = normalizeClientOptions(options); - } - - public get api(): ApiClient { - return (this._api ??= new ApiClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/index.ts deleted file mode 100644 index 9eb1192dcc32..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts deleted file mode 100644 index 4c95c5883353..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts +++ /dev/null @@ -1,64 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../../../core/headers.js"; -import * as core from "../../../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../../../errors/index.js"; - -export declare namespace ApiClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class ApiClient { - protected readonly _options: NormalizedClientOptions; - - constructor(options: ApiClient.Options) { - this._options = normalizeClientOptions(options); - } - - /** - * @param {ApiClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.nestedNoAuth.api.getSomething() - */ - public getSomething(requestOptions?: ApiClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: ApiClient.RequestOptions): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/nested-no-auth/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/nested-no-auth/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/index.ts deleted file mode 100644 index e2d4d5d890ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/nestedNoAuth/resources/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as api from "./api/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/Client.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/Client.ts deleted file mode 100644 index 9867502fc935..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/Client.ts +++ /dev/null @@ -1,69 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../core/headers.js"; -import * as core from "../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../errors/index.js"; - -export declare namespace SimpleClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class SimpleClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - - constructor(options: SimpleClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - /** - * @param {SimpleClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.simple.getSomething() - */ - public getSomething(requestOptions?: SimpleClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: SimpleClient.RequestOptions): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/api/resources/simple/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/OAuthAuthProvider.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/OAuthAuthProvider.ts deleted file mode 100644 index 7a6e11afb2e0..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/OAuthAuthProvider.ts +++ /dev/null @@ -1,147 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { AuthClient } from "../api/resources/auth/client/Client.js"; -import type { BaseClientOptions } from "../BaseClient.js"; -import * as core from "../core/index.js"; -import * as errors from "../errors/index.js"; - -export class OAuthAuthProvider implements core.AuthProvider { - private readonly BUFFER_IN_MINUTES: number = 2; - private readonly _clientId: core.Supplier | undefined; - private readonly _clientSecret: core.Supplier | undefined; - private readonly _authClient: AuthClient; - private _accessToken: string | undefined; - private _expiresAt: Date; - private _refreshPromise: Promise | undefined; - - constructor(options: OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.ClientCredentials) { - this._clientId = options.clientId; - this._clientSecret = options.clientSecret; - this._authClient = new AuthClient(options); - this._expiresAt = new Date(); - } - - public static canCreate( - options: OAuthAuthProvider.Options, - ): options is OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.ClientCredentials { - return ( - (("clientId" in options && options.clientId != null) || process.env?.CLIENT_ID != null) && - (("clientSecret" in options && options.clientSecret != null) || process.env?.CLIENT_SECRET != null) - ); - } - - public async getAuthRequest(arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - const token = await this.getToken(arg); - - return { - headers: { - Authorization: `Bearer ${token}`, - }, - }; - } - - private async getToken(arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - if (this._accessToken && this._expiresAt > new Date()) { - return this._accessToken; - } - // If a refresh is already in progress, return the existing promise - if (this._refreshPromise != null) { - return this._refreshPromise; - } - return this.refresh(arg); - } - - private async refresh(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - this._refreshPromise = (async () => { - try { - const clientId = (await core.Supplier.get(this._clientId)) ?? process.env?.CLIENT_ID; - if (clientId == null) { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: - "clientId is required; either pass it as an argument or set the CLIENT_ID environment variable", - }); - } - - const clientSecret = (await core.Supplier.get(this._clientSecret)) ?? process.env?.CLIENT_SECRET; - if (clientSecret == null) { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: - "clientSecret is required; either pass it as an argument or set the CLIENT_SECRET environment variable", - }); - } - const tokenResponse = await this._authClient.getTokenWithClientCredentials({ - client_id: clientId, - client_secret: clientSecret, - }); - - this._accessToken = tokenResponse.access_token; - this._expiresAt = this.getExpiresAt(tokenResponse.expires_in, this.BUFFER_IN_MINUTES); - return this._accessToken; - } finally { - this._refreshPromise = undefined; - } - })(); - return this._refreshPromise; - } - - private getExpiresAt(expiresInSeconds: number, bufferInMinutes: number): Date { - const now = new Date(); - return new Date(now.getTime() + expiresInSeconds * 1000 - bufferInMinutes * 60 * 1000); - } -} - -export class OAuthTokenOverrideAuthProvider implements core.AuthProvider { - private readonly _token: core.Supplier; - - constructor(options: OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.TokenOverride) { - if (options.token == null) { - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: "token is required. Please provide it in options.", - }); - } - this._token = options.token; - } - - public static canCreate( - options: OAuthAuthProvider.Options, - ): options is OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.TokenOverride { - return "token" in options && options.token != null; - } - - public async getAuthRequest(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - return { - headers: { - Authorization: `Bearer ${await core.Supplier.get(this._token)}`, - }, - }; - } -} - -export namespace OAuthAuthProvider { - export type AuthOptions = AuthOptions.ClientCredentials | AuthOptions.TokenOverride; - - export namespace AuthOptions { - export interface ClientCredentials { - clientId: core.Supplier | undefined; - clientSecret: core.Supplier | undefined; - } - - export interface TokenOverride { - token: core.Supplier; - } - } - - export type Options = BaseClientOptions; - - export function createInstance(options: Options): core.AuthProvider { - if (OAuthTokenOverrideAuthProvider.canCreate(options)) { - return new OAuthTokenOverrideAuthProvider(options); - } else if (OAuthAuthProvider.canCreate(options)) { - return new OAuthAuthProvider(options); - } - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: - "Insufficient options to create OAuthAuthProvider. Please provide either clientId and clientSecret, or token.", - }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/index.ts deleted file mode 100644 index 83e326d1824d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OAuthAuthProvider } from "./OAuthAuthProvider.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthProvider.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthProvider.ts deleted file mode 100644 index 895a50ff30da..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthProvider.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { EndpointMetadata } from "../fetcher/EndpointMetadata.js"; -import type { AuthRequest } from "./AuthRequest.js"; - -export interface AuthProvider { - getAuthRequest(arg?: { endpointMetadata?: EndpointMetadata }): Promise; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthRequest.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthRequest.ts deleted file mode 100644 index f6218b42211e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/AuthRequest.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Request parameters for authentication requests. - */ -export interface AuthRequest { - /** - * The headers to be included in the request. - */ - headers: Record; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BasicAuth.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BasicAuth.ts deleted file mode 100644 index a64235910062..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BasicAuth.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { base64Decode, base64Encode } from "../base64.js"; - -export interface BasicAuth { - username: string; - password: string; -} - -const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; - -export const BasicAuth = { - toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { - if (basicAuth == null) { - return undefined; - } - const token = base64Encode(`${basicAuth.username}:${basicAuth.password}`); - return `Basic ${token}`; - }, - fromAuthorizationHeader: (header: string): BasicAuth => { - const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); - const decoded = base64Decode(credentials); - const [username, ...passwordParts] = decoded.split(":"); - const password = passwordParts.length > 0 ? passwordParts.join(":") : undefined; - - if (username == null || password == null) { - throw new Error("Invalid basic auth"); - } - return { - username, - password, - }; - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BearerToken.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BearerToken.ts deleted file mode 100644 index c44a06c38f06..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/BearerToken.ts +++ /dev/null @@ -1,20 +0,0 @@ -export type BearerToken = string; - -const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; - -function toAuthorizationHeader(token: string | undefined): string | undefined { - if (token == null) { - return undefined; - } - return `Bearer ${token}`; -} - -export const BearerToken: { - toAuthorizationHeader: typeof toAuthorizationHeader; - fromAuthorizationHeader: (header: string) => BearerToken; -} = { - toAuthorizationHeader: toAuthorizationHeader, - fromAuthorizationHeader: (header: string): BearerToken => { - return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/NoOpAuthProvider.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/NoOpAuthProvider.ts deleted file mode 100644 index 5b7acfd2bd8b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/NoOpAuthProvider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { AuthProvider } from "./AuthProvider.js"; -import type { AuthRequest } from "./AuthRequest.js"; - -export class NoOpAuthProvider implements AuthProvider { - public getAuthRequest(): Promise { - return Promise.resolve({ headers: {} }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/index.ts deleted file mode 100644 index 2215b227709e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/auth/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type { AuthProvider } from "./AuthProvider.js"; -export type { AuthRequest } from "./AuthRequest.js"; -export { BasicAuth } from "./BasicAuth.js"; -export { BearerToken } from "./BearerToken.js"; -export { NoOpAuthProvider } from "./NoOpAuthProvider.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/base64.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/base64.ts deleted file mode 100644 index 448a0db638a6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/base64.ts +++ /dev/null @@ -1,27 +0,0 @@ -function base64ToBytes(base64: string): Uint8Array { - const binString = atob(base64); - return Uint8Array.from(binString, (m) => m.codePointAt(0)!); -} - -function bytesToBase64(bytes: Uint8Array): string { - const binString = String.fromCodePoint(...bytes); - return btoa(binString); -} - -export function base64Encode(input: string): string { - if (typeof Buffer !== "undefined") { - return Buffer.from(input, "utf8").toString("base64"); - } - - const bytes = new TextEncoder().encode(input); - return bytesToBase64(bytes); -} - -export function base64Decode(input: string): string { - if (typeof Buffer !== "undefined") { - return Buffer.from(input, "base64").toString("utf8"); - } - - const bytes = base64ToBytes(input); - return new TextDecoder().decode(bytes); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/exports.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/exports.ts deleted file mode 100644 index 69296d7100d6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/exports.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/APIResponse.ts deleted file mode 100644 index 97ab83c2b195..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/APIResponse.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { RawResponse } from "./RawResponse.js"; - -/** - * The response of an API call. - * It is a successful response or a failed response. - */ -export type APIResponse = SuccessfulResponse | FailedResponse; - -export interface SuccessfulResponse { - ok: true; - body: T; - /** - * @deprecated Use `rawResponse` instead - */ - headers?: Record; - rawResponse: RawResponse; -} - -export interface FailedResponse { - ok: false; - error: T; - rawResponse: RawResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/BinaryResponse.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/BinaryResponse.ts deleted file mode 100644 index bca7f4c77981..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/BinaryResponse.ts +++ /dev/null @@ -1,34 +0,0 @@ -export type BinaryResponse = { - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ - bodyUsed: Response["bodyUsed"]; - /** - * Returns a ReadableStream of the response body. - * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) - */ - stream: () => Response["body"]; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ - arrayBuffer: () => ReturnType; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ - blob: () => ReturnType; - /** - * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) - * Some versions of the Fetch API may not support this method. - */ - bytes?(): ReturnType; -}; - -export function getBinaryResponse(response: Response): BinaryResponse { - const binaryResponse: BinaryResponse = { - get bodyUsed() { - return response.bodyUsed; - }, - stream: () => response.body, - arrayBuffer: response.arrayBuffer.bind(response), - blob: response.blob.bind(response), - }; - if ("bytes" in response && typeof response.bytes === "function") { - binaryResponse.bytes = response.bytes.bind(response); - } - - return binaryResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointMetadata.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointMetadata.ts deleted file mode 100644 index 998d68f5c20c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointMetadata.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type SecuritySchemeKey = string; -/** - * A collection of security schemes, where the key is the name of the security scheme and the value is the list of scopes required for that scheme. - * All schemes in the collection must be satisfied for authentication to be successful. - */ -export type SecuritySchemeCollection = Record; -export type AuthScope = string; -export type EndpointMetadata = { - /** - * An array of security scheme collections. Each collection represents an alternative way to authenticate. - */ - security?: SecuritySchemeCollection[]; -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointSupplier.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointSupplier.ts deleted file mode 100644 index 8079841c4062..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/EndpointSupplier.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { EndpointMetadata } from "./EndpointMetadata.js"; -import type { Supplier } from "./Supplier.js"; - -type EndpointSupplierFn = (arg: { endpointMetadata: EndpointMetadata }) => T | Promise; -export type EndpointSupplier = Supplier | EndpointSupplierFn; -export const EndpointSupplier = { - get: async (supplier: EndpointSupplier, arg: { endpointMetadata: EndpointMetadata }): Promise => { - if (typeof supplier === "function") { - return (supplier as EndpointSupplierFn)(arg); - } else { - return supplier; - } - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Fetcher.ts deleted file mode 100644 index 58bb0e3ef7d9..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Fetcher.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { toJson } from "../json.js"; -import { createLogger, type LogConfig, type Logger } from "../logging/logger.js"; -import type { APIResponse } from "./APIResponse.js"; -import { createRequestUrl } from "./createRequestUrl.js"; -import type { EndpointMetadata } from "./EndpointMetadata.js"; -import { EndpointSupplier } from "./EndpointSupplier.js"; -import { getErrorResponseBody } from "./getErrorResponseBody.js"; -import { getFetchFn } from "./getFetchFn.js"; -import { getRequestBody } from "./getRequestBody.js"; -import { getResponseBody } from "./getResponseBody.js"; -import { Headers } from "./Headers.js"; -import { makeRequest } from "./makeRequest.js"; -import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -import { requestWithRetries } from "./requestWithRetries.js"; - -export type FetchFunction = (args: Fetcher.Args) => Promise>; - -export declare namespace Fetcher { - export interface Args { - url: string; - method: string; - contentType?: string; - headers?: Record | null | undefined>; - queryParameters?: Record; - body?: unknown; - timeoutMs?: number; - maxRetries?: number; - withCredentials?: boolean; - abortSignal?: AbortSignal; - requestType?: "json" | "file" | "bytes" | "form" | "other"; - responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer" | "binary-response"; - duplex?: "half"; - endpointMetadata?: EndpointMetadata; - fetchFn?: typeof fetch; - logging?: LogConfig | Logger; - } - - export type Error = FailedStatusCodeError | NonJsonError | BodyIsNullError | TimeoutError | UnknownError; - - export interface FailedStatusCodeError { - reason: "status-code"; - statusCode: number; - body: unknown; - } - - export interface NonJsonError { - reason: "non-json"; - statusCode: number; - rawBody: string; - } - - export interface BodyIsNullError { - reason: "body-is-null"; - statusCode: number; - } - - export interface TimeoutError { - reason: "timeout"; - } - - export interface UnknownError { - reason: "unknown"; - errorMessage: string; - } -} - -const SENSITIVE_HEADERS = new Set([ - "authorization", - "www-authenticate", - "x-api-key", - "api-key", - "apikey", - "x-api-token", - "x-auth-token", - "auth-token", - "cookie", - "set-cookie", - "proxy-authorization", - "proxy-authenticate", - "x-csrf-token", - "x-xsrf-token", - "x-session-token", - "x-access-token", -]); - -function redactHeaders(headers: Headers | Record): Record { - const filtered: Record = {}; - for (const [key, value] of headers instanceof Headers ? headers.entries() : Object.entries(headers)) { - if (SENSITIVE_HEADERS.has(key.toLowerCase())) { - filtered[key] = "[REDACTED]"; - } else { - filtered[key] = value; - } - } - return filtered; -} - -const SENSITIVE_QUERY_PARAMS = new Set([ - "api_key", - "api-key", - "apikey", - "token", - "access_token", - "access-token", - "auth_token", - "auth-token", - "password", - "passwd", - "secret", - "api_secret", - "api-secret", - "apisecret", - "key", - "session", - "session_id", - "session-id", -]); - -function redactQueryParameters(queryParameters?: Record): Record | undefined { - if (queryParameters == null) { - return queryParameters; - } - const redacted: Record = {}; - for (const [key, value] of Object.entries(queryParameters)) { - if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) { - redacted[key] = "[REDACTED]"; - } else { - redacted[key] = value; - } - } - return redacted; -} - -function redactUrl(url: string): string { - const protocolIndex = url.indexOf("://"); - if (protocolIndex === -1) return url; - - const afterProtocol = protocolIndex + 3; - - // Find the first delimiter that marks the end of the authority section - const pathStart = url.indexOf("/", afterProtocol); - let queryStart = url.indexOf("?", afterProtocol); - let fragmentStart = url.indexOf("#", afterProtocol); - - const firstDelimiter = Math.min( - pathStart === -1 ? url.length : pathStart, - queryStart === -1 ? url.length : queryStart, - fragmentStart === -1 ? url.length : fragmentStart, - ); - - // Find the LAST @ before the delimiter (handles multiple @ in credentials) - let atIndex = -1; - for (let i = afterProtocol; i < firstDelimiter; i++) { - if (url[i] === "@") { - atIndex = i; - } - } - - if (atIndex !== -1) { - url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`; - } - - // Recalculate queryStart since url might have changed - queryStart = url.indexOf("?"); - if (queryStart === -1) return url; - - fragmentStart = url.indexOf("#", queryStart); - const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length; - const queryString = url.slice(queryStart + 1, queryEnd); - - if (queryString.length === 0) return url; - - // FAST PATH: Quick check if any sensitive keywords present - // Using indexOf is faster than regex for simple substring matching - const lower = queryString.toLowerCase(); - const hasSensitive = - lower.includes("token") || - lower.includes("key") || - lower.includes("password") || - lower.includes("passwd") || - lower.includes("secret") || - lower.includes("session") || - lower.includes("auth"); - - if (!hasSensitive) { - return url; - } - - // SLOW PATH: Parse and redact - const redactedParams: string[] = []; - const params = queryString.split("&"); - - for (const param of params) { - const equalIndex = param.indexOf("="); - if (equalIndex === -1) { - redactedParams.push(param); - continue; - } - - const key = param.slice(0, equalIndex); - let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()); - - if (!shouldRedact && key.includes("%")) { - try { - const decodedKey = decodeURIComponent(key); - shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase()); - } catch {} - } - - redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param); - } - - return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd); -} - -async function getHeaders(args: Fetcher.Args): Promise { - const newHeaders: Headers = new Headers(); - - newHeaders.set( - "Accept", - args.responseType === "json" ? "application/json" : args.responseType === "text" ? "text/plain" : "*/*", - ); - if (args.body !== undefined && args.contentType != null) { - newHeaders.set("Content-Type", args.contentType); - } - - if (args.headers == null) { - return newHeaders; - } - - for (const [key, value] of Object.entries(args.headers)) { - const result = await EndpointSupplier.get(value, { endpointMetadata: args.endpointMetadata ?? {} }); - if (typeof result === "string") { - newHeaders.set(key, result); - continue; - } - if (result == null) { - continue; - } - newHeaders.set(key, `${result}`); - } - return newHeaders; -} - -export async function fetcherImpl(args: Fetcher.Args): Promise> { - const url = createRequestUrl(args.url, args.queryParameters); - const requestBody: BodyInit | undefined = await getRequestBody({ - body: args.body, - type: args.requestType ?? "other", - }); - const fetchFn = args.fetchFn ?? (await getFetchFn()); - const headers = await getHeaders(args); - const logger = createLogger(args.logging); - - if (logger.isDebug()) { - const metadata = { - method: args.method, - url: redactUrl(url), - headers: redactHeaders(headers), - queryParameters: redactQueryParameters(args.queryParameters), - hasBody: requestBody != null, - }; - logger.debug("Making HTTP request", metadata); - } - - try { - const response = await requestWithRetries( - async () => - makeRequest( - fetchFn, - url, - args.method, - headers, - requestBody, - args.timeoutMs, - args.abortSignal, - args.withCredentials, - args.duplex, - ), - args.maxRetries, - ); - - if (response.status >= 200 && response.status < 400) { - if (logger.isDebug()) { - const metadata = { - method: args.method, - url: redactUrl(url), - statusCode: response.status, - responseHeaders: redactHeaders(response.headers), - }; - logger.debug("HTTP request succeeded", metadata); - } - const body = await getResponseBody(response, args.responseType); - return { - ok: true, - body: body as R, - headers: response.headers, - rawResponse: toRawResponse(response), - }; - } else { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - statusCode: response.status, - responseHeaders: redactHeaders(Object.fromEntries(response.headers.entries())), - }; - logger.error("HTTP request failed with error status", metadata); - } - return { - ok: false, - error: { - reason: "status-code", - statusCode: response.status, - body: await getErrorResponseBody(response), - }, - rawResponse: toRawResponse(response), - }; - } - } catch (error) { - if (args.abortSignal?.aborted) { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - }; - logger.error("HTTP request was aborted", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: "The user aborted a request", - }, - rawResponse: abortRawResponse, - }; - } else if (error instanceof Error && error.name === "AbortError") { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - timeoutMs: args.timeoutMs, - }; - logger.error("HTTP request timed out", metadata); - } - return { - ok: false, - error: { - reason: "timeout", - }, - rawResponse: abortRawResponse, - }; - } else if (error instanceof Error) { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - errorMessage: error.message, - }; - logger.error("HTTP request failed with error", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: error.message, - }, - rawResponse: unknownRawResponse, - }; - } - - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - error: toJson(error), - }; - logger.error("HTTP request failed with unknown error", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: toJson(error), - }, - rawResponse: unknownRawResponse, - }; - } -} - -export const fetcher: FetchFunction = fetcherImpl; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Headers.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Headers.ts deleted file mode 100644 index af841aa24f55..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Headers.ts +++ /dev/null @@ -1,93 +0,0 @@ -let Headers: typeof globalThis.Headers; - -if (typeof globalThis.Headers !== "undefined") { - Headers = globalThis.Headers; -} else { - Headers = class Headers implements Headers { - private headers: Map; - - constructor(init?: HeadersInit) { - this.headers = new Map(); - - if (init) { - if (init instanceof Headers) { - init.forEach((value, key) => this.append(key, value)); - } else if (Array.isArray(init)) { - for (const [key, value] of init) { - if (typeof key === "string" && typeof value === "string") { - this.append(key, value); - } else { - throw new TypeError("Each header entry must be a [string, string] tuple"); - } - } - } else { - for (const [key, value] of Object.entries(init)) { - if (typeof value === "string") { - this.append(key, value); - } else { - throw new TypeError("Header values must be strings"); - } - } - } - } - } - - append(name: string, value: string): void { - const key = name.toLowerCase(); - const existing = this.headers.get(key) || []; - this.headers.set(key, [...existing, value]); - } - - delete(name: string): void { - const key = name.toLowerCase(); - this.headers.delete(key); - } - - get(name: string): string | null { - const key = name.toLowerCase(); - const values = this.headers.get(key); - return values ? values.join(", ") : null; - } - - has(name: string): boolean { - const key = name.toLowerCase(); - return this.headers.has(key); - } - - set(name: string, value: string): void { - const key = name.toLowerCase(); - this.headers.set(key, [value]); - } - - forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: unknown): void { - const boundCallback = thisArg ? callbackfn.bind(thisArg) : callbackfn; - this.headers.forEach((values, key) => boundCallback(values.join(", "), key, this)); - } - - getSetCookie(): string[] { - return this.headers.get("set-cookie") || []; - } - - *entries(): HeadersIterator<[string, string]> { - for (const [key, values] of this.headers.entries()) { - yield [key, values.join(", ")]; - } - } - - *keys(): HeadersIterator { - yield* this.headers.keys(); - } - - *values(): HeadersIterator { - for (const values of this.headers.values()) { - yield values.join(", "); - } - } - - [Symbol.iterator](): HeadersIterator<[string, string]> { - return this.entries(); - } - }; -} - -export { Headers }; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/HttpResponsePromise.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/HttpResponsePromise.ts deleted file mode 100644 index 692ca7d795f0..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/HttpResponsePromise.ts +++ /dev/null @@ -1,116 +0,0 @@ -import type { WithRawResponse } from "./RawResponse.js"; - -/** - * A promise that returns the parsed response and lets you retrieve the raw response too. - */ -export class HttpResponsePromise extends Promise { - private innerPromise: Promise>; - private unwrappedPromise: Promise | undefined; - - private constructor(promise: Promise>) { - // Initialize with a no-op to avoid premature parsing - super((resolve) => { - resolve(undefined as unknown as T); - }); - this.innerPromise = promise; - } - - /** - * Creates an `HttpResponsePromise` from a function that returns a promise. - * - * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. - * @param args - Arguments to pass to the function. - * @returns An `HttpResponsePromise` instance. - */ - public static fromFunction Promise>, T>( - fn: F, - ...args: Parameters - ): HttpResponsePromise { - return new HttpResponsePromise(fn(...args)); - } - - /** - * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. - * - * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. - * @returns A function that returns an `HttpResponsePromise` instance. - */ - public static interceptFunction< - F extends (...args: never[]) => Promise>, - T = Awaited>["data"], - >(fn: F): (...args: Parameters) => HttpResponsePromise { - return (...args: Parameters): HttpResponsePromise => { - return HttpResponsePromise.fromPromise(fn(...args)); - }; - } - - /** - * Creates an `HttpResponsePromise` from an existing promise. - * - * @param promise - A promise resolving to a `WithRawResponse` object. - * @returns An `HttpResponsePromise` instance. - */ - public static fromPromise(promise: Promise>): HttpResponsePromise { - return new HttpResponsePromise(promise); - } - - /** - * Creates an `HttpResponsePromise` from an executor function. - * - * @param executor - A function that takes resolve and reject callbacks to create a promise. - * @returns An `HttpResponsePromise` instance. - */ - public static fromExecutor( - executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, - ): HttpResponsePromise { - const promise = new Promise>(executor); - return new HttpResponsePromise(promise); - } - - /** - * Creates an `HttpResponsePromise` from a resolved result. - * - * @param result - A `WithRawResponse` object to resolve immediately. - * @returns An `HttpResponsePromise` instance. - */ - public static fromResult(result: WithRawResponse): HttpResponsePromise { - const promise = Promise.resolve(result); - return new HttpResponsePromise(promise); - } - - private unwrap(): Promise { - if (!this.unwrappedPromise) { - this.unwrappedPromise = this.innerPromise.then(({ data }) => data); - } - return this.unwrappedPromise; - } - - /** @inheritdoc */ - public override then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, - ): Promise { - return this.unwrap().then(onfulfilled, onrejected); - } - - /** @inheritdoc */ - public override catch( - onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, - ): Promise { - return this.unwrap().catch(onrejected); - } - - /** @inheritdoc */ - public override finally(onfinally?: (() => void) | null): Promise { - return this.unwrap().finally(onfinally); - } - - /** - * Retrieves the data and raw response. - * - * @returns A promise resolving to a `WithRawResponse` object. - */ - public async withRawResponse(): Promise> { - return await this.innerPromise; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/RawResponse.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/RawResponse.ts deleted file mode 100644 index 37fb44e2aa99..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/RawResponse.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Headers } from "./Headers.js"; - -/** - * The raw response from the fetch call excluding the body. - */ -export type RawResponse = Omit< - { - [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions - }, - "ok" | "body" | "bodyUsed" ->; // strips out body and bodyUsed - -/** - * A raw response indicating that the request was aborted. - */ -export const abortRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 499, - statusText: "Client Closed Request", - type: "error", - url: "", -} as const; - -/** - * A raw response indicating an unknown error. - */ -export const unknownRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 0, - statusText: "Unknown Error", - type: "error", - url: "", -} as const; - -/** - * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, - * excluding the `body` and `bodyUsed` fields. - * - * @param response - The `RawResponse` object to convert. - * @returns A `RawResponse` object containing the extracted properties of the input response. - */ -export function toRawResponse(response: Response): RawResponse { - return { - headers: response.headers, - redirected: response.redirected, - status: response.status, - statusText: response.statusText, - type: response.type, - url: response.url, - }; -} - -/** - * Creates a `RawResponse` from a standard `Response` object. - */ -export interface WithRawResponse { - readonly data: T; - readonly rawResponse: RawResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Supplier.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Supplier.ts deleted file mode 100644 index 867c931c02f4..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/Supplier.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type Supplier = T | Promise | (() => T | Promise); - -export const Supplier = { - get: async (supplier: Supplier): Promise => { - if (typeof supplier === "function") { - return (supplier as () => T)(); - } else { - return supplier; - } - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/createRequestUrl.ts deleted file mode 100644 index 88e13265e112..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/createRequestUrl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { toQueryString } from "../url/qs.js"; - -export function createRequestUrl(baseUrl: string, queryParameters?: Record): string { - const queryString = toQueryString(queryParameters, { arrayFormat: "repeat" }); - return queryString ? `${baseUrl}?${queryString}` : baseUrl; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getErrorResponseBody.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getErrorResponseBody.ts deleted file mode 100644 index 7cf4e623c2f5..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getErrorResponseBody.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { fromJson } from "../json.js"; -import { getResponseBody } from "./getResponseBody.js"; - -export async function getErrorResponseBody(response: Response): Promise { - let contentType = response.headers.get("Content-Type")?.toLowerCase(); - if (contentType == null || contentType.length === 0) { - return getResponseBody(response); - } - - if (contentType.indexOf(";") !== -1) { - contentType = contentType.split(";")[0]?.trim() ?? ""; - } - switch (contentType) { - case "application/hal+json": - case "application/json": - case "application/ld+json": - case "application/problem+json": - case "application/vnd.api+json": - case "text/json": { - const text = await response.text(); - return text.length > 0 ? fromJson(text) : undefined; - } - default: - if (contentType.startsWith("application/vnd.") && contentType.endsWith("+json")) { - const text = await response.text(); - return text.length > 0 ? fromJson(text) : undefined; - } - - // Fallback to plain text if content type is not recognized - // Even if no body is present, the response will be an empty string - return await response.text(); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getFetchFn.ts deleted file mode 100644 index 9f845b956392..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getFetchFn.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function getFetchFn(): Promise { - return fetch; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getHeader.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getHeader.ts deleted file mode 100644 index 50f922b0e87f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getHeader.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function getHeader(headers: Record, header: string): string | undefined { - for (const [headerKey, headerValue] of Object.entries(headers)) { - if (headerKey.toLowerCase() === header.toLowerCase()) { - return headerValue; - } - } - return undefined; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getRequestBody.ts deleted file mode 100644 index 91d9d81f50e5..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getRequestBody.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { toJson } from "../json.js"; -import { toQueryString } from "../url/qs.js"; - -export declare namespace GetRequestBody { - interface Args { - body: unknown; - type: "json" | "file" | "bytes" | "form" | "other"; - } -} - -export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { - if (type === "form") { - return toQueryString(body, { arrayFormat: "repeat", encode: true }); - } - if (type.includes("json")) { - return toJson(body); - } else { - return body as BodyInit; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getResponseBody.ts deleted file mode 100644 index 708d55728f2b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/getResponseBody.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { fromJson } from "../json.js"; -import { getBinaryResponse } from "./BinaryResponse.js"; - -export async function getResponseBody(response: Response, responseType?: string): Promise { - switch (responseType) { - case "binary-response": - return getBinaryResponse(response); - case "blob": - return await response.blob(); - case "arrayBuffer": - return await response.arrayBuffer(); - case "sse": - if (response.body == null) { - return { - ok: false, - error: { - reason: "body-is-null", - statusCode: response.status, - }, - }; - } - return response.body; - case "streaming": - if (response.body == null) { - return { - ok: false, - error: { - reason: "body-is-null", - statusCode: response.status, - }, - }; - } - - return response.body; - - case "text": - return await response.text(); - } - - // if responseType is "json" or not specified, try to parse as JSON - const text = await response.text(); - if (text.length > 0) { - try { - const responseBody = fromJson(text); - return responseBody; - } catch (_err) { - return { - ok: false, - error: { - reason: "non-json", - statusCode: response.status, - rawBody: text, - }, - }; - } - } - return undefined; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/index.ts deleted file mode 100644 index c3bc6da20f49..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type { APIResponse } from "./APIResponse.js"; -export type { BinaryResponse } from "./BinaryResponse.js"; -export type { EndpointMetadata } from "./EndpointMetadata.js"; -export { EndpointSupplier } from "./EndpointSupplier.js"; -export type { Fetcher, FetchFunction } from "./Fetcher.js"; -export { fetcher } from "./Fetcher.js"; -export { getHeader } from "./getHeader.js"; -export { HttpResponsePromise } from "./HttpResponsePromise.js"; -export type { RawResponse, WithRawResponse } from "./RawResponse.js"; -export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -export { Supplier } from "./Supplier.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/makeRequest.ts deleted file mode 100644 index 921565eb0063..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/makeRequest.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { anySignal, getTimeoutSignal } from "./signals.js"; - -export const makeRequest = async ( - fetchFn: (url: string, init: RequestInit) => Promise, - url: string, - method: string, - headers: Headers | Record, - requestBody: BodyInit | undefined, - timeoutMs?: number, - abortSignal?: AbortSignal, - withCredentials?: boolean, - duplex?: "half", -): Promise => { - const signals: AbortSignal[] = []; - - let timeoutAbortId: ReturnType | undefined; - if (timeoutMs != null) { - const { signal, abortId } = getTimeoutSignal(timeoutMs); - timeoutAbortId = abortId; - signals.push(signal); - } - - if (abortSignal != null) { - signals.push(abortSignal); - } - const newSignals = anySignal(signals); - const response = await fetchFn(url, { - method: method, - headers, - body: requestBody, - signal: newSignals, - credentials: withCredentials ? "include" : undefined, - // @ts-ignore - duplex, - }); - - if (timeoutAbortId != null) { - clearTimeout(timeoutAbortId); - } - - return response; -}; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/requestWithRetries.ts deleted file mode 100644 index 1f689688c4b2..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/requestWithRetries.ts +++ /dev/null @@ -1,64 +0,0 @@ -const INITIAL_RETRY_DELAY = 1000; // in milliseconds -const MAX_RETRY_DELAY = 60000; // in milliseconds -const DEFAULT_MAX_RETRIES = 2; -const JITTER_FACTOR = 0.2; // 20% random jitter - -function addPositiveJitter(delay: number): number { - const jitterMultiplier = 1 + Math.random() * JITTER_FACTOR; - return delay * jitterMultiplier; -} - -function addSymmetricJitter(delay: number): number { - const jitterMultiplier = 1 + (Math.random() - 0.5) * JITTER_FACTOR; - return delay * jitterMultiplier; -} - -function getRetryDelayFromHeaders(response: Response, retryAttempt: number): number { - const retryAfter = response.headers.get("Retry-After"); - if (retryAfter) { - const retryAfterSeconds = parseInt(retryAfter, 10); - if (!Number.isNaN(retryAfterSeconds) && retryAfterSeconds > 0) { - return Math.min(retryAfterSeconds * 1000, MAX_RETRY_DELAY); - } - - const retryAfterDate = new Date(retryAfter); - if (!Number.isNaN(retryAfterDate.getTime())) { - const delay = retryAfterDate.getTime() - Date.now(); - if (delay > 0) { - return Math.min(Math.max(delay, 0), MAX_RETRY_DELAY); - } - } - } - - const rateLimitReset = response.headers.get("X-RateLimit-Reset"); - if (rateLimitReset) { - const resetTime = parseInt(rateLimitReset, 10); - if (!Number.isNaN(resetTime)) { - const delay = resetTime * 1000 - Date.now(); - if (delay > 0) { - return addPositiveJitter(Math.min(delay, MAX_RETRY_DELAY)); - } - } - } - - return addSymmetricJitter(Math.min(INITIAL_RETRY_DELAY * 2 ** retryAttempt, MAX_RETRY_DELAY)); -} - -export async function requestWithRetries( - requestFn: () => Promise, - maxRetries: number = DEFAULT_MAX_RETRIES, -): Promise { - let response: Response = await requestFn(); - - for (let i = 0; i < maxRetries; ++i) { - if ([408, 429].includes(response.status) || response.status >= 500) { - const delay = getRetryDelayFromHeaders(response, i); - - await new Promise((resolve) => setTimeout(resolve, delay)); - response = await requestFn(); - } else { - break; - } - } - return response!; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/signals.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/signals.ts deleted file mode 100644 index 7bd3757ec3a7..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/fetcher/signals.ts +++ /dev/null @@ -1,26 +0,0 @@ -const TIMEOUT = "timeout"; - -export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: ReturnType } { - const controller = new AbortController(); - const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); - return { signal: controller.signal, abortId }; -} - -export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { - const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; - - const controller = new AbortController(); - - for (const signal of signals) { - if (signal.aborted) { - controller.abort((signal as any)?.reason); - break; - } - - signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { - signal: controller.signal, - }); - } - - return controller.signal; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/headers.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/headers.ts deleted file mode 100644 index 78ed8b500c95..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/headers.ts +++ /dev/null @@ -1,35 +0,0 @@ -export function mergeHeaders( - ...headersArray: (Record | null | undefined)[] -): Record { - const result: Record = {}; - - for (const [key, value] of headersArray - .filter((headers) => headers != null) - .flatMap((headers) => Object.entries(headers))) { - const insensitiveKey = key.toLowerCase(); - if (value != null) { - result[insensitiveKey] = value; - } else if (insensitiveKey in result) { - delete result[insensitiveKey]; - } - } - - return result; -} - -export function mergeOnlyDefinedHeaders( - ...headersArray: (Record | null | undefined)[] -): Record { - const result: Record = {}; - - for (const [key, value] of headersArray - .filter((headers) => headers != null) - .flatMap((headers) => Object.entries(headers))) { - const insensitiveKey = key.toLowerCase(); - if (value != null) { - result[insensitiveKey] = value; - } - } - - return result; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/index.ts deleted file mode 100644 index 92290bfadcac..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./auth/index.js"; -export * from "./base64.js"; -export * from "./fetcher/index.js"; -export * as logging from "./logging/index.js"; -export * from "./runtime/index.js"; -export * as url from "./url/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/json.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/json.ts deleted file mode 100644 index c052f3249f4f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/json.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Serialize a value to JSON - * @param value A JavaScript value, usually an object or array, to be converted. - * @param replacer A function that transforms the results. - * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. - * @returns JSON string - */ -export const toJson = ( - value: unknown, - replacer?: (this: unknown, key: string, value: unknown) => unknown, - space?: string | number, -): string => { - return JSON.stringify(value, replacer, space); -}; - -/** - * Parse JSON string to object, array, or other type - * @param text A valid JSON string. - * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. - * @returns Parsed object, array, or other type - */ -export function fromJson( - text: string, - reviver?: (this: unknown, key: string, value: unknown) => unknown, -): T { - return JSON.parse(text, reviver); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/exports.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/exports.ts deleted file mode 100644 index 88f6c00db0cf..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/exports.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as logger from "./logger.js"; - -export namespace logging { - /** - * Configuration for logger instances. - */ - export type LogConfig = logger.LogConfig; - export type LogLevel = logger.LogLevel; - export const LogLevel: typeof logger.LogLevel = logger.LogLevel; - export type ILogger = logger.ILogger; - /** - * Console logger implementation that outputs to the console. - */ - export type ConsoleLogger = logger.ConsoleLogger; - /** - * Console logger implementation that outputs to the console. - */ - export const ConsoleLogger: typeof logger.ConsoleLogger = logger.ConsoleLogger; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/index.ts deleted file mode 100644 index d81cc32c40f9..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./logger.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/logger.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/logger.ts deleted file mode 100644 index a3f3673cda93..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/logging/logger.ts +++ /dev/null @@ -1,203 +0,0 @@ -export const LogLevel = { - Debug: "debug", - Info: "info", - Warn: "warn", - Error: "error", -} as const; -export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; -const logLevelMap: Record = { - [LogLevel.Debug]: 1, - [LogLevel.Info]: 2, - [LogLevel.Warn]: 3, - [LogLevel.Error]: 4, -}; - -export interface ILogger { - /** - * Logs a debug message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - debug(message: string, ...args: unknown[]): void; - /** - * Logs an info message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - info(message: string, ...args: unknown[]): void; - /** - * Logs a warning message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - warn(message: string, ...args: unknown[]): void; - /** - * Logs an error message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - error(message: string, ...args: unknown[]): void; -} - -/** - * Configuration for logger initialization. - */ -export interface LogConfig { - /** - * Minimum log level to output. - * @default LogLevel.Info - */ - level?: LogLevel; - /** - * Logger implementation to use. - * @default new ConsoleLogger() - */ - logger?: ILogger; - /** - * Whether logging should be silenced. - * @default true - */ - silent?: boolean; -} - -/** - * Default console-based logger implementation. - */ -export class ConsoleLogger implements ILogger { - debug(message: string, ...args: unknown[]): void { - console.debug(message, ...args); - } - info(message: string, ...args: unknown[]): void { - console.info(message, ...args); - } - warn(message: string, ...args: unknown[]): void { - console.warn(message, ...args); - } - error(message: string, ...args: unknown[]): void { - console.error(message, ...args); - } -} - -/** - * Logger class that provides level-based logging functionality. - */ -export class Logger { - private readonly level: number; - private readonly logger: ILogger; - private readonly silent: boolean; - - /** - * Creates a new logger instance. - * @param config - Logger configuration - */ - constructor(config: Required) { - this.level = logLevelMap[config.level]; - this.logger = config.logger; - this.silent = config.silent; - } - - /** - * Checks if a log level should be output based on configuration. - * @param level - The log level to check - * @returns True if the level should be logged - */ - public shouldLog(level: LogLevel): boolean { - return !this.silent && this.level <= logLevelMap[level]; - } - - /** - * Checks if debug logging is enabled. - * @returns True if debug logs should be output - */ - public isDebug(): boolean { - return this.shouldLog(LogLevel.Debug); - } - - /** - * Logs a debug message if debug logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public debug(message: string, ...args: unknown[]): void { - if (this.isDebug()) { - this.logger.debug(message, ...args); - } - } - - /** - * Checks if info logging is enabled. - * @returns True if info logs should be output - */ - public isInfo(): boolean { - return this.shouldLog(LogLevel.Info); - } - - /** - * Logs an info message if info logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public info(message: string, ...args: unknown[]): void { - if (this.isInfo()) { - this.logger.info(message, ...args); - } - } - - /** - * Checks if warning logging is enabled. - * @returns True if warning logs should be output - */ - public isWarn(): boolean { - return this.shouldLog(LogLevel.Warn); - } - - /** - * Logs a warning message if warning logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public warn(message: string, ...args: unknown[]): void { - if (this.isWarn()) { - this.logger.warn(message, ...args); - } - } - - /** - * Checks if error logging is enabled. - * @returns True if error logs should be output - */ - public isError(): boolean { - return this.shouldLog(LogLevel.Error); - } - - /** - * Logs an error message if error logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public error(message: string, ...args: unknown[]): void { - if (this.isError()) { - this.logger.error(message, ...args); - } - } -} - -export function createLogger(config?: LogConfig | Logger): Logger { - if (config == null) { - return defaultLogger; - } - if (config instanceof Logger) { - return config; - } - config = config ?? {}; - config.level ??= LogLevel.Info; - config.logger ??= new ConsoleLogger(); - config.silent ??= true; - return new Logger(config as Required); -} - -const defaultLogger: Logger = new Logger({ - level: LogLevel.Info, - logger: new ConsoleLogger(), - silent: true, -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/index.ts deleted file mode 100644 index cfab23f9a834..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { RUNTIME } from "./runtime.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/runtime.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/runtime.ts deleted file mode 100644 index 56ebbb87c4d3..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/runtime/runtime.ts +++ /dev/null @@ -1,134 +0,0 @@ -interface DenoGlobal { - version: { - deno: string; - }; -} - -interface BunGlobal { - version: string; -} - -declare const Deno: DenoGlobal | undefined; -declare const Bun: BunGlobal | undefined; -declare const EdgeRuntime: string | undefined; -declare const self: typeof globalThis.self & { - importScripts?: unknown; -}; - -/** - * A constant that indicates which environment and version the SDK is running in. - */ -export const RUNTIME: Runtime = evaluateRuntime(); - -export interface Runtime { - type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; - version?: string; - parsedVersion?: number; -} - -function evaluateRuntime(): Runtime { - /** - * A constant that indicates whether the environment the code is running is a Web Browser. - */ - const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; - if (isBrowser) { - return { - type: "browser", - version: window.navigator.userAgent, - }; - } - - /** - * A constant that indicates whether the environment the code is running is Cloudflare. - * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent - */ - const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; - if (isCloudflare) { - return { - type: "workerd", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Edge Runtime. - * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime - */ - const isEdgeRuntime = typeof EdgeRuntime === "string"; - if (isEdgeRuntime) { - return { - type: "edge-runtime", - }; - } - - /** - * A constant that indicates whether the environment the code is running is a Web Worker. - */ - const isWebWorker = - typeof self === "object" && - typeof self?.importScripts === "function" && - (self.constructor?.name === "DedicatedWorkerGlobalScope" || - self.constructor?.name === "ServiceWorkerGlobalScope" || - self.constructor?.name === "SharedWorkerGlobalScope"); - if (isWebWorker) { - return { - type: "web-worker", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Deno. - * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions - */ - const isDeno = - typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; - if (isDeno) { - return { - type: "deno", - version: Deno.version.deno, - }; - } - - /** - * A constant that indicates whether the environment the code is running is Bun.sh. - */ - const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; - if (isBun) { - return { - type: "bun", - version: Bun.version, - }; - } - - /** - * A constant that indicates whether the environment the code is running is in React-Native. - * This check should come before Node.js detection since React Native may have a process polyfill. - * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js - */ - const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; - if (isReactNative) { - return { - type: "react-native", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Node.JS. - */ - const isNode = - typeof process !== "undefined" && - "version" in process && - !!process.version && - "versions" in process && - !!process.versions?.node; - if (isNode) { - return { - type: "node", - version: process.versions.node, - parsedVersion: Number(process.versions.node.split(".")[0]), - }; - } - - return { - type: "unknown", - }; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/encodePathParam.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/encodePathParam.ts deleted file mode 100644 index 19b901244218..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/encodePathParam.ts +++ /dev/null @@ -1,18 +0,0 @@ -export function encodePathParam(param: unknown): string { - if (param === null) { - return "null"; - } - const typeofParam = typeof param; - switch (typeofParam) { - case "undefined": - return "undefined"; - case "string": - case "number": - case "boolean": - break; - default: - param = String(param); - break; - } - return encodeURIComponent(param as string | number | boolean); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/index.ts deleted file mode 100644 index f2e0fa2d2221..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { encodePathParam } from "./encodePathParam.js"; -export { join } from "./join.js"; -export { toQueryString } from "./qs.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/join.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/join.ts deleted file mode 100644 index 7ca7daef094d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/join.ts +++ /dev/null @@ -1,79 +0,0 @@ -export function join(base: string, ...segments: string[]): string { - if (!base) { - return ""; - } - - if (segments.length === 0) { - return base; - } - - if (base.includes("://")) { - let url: URL; - try { - url = new URL(base); - } catch { - return joinPath(base, ...segments); - } - - const lastSegment = segments[segments.length - 1]; - const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); - - for (const segment of segments) { - const cleanSegment = trimSlashes(segment); - if (cleanSegment) { - url.pathname = joinPathSegments(url.pathname, cleanSegment); - } - } - - if (shouldPreserveTrailingSlash && !url.pathname.endsWith("/")) { - url.pathname += "/"; - } - - return url.toString(); - } - - return joinPath(base, ...segments); -} - -function joinPath(base: string, ...segments: string[]): string { - if (segments.length === 0) { - return base; - } - - let result = base; - - const lastSegment = segments[segments.length - 1]; - const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); - - for (const segment of segments) { - const cleanSegment = trimSlashes(segment); - if (cleanSegment) { - result = joinPathSegments(result, cleanSegment); - } - } - - if (shouldPreserveTrailingSlash && !result.endsWith("/")) { - result += "/"; - } - - return result; -} - -function joinPathSegments(left: string, right: string): string { - if (left.endsWith("/")) { - return left + right; - } - return `${left}/${right}`; -} - -function trimSlashes(str: string): string { - if (!str) return str; - - let start = 0; - let end = str.length; - - if (str.startsWith("/")) start = 1; - if (str.endsWith("/")) end = str.length - 1; - - return start === 0 && end === str.length ? str : str.slice(start, end); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/qs.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/qs.ts deleted file mode 100644 index 13e89be9d9a6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/core/url/qs.ts +++ /dev/null @@ -1,74 +0,0 @@ -interface QueryStringOptions { - arrayFormat?: "indices" | "repeat"; - encode?: boolean; -} - -const defaultQsOptions: Required = { - arrayFormat: "indices", - encode: true, -} as const; - -function encodeValue(value: unknown, shouldEncode: boolean): string { - if (value === undefined) { - return ""; - } - if (value === null) { - return ""; - } - const stringValue = String(value); - return shouldEncode ? encodeURIComponent(stringValue) : stringValue; -} - -function stringifyObject(obj: Record, prefix = "", options: Required): string[] { - const parts: string[] = []; - - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}[${key}]` : key; - - if (value === undefined) { - continue; - } - - if (Array.isArray(value)) { - if (value.length === 0) { - continue; - } - for (let i = 0; i < value.length; i++) { - const item = value[i]; - if (item === undefined) { - continue; - } - if (typeof item === "object" && !Array.isArray(item) && item !== null) { - const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; - parts.push(...stringifyObject(item as Record, arrayKey, options)); - } else { - const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; - const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey; - parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`); - } - } - } else if (typeof value === "object" && value !== null) { - if (Object.keys(value as Record).length === 0) { - continue; - } - parts.push(...stringifyObject(value as Record, fullKey, options)); - } else { - const encodedKey = options.encode ? encodeURIComponent(fullKey) : fullKey; - parts.push(`${encodedKey}=${encodeValue(value, options.encode)}`); - } - } - - return parts; -} - -export function toQueryString(obj: unknown, options?: QueryStringOptions): string { - if (obj == null || typeof obj !== "object") { - return ""; - } - - const parts = stringifyObject(obj as Record, "", { - ...defaultQsOptions, - ...options, - }); - return parts.join("&"); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesError.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesError.ts deleted file mode 100644 index c7eecf96312a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesError.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as core from "../core/index.js"; -import { toJson } from "../core/json.js"; - -export class SeedOauthClientCredentialsEnvironmentVariablesError extends Error { - public readonly statusCode?: number; - public readonly body?: unknown; - public readonly rawResponse?: core.RawResponse; - - constructor({ - message, - statusCode, - body, - rawResponse, - }: { - message?: string; - statusCode?: number; - body?: unknown; - rawResponse?: core.RawResponse; - }) { - super(buildMessage({ message, statusCode, body })); - Object.setPrototypeOf(this, new.target.prototype); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = this.constructor.name; - this.statusCode = statusCode; - this.body = body; - this.rawResponse = rawResponse; - } -} - -function buildMessage({ - message, - statusCode, - body, -}: { - message: string | undefined; - statusCode: number | undefined; - body: unknown | undefined; -}): string { - const lines: string[] = []; - if (message != null) { - lines.push(message); - } - - if (statusCode != null) { - lines.push(`Status code: ${statusCode.toString()}`); - } - - if (body != null) { - lines.push(`Body: ${toJson(body, undefined, 2)}`); - } - - return lines.join("\n"); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesTimeoutError.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesTimeoutError.ts deleted file mode 100644 index 89b0a2166fee..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/SeedOauthClientCredentialsEnvironmentVariablesTimeoutError.ts +++ /dev/null @@ -1,13 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -export class SeedOauthClientCredentialsEnvironmentVariablesTimeoutError extends Error { - constructor(message: string) { - super(message); - Object.setPrototypeOf(this, new.target.prototype); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = this.constructor.name; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/handleNonStatusCodeError.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/handleNonStatusCodeError.ts deleted file mode 100644 index 081f0eda51d4..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/handleNonStatusCodeError.ts +++ /dev/null @@ -1,39 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as core from "../core/index.js"; -import * as errors from "./index.js"; - -export function handleNonStatusCodeError( - error: core.Fetcher.Error, - rawResponse: core.RawResponse, - method: string, - path: string, -): never { - switch (error.reason) { - case "non-json": - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: error.statusCode, - body: error.rawBody, - rawResponse: rawResponse, - }); - case "body-is-null": - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - statusCode: error.statusCode, - rawResponse: rawResponse, - }); - case "timeout": - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesTimeoutError( - `Timeout exceeded when calling ${method} ${path}.`, - ); - case "unknown": - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: error.errorMessage, - rawResponse: rawResponse, - }); - default: - throw new errors.SeedOauthClientCredentialsEnvironmentVariablesError({ - message: "Unknown error", - rawResponse: rawResponse, - }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/index.ts deleted file mode 100644 index c6ee047ba31c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { SeedOauthClientCredentialsEnvironmentVariablesError } from "./SeedOauthClientCredentialsEnvironmentVariablesError.js"; -export { SeedOauthClientCredentialsEnvironmentVariablesTimeoutError } from "./SeedOauthClientCredentialsEnvironmentVariablesTimeoutError.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/exports.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/exports.ts deleted file mode 100644 index 7b70ee14fc02..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/exports.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./core/exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/index.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/index.ts deleted file mode 100644 index 1602329b1d31..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * as SeedOauthClientCredentialsEnvironmentVariables from "./api/index.js"; -export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -export { SeedOauthClientCredentialsEnvironmentVariablesClient } from "./Client.js"; -export { - SeedOauthClientCredentialsEnvironmentVariablesError, - SeedOauthClientCredentialsEnvironmentVariablesTimeoutError, -} from "./errors/index.js"; -export * from "./exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/version.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/version.ts deleted file mode 100644 index b643a3e3ea27..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/src/version.ts +++ /dev/null @@ -1 +0,0 @@ -export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/custom.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/custom.test.ts deleted file mode 100644 index 7f5e031c8396..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/custom.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This is a custom test file, if you wish to add more tests - * to your SDK. - * Be sure to mark this file in `.fernignore`. - * - * If you include example requests/responses in your fern definition, - * you will have tests automatically generated for you. - */ -describe("test", () => { - it("default", () => { - expect(true).toBe(true); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServer.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServer.ts deleted file mode 100644 index 954872157d52..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServer.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { RequestHandlerOptions } from "msw"; -import type { SetupServer } from "msw/node"; - -import { mockEndpointBuilder } from "./mockEndpointBuilder"; - -export interface MockServerOptions { - baseUrl: string; - server: SetupServer; -} - -export class MockServer { - private readonly server: SetupServer; - public readonly baseUrl: string; - - constructor({ baseUrl, server }: MockServerOptions) { - this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; - this.server = server; - } - - public mockEndpoint(options?: RequestHandlerOptions): ReturnType { - const builder = mockEndpointBuilder({ - once: options?.once ?? true, - onBuild: (handler) => { - this.server.use(handler); - }, - }).baseUrl(this.baseUrl); - return builder; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServerPool.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServerPool.ts deleted file mode 100644 index e1a90f7fb2e3..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/MockServerPool.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { setupServer } from "msw/node"; - -import { fromJson, toJson } from "../../src/core/json"; -import { MockServer } from "./MockServer"; -import { randomBaseUrl } from "./randomBaseUrl"; - -const mswServer = setupServer(); -interface MockServerOptions { - baseUrl?: string; -} - -async function formatHttpRequest(request: Request, id?: string): Promise { - try { - const clone = request.clone(); - const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); - - let body = ""; - try { - const contentType = clone.headers.get("content-type"); - if (contentType?.includes("application/json")) { - body = toJson(fromJson(await clone.text()), undefined, 2); - } else if (clone.body) { - body = await clone.text(); - } - } catch (_e) { - body = "(unable to parse body)"; - } - - const title = id ? `### Request ${id} ###\n` : ""; - const firstLine = `${title}${request.method} ${request.url.toString()} HTTP/1.1`; - - return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; - } catch (e) { - return `Error formatting request: ${e}`; - } -} - -async function formatHttpResponse(response: Response, id?: string): Promise { - try { - const clone = response.clone(); - const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); - - let body = ""; - try { - const contentType = clone.headers.get("content-type"); - if (contentType?.includes("application/json")) { - body = toJson(fromJson(await clone.text()), undefined, 2); - } else if (clone.body) { - body = await clone.text(); - } - } catch (_e) { - body = "(unable to parse body)"; - } - - const title = id ? `### Response for ${id} ###\n` : ""; - const firstLine = `${title}HTTP/1.1 ${response.status} ${response.statusText}`; - - return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; - } catch (e) { - return `Error formatting response: ${e}`; - } -} - -class MockServerPool { - private servers: MockServer[] = []; - - public createServer(options?: Partial): MockServer { - const baseUrl = options?.baseUrl || randomBaseUrl(); - const server = new MockServer({ baseUrl, server: mswServer }); - this.servers.push(server); - return server; - } - - public getServers(): MockServer[] { - return [...this.servers]; - } - - public listen(): void { - const onUnhandledRequest = process.env.LOG_LEVEL === "debug" ? "warn" : "bypass"; - mswServer.listen({ onUnhandledRequest }); - - if (process.env.LOG_LEVEL === "debug") { - mswServer.events.on("request:start", async ({ request, requestId }) => { - const formattedRequest = await formatHttpRequest(request, requestId); - console.debug(`request:start\n${formattedRequest}`); - }); - - mswServer.events.on("request:unhandled", async ({ request, requestId }) => { - const formattedRequest = await formatHttpRequest(request, requestId); - console.debug(`request:unhandled\n${formattedRequest}`); - }); - - mswServer.events.on("response:mocked", async ({ request, response, requestId }) => { - const formattedResponse = await formatHttpResponse(response, requestId); - console.debug(`response:mocked\n${formattedResponse}`); - }); - } - } - - public close(): void { - this.servers = []; - mswServer.close(); - } -} - -export const mockServerPool = new MockServerPool(); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/mockEndpointBuilder.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/mockEndpointBuilder.ts deleted file mode 100644 index 1b0e51079e6b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/mockEndpointBuilder.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { type DefaultBodyType, type HttpHandler, HttpResponse, type HttpResponseResolver, http } from "msw"; - -import { url } from "../../src/core"; -import { toJson } from "../../src/core/json"; -import { withFormUrlEncoded } from "./withFormUrlEncoded"; -import { withHeaders } from "./withHeaders"; -import { withJson } from "./withJson"; - -type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; - -interface MethodStage { - baseUrl(baseUrl: string): MethodStage; - all(path: string): RequestHeadersStage; - get(path: string): RequestHeadersStage; - post(path: string): RequestHeadersStage; - put(path: string): RequestHeadersStage; - delete(path: string): RequestHeadersStage; - patch(path: string): RequestHeadersStage; - options(path: string): RequestHeadersStage; - head(path: string): RequestHeadersStage; -} - -interface RequestHeadersStage extends RequestBodyStage, ResponseStage { - header(name: string, value: string): RequestHeadersStage; - headers(headers: Record): RequestBodyStage; -} - -interface RequestBodyStage extends ResponseStage { - jsonBody(body: unknown): ResponseStage; - formUrlEncodedBody(body: unknown): ResponseStage; -} - -interface ResponseStage { - respondWith(): ResponseStatusStage; -} -interface ResponseStatusStage { - statusCode(statusCode: number): ResponseHeaderStage; -} - -interface ResponseHeaderStage extends ResponseBodyStage, BuildStage { - header(name: string, value: string): ResponseHeaderStage; - headers(headers: Record): ResponseHeaderStage; -} - -interface ResponseBodyStage { - jsonBody(body: unknown): BuildStage; -} - -interface BuildStage { - build(): HttpHandler; -} - -export interface HttpHandlerBuilderOptions { - onBuild?: (handler: HttpHandler) => void; - once?: boolean; -} - -class RequestBuilder implements MethodStage, RequestHeadersStage, RequestBodyStage, ResponseStage { - private method: HttpMethod = "get"; - private _baseUrl: string = ""; - private path: string = "/"; - private readonly predicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[] = []; - private readonly handlerOptions?: HttpHandlerBuilderOptions; - - constructor(options?: HttpHandlerBuilderOptions) { - this.handlerOptions = options; - } - - baseUrl(baseUrl: string): MethodStage { - this._baseUrl = baseUrl; - return this; - } - - all(path: string): RequestHeadersStage { - this.method = "all"; - this.path = path; - return this; - } - - get(path: string): RequestHeadersStage { - this.method = "get"; - this.path = path; - return this; - } - - post(path: string): RequestHeadersStage { - this.method = "post"; - this.path = path; - return this; - } - - put(path: string): RequestHeadersStage { - this.method = "put"; - this.path = path; - return this; - } - - delete(path: string): RequestHeadersStage { - this.method = "delete"; - this.path = path; - return this; - } - - patch(path: string): RequestHeadersStage { - this.method = "patch"; - this.path = path; - return this; - } - - options(path: string): RequestHeadersStage { - this.method = "options"; - this.path = path; - return this; - } - - head(path: string): RequestHeadersStage { - this.method = "head"; - this.path = path; - return this; - } - - header(name: string, value: string): RequestHeadersStage { - this.predicates.push((resolver) => withHeaders({ [name]: value }, resolver)); - return this; - } - - headers(headers: Record): RequestBodyStage { - this.predicates.push((resolver) => withHeaders(headers, resolver)); - return this; - } - - jsonBody(body: unknown): ResponseStage { - if (body === undefined) { - throw new Error("Undefined is not valid JSON. Do not call jsonBody if you want an empty body."); - } - this.predicates.push((resolver) => withJson(body, resolver)); - return this; - } - - formUrlEncodedBody(body: unknown): ResponseStage { - if (body === undefined) { - throw new Error( - "Undefined is not valid for form-urlencoded. Do not call formUrlEncodedBody if you want an empty body.", - ); - } - this.predicates.push((resolver) => withFormUrlEncoded(body, resolver)); - return this; - } - - respondWith(): ResponseStatusStage { - return new ResponseBuilder(this.method, this.buildUrl(), this.predicates, this.handlerOptions); - } - - private buildUrl(): string { - return url.join(this._baseUrl, this.path); - } -} - -class ResponseBuilder implements ResponseStatusStage, ResponseHeaderStage, ResponseBodyStage, BuildStage { - private readonly method: HttpMethod; - private readonly url: string; - private readonly requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[]; - private readonly handlerOptions?: HttpHandlerBuilderOptions; - - private responseStatusCode: number = 200; - private responseHeaders: Record = {}; - private responseBody: DefaultBodyType = undefined; - - constructor( - method: HttpMethod, - url: string, - requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[], - options?: HttpHandlerBuilderOptions, - ) { - this.method = method; - this.url = url; - this.requestPredicates = requestPredicates; - this.handlerOptions = options; - } - - public statusCode(code: number): ResponseHeaderStage { - this.responseStatusCode = code; - return this; - } - - public header(name: string, value: string): ResponseHeaderStage { - this.responseHeaders[name] = value; - return this; - } - - public headers(headers: Record): ResponseHeaderStage { - this.responseHeaders = { ...this.responseHeaders, ...headers }; - return this; - } - - public jsonBody(body: unknown): BuildStage { - if (body === undefined) { - throw new Error("Undefined is not valid JSON. Do not call jsonBody if you expect an empty body."); - } - this.responseBody = toJson(body); - return this; - } - - public build(): HttpHandler { - const responseResolver: HttpResponseResolver = () => { - const response = new HttpResponse(this.responseBody, { - status: this.responseStatusCode, - headers: this.responseHeaders, - }); - // if no Content-Type header is set, delete the default text content type that is set - if (Object.keys(this.responseHeaders).some((key) => key.toLowerCase() === "content-type") === false) { - response.headers.delete("Content-Type"); - } - return response; - }; - - const finalResolver = this.requestPredicates.reduceRight((acc, predicate) => predicate(acc), responseResolver); - - const handler = http[this.method](this.url, finalResolver, this.handlerOptions); - this.handlerOptions?.onBuild?.(handler); - return handler; - } -} - -export function mockEndpointBuilder(options?: HttpHandlerBuilderOptions): MethodStage { - return new RequestBuilder(options); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/randomBaseUrl.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/randomBaseUrl.ts deleted file mode 100644 index 031aa6408aca..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/randomBaseUrl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function randomBaseUrl(): string { - const randomString = Math.random().toString(36).substring(2, 15); - return `http://${randomString}.localhost`; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/setup.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/setup.ts deleted file mode 100644 index aeb3a95af7dc..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/setup.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { afterAll, beforeAll } from "vitest"; - -import { mockServerPool } from "./MockServerPool"; - -beforeAll(() => { - mockServerPool.listen(); -}); -afterAll(() => { - mockServerPool.close(); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withFormUrlEncoded.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withFormUrlEncoded.ts deleted file mode 100644 index e9e6ff2d9cf1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withFormUrlEncoded.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -import { toJson } from "../../src/core/json"; - -/** - * Creates a request matcher that validates if the request form-urlencoded body exactly matches the expected object - * @param expectedBody - The exact body object to match against - * @param resolver - Response resolver to execute if body matches - */ -export function withFormUrlEncoded(expectedBody: unknown, resolver: HttpResponseResolver): HttpResponseResolver { - return async (args) => { - const { request } = args; - - let clonedRequest: Request; - let bodyText: string | undefined; - let actualBody: Record; - try { - clonedRequest = request.clone(); - bodyText = await clonedRequest.text(); - if (bodyText === "") { - console.error("Request body is empty, expected a form-urlencoded body."); - return passthrough(); - } - const params = new URLSearchParams(bodyText); - actualBody = {}; - for (const [key, value] of params.entries()) { - actualBody[key] = value; - } - } catch (error) { - console.error(`Error processing form-urlencoded request body:\n\tError: ${error}\n\tBody: ${bodyText}`); - return passthrough(); - } - - const mismatches = findMismatches(actualBody, expectedBody); - if (Object.keys(mismatches).length > 0) { - console.error("Form-urlencoded body mismatch:", toJson(mismatches, undefined, 2)); - return passthrough(); - } - - return resolver(args); - }; -} - -function findMismatches(actual: any, expected: any): Record { - const mismatches: Record = {}; - - if (typeof actual !== typeof expected) { - return { value: { actual, expected } }; - } - - if (typeof actual !== "object" || actual === null || expected === null) { - if (actual !== expected) { - return { value: { actual, expected } }; - } - return {}; - } - - const actualKeys = Object.keys(actual); - const expectedKeys = Object.keys(expected); - - const allKeys = new Set([...actualKeys, ...expectedKeys]); - - for (const key of allKeys) { - if (!expectedKeys.includes(key)) { - if (actual[key] === undefined) { - continue; - } - mismatches[key] = { actual: actual[key], expected: undefined }; - } else if (!actualKeys.includes(key)) { - if (expected[key] === undefined) { - continue; - } - mismatches[key] = { actual: undefined, expected: expected[key] }; - } else if (actual[key] !== expected[key]) { - mismatches[key] = { actual: actual[key], expected: expected[key] }; - } - } - - return mismatches; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withHeaders.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withHeaders.ts deleted file mode 100644 index 6599d2b4a92d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withHeaders.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -/** - * Creates a request matcher that validates if request headers match specified criteria - * @param expectedHeaders - Headers to match against - * @param resolver - Response resolver to execute if headers match - */ -export function withHeaders( - expectedHeaders: Record boolean)>, - resolver: HttpResponseResolver, -): HttpResponseResolver { - return (args) => { - const { request } = args; - const { headers } = request; - - const mismatches: Record< - string, - { actual: string | null; expected: string | RegExp | ((value: string) => boolean) } - > = {}; - - for (const [key, expectedValue] of Object.entries(expectedHeaders)) { - const actualValue = headers.get(key); - - if (actualValue === null) { - mismatches[key] = { actual: null, expected: expectedValue }; - continue; - } - - if (typeof expectedValue === "function") { - if (!expectedValue(actualValue)) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } else if (expectedValue instanceof RegExp) { - if (!expectedValue.test(actualValue)) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } else if (expectedValue !== actualValue) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } - - if (Object.keys(mismatches).length > 0) { - const formattedMismatches = formatHeaderMismatches(mismatches); - console.error("Header mismatch:", formattedMismatches); - return passthrough(); - } - - return resolver(args); - }; -} - -function formatHeaderMismatches( - mismatches: Record boolean) }>, -): Record { - const formatted: Record = {}; - - for (const [key, { actual, expected }] of Object.entries(mismatches)) { - formatted[key] = { - actual, - expected: - expected instanceof RegExp - ? expected.toString() - : typeof expected === "function" - ? "[Function]" - : expected, - }; - } - - return formatted; -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withJson.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withJson.ts deleted file mode 100644 index b627638b015f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/mock-server/withJson.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -import { fromJson, toJson } from "../../src/core/json"; - -/** - * Creates a request matcher that validates if the request JSON body exactly matches the expected object - * @param expectedBody - The exact body object to match against - * @param resolver - Response resolver to execute if body matches - */ -export function withJson(expectedBody: unknown, resolver: HttpResponseResolver): HttpResponseResolver { - return async (args) => { - const { request } = args; - - let clonedRequest: Request; - let bodyText: string | undefined; - let actualBody: unknown; - try { - clonedRequest = request.clone(); - bodyText = await clonedRequest.text(); - if (bodyText === "") { - console.error("Request body is empty, expected a JSON object."); - return passthrough(); - } - actualBody = fromJson(bodyText); - } catch (error) { - console.error(`Error processing request body:\n\tError: ${error}\n\tBody: ${bodyText}`); - return passthrough(); - } - - const mismatches = findMismatches(actualBody, expectedBody); - if (Object.keys(mismatches).filter((key) => !key.startsWith("pagination.")).length > 0) { - console.error("JSON body mismatch:", toJson(mismatches, undefined, 2)); - return passthrough(); - } - - return resolver(args); - }; -} - -function findMismatches(actual: any, expected: any): Record { - const mismatches: Record = {}; - - if (typeof actual !== typeof expected) { - if (areEquivalent(actual, expected)) { - return {}; - } - return { value: { actual, expected } }; - } - - if (typeof actual !== "object" || actual === null || expected === null) { - if (actual !== expected) { - if (areEquivalent(actual, expected)) { - return {}; - } - return { value: { actual, expected } }; - } - return {}; - } - - if (Array.isArray(actual) && Array.isArray(expected)) { - if (actual.length !== expected.length) { - return { length: { actual: actual.length, expected: expected.length } }; - } - - const arrayMismatches: Record = {}; - for (let i = 0; i < actual.length; i++) { - const itemMismatches = findMismatches(actual[i], expected[i]); - if (Object.keys(itemMismatches).length > 0) { - for (const [mismatchKey, mismatchValue] of Object.entries(itemMismatches)) { - arrayMismatches[`[${i}]${mismatchKey === "value" ? "" : `.${mismatchKey}`}`] = mismatchValue; - } - } - } - return arrayMismatches; - } - - const actualKeys = Object.keys(actual); - const expectedKeys = Object.keys(expected); - - const allKeys = new Set([...actualKeys, ...expectedKeys]); - - for (const key of allKeys) { - if (!expectedKeys.includes(key)) { - if (actual[key] === undefined) { - continue; // Skip undefined values in actual - } - mismatches[key] = { actual: actual[key], expected: undefined }; - } else if (!actualKeys.includes(key)) { - if (expected[key] === undefined) { - continue; // Skip undefined values in expected - } - mismatches[key] = { actual: undefined, expected: expected[key] }; - } else if ( - typeof actual[key] === "object" && - actual[key] !== null && - typeof expected[key] === "object" && - expected[key] !== null - ) { - const nestedMismatches = findMismatches(actual[key], expected[key]); - if (Object.keys(nestedMismatches).length > 0) { - for (const [nestedKey, nestedValue] of Object.entries(nestedMismatches)) { - mismatches[`${key}${nestedKey === "value" ? "" : `.${nestedKey}`}`] = nestedValue; - } - } - } else if (actual[key] !== expected[key]) { - if (areEquivalent(actual[key], expected[key])) { - continue; - } - mismatches[key] = { actual: actual[key], expected: expected[key] }; - } - } - - return mismatches; -} - -function areEquivalent(actual: unknown, expected: unknown): boolean { - if (actual === expected) { - return true; - } - if (isEquivalentBigInt(actual, expected)) { - return true; - } - if (isEquivalentDatetime(actual, expected)) { - return true; - } - return false; -} - -function isEquivalentBigInt(actual: unknown, expected: unknown) { - if (typeof actual === "number") { - actual = BigInt(actual); - } - if (typeof expected === "number") { - expected = BigInt(expected); - } - if (typeof actual === "bigint" && typeof expected === "bigint") { - return actual === expected; - } - return false; -} - -function isEquivalentDatetime(str1: unknown, str2: unknown): boolean { - if (typeof str1 !== "string" || typeof str2 !== "string") { - return false; - } - const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/; - if (!isoDatePattern.test(str1) || !isoDatePattern.test(str2)) { - return false; - } - - try { - const date1 = new Date(str1).getTime(); - const date2 = new Date(str2).getTime(); - return date1 === date2; - } catch { - return false; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/setup.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/setup.ts deleted file mode 100644 index a5651f81ba10..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/setup.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { expect } from "vitest"; - -interface CustomMatchers { - toContainHeaders(expectedHeaders: Record): R; -} - -declare module "vitest" { - interface Assertion extends CustomMatchers {} - interface AsymmetricMatchersContaining extends CustomMatchers {} -} - -expect.extend({ - toContainHeaders(actual: unknown, expectedHeaders: Record) { - const isHeaders = actual instanceof Headers; - const isPlainObject = typeof actual === "object" && actual !== null && !Array.isArray(actual); - - if (!isHeaders && !isPlainObject) { - throw new TypeError("Received value must be an instance of Headers or a plain object!"); - } - - if (typeof expectedHeaders !== "object" || expectedHeaders === null || Array.isArray(expectedHeaders)) { - throw new TypeError("Expected headers must be a plain object!"); - } - - const missingHeaders: string[] = []; - const mismatchedHeaders: Array<{ key: string; expected: string; actual: string | null }> = []; - - for (const [key, value] of Object.entries(expectedHeaders)) { - let actualValue: string | null = null; - - if (isHeaders) { - // Headers.get() is already case-insensitive - actualValue = (actual as Headers).get(key); - } else { - // For plain objects, do case-insensitive lookup - const actualObj = actual as Record; - const lowerKey = key.toLowerCase(); - const foundKey = Object.keys(actualObj).find((k) => k.toLowerCase() === lowerKey); - actualValue = foundKey ? actualObj[foundKey] : null; - } - - if (actualValue === null || actualValue === undefined) { - missingHeaders.push(key); - } else if (actualValue !== value) { - mismatchedHeaders.push({ key, expected: value, actual: actualValue }); - } - } - - const pass = missingHeaders.length === 0 && mismatchedHeaders.length === 0; - - const actualType = isHeaders ? "Headers" : "object"; - - if (pass) { - return { - message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, - pass: true, - }; - } else { - const messages: string[] = []; - - if (missingHeaders.length > 0) { - messages.push(`Missing headers: ${this.utils.printExpected(missingHeaders.join(", "))}`); - } - - if (mismatchedHeaders.length > 0) { - const mismatches = mismatchedHeaders.map( - ({ key, expected, actual }) => - `${key}: expected ${this.utils.printExpected(expected)} but got ${this.utils.printReceived(actual)}`, - ); - messages.push(mismatches.join("\n")); - } - - return { - message: () => - `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, - pass: false, - }; - } - }, -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/tsconfig.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/tsconfig.json deleted file mode 100644 index a477df47920c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": null, - "rootDir": "..", - "baseUrl": "..", - "types": ["vitest/globals"] - }, - "include": ["../src", "../tests"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BasicAuth.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BasicAuth.test.ts deleted file mode 100644 index 9b5123364c47..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BasicAuth.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BasicAuth } from "../../../src/core/auth/BasicAuth"; - -describe("BasicAuth", () => { - interface ToHeaderTestCase { - description: string; - input: { username: string; password: string }; - expected: string; - } - - interface FromHeaderTestCase { - description: string; - input: string; - expected: { username: string; password: string }; - } - - interface ErrorTestCase { - description: string; - input: string; - expectedError: string; - } - - describe("toAuthorizationHeader", () => { - const toHeaderTests: ToHeaderTestCase[] = [ - { - description: "correctly converts to header", - input: { username: "username", password: "password" }, - expected: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", - }, - ]; - - toHeaderTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(BasicAuth.toAuthorizationHeader(input)).toBe(expected); - }); - }); - }); - - describe("fromAuthorizationHeader", () => { - const fromHeaderTests: FromHeaderTestCase[] = [ - { - description: "correctly parses header", - input: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", - expected: { username: "username", password: "password" }, - }, - { - description: "handles password with colons", - input: "Basic dXNlcjpwYXNzOndvcmQ=", - expected: { username: "user", password: "pass:word" }, - }, - { - description: "handles empty username and password (just colon)", - input: "Basic Og==", - expected: { username: "", password: "" }, - }, - { - description: "handles empty username", - input: "Basic OnBhc3N3b3Jk", - expected: { username: "", password: "password" }, - }, - { - description: "handles empty password", - input: "Basic dXNlcm5hbWU6", - expected: { username: "username", password: "" }, - }, - ]; - - fromHeaderTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(BasicAuth.fromAuthorizationHeader(input)).toEqual(expected); - }); - }); - - const errorTests: ErrorTestCase[] = [ - { - description: "throws error for completely empty credentials", - input: "Basic ", - expectedError: "Invalid basic auth", - }, - { - description: "throws error for credentials without colon", - input: "Basic dXNlcm5hbWU=", - expectedError: "Invalid basic auth", - }, - ]; - - errorTests.forEach(({ description, input, expectedError }) => { - it(description, () => { - expect(() => BasicAuth.fromAuthorizationHeader(input)).toThrow(expectedError); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BearerToken.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BearerToken.test.ts deleted file mode 100644 index 7757b87cb97e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/auth/BearerToken.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BearerToken } from "../../../src/core/auth/BearerToken"; - -describe("BearerToken", () => { - describe("toAuthorizationHeader", () => { - it("correctly converts to header", () => { - expect(BearerToken.toAuthorizationHeader("my-token")).toBe("Bearer my-token"); - }); - }); - describe("fromAuthorizationHeader", () => { - it("correctly parses header", () => { - expect(BearerToken.fromAuthorizationHeader("Bearer my-token")).toBe("my-token"); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/base64.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/base64.test.ts deleted file mode 100644 index 939594ca277b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/base64.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { base64Decode, base64Encode } from "../../src/core/base64"; - -describe("base64", () => { - describe("base64Encode", () => { - it("should encode ASCII strings", () => { - expect(base64Encode("hello")).toBe("aGVsbG8="); - expect(base64Encode("")).toBe(""); - }); - - it("should encode UTF-8 strings", () => { - expect(base64Encode("café")).toBe("Y2Fmw6k="); - expect(base64Encode("🎉")).toBe("8J+OiQ=="); - }); - - it("should handle basic auth credentials", () => { - expect(base64Encode("username:password")).toBe("dXNlcm5hbWU6cGFzc3dvcmQ="); - }); - }); - - describe("base64Decode", () => { - it("should decode ASCII strings", () => { - expect(base64Decode("aGVsbG8=")).toBe("hello"); - expect(base64Decode("")).toBe(""); - }); - - it("should decode UTF-8 strings", () => { - expect(base64Decode("Y2Fmw6k=")).toBe("café"); - expect(base64Decode("8J+OiQ==")).toBe("🎉"); - }); - - it("should handle basic auth credentials", () => { - expect(base64Decode("dXNlcm5hbWU6cGFzc3dvcmQ=")).toBe("username:password"); - }); - }); - - describe("round-trip encoding", () => { - const testStrings = [ - "hello world", - "test@example.com", - "café", - "username:password", - "user@domain.com:super$ecret123!", - ]; - - testStrings.forEach((testString) => { - it(`should round-trip encode/decode: "${testString}"`, () => { - const encoded = base64Encode(testString); - const decoded = base64Decode(encoded); - expect(decoded).toBe(testString); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/Fetcher.test.ts deleted file mode 100644 index 2f98ede81d78..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/Fetcher.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -import fs from "fs"; -import { join } from "path"; -import stream from "stream"; -import type { BinaryResponse } from "../../../src/core"; -import { type Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -describe("Test fetcherImpl", () => { - it("should handle successful request", async () => { - const mockArgs: Fetcher.Args = { - url: "https://httpbin.org/post", - method: "POST", - headers: { "X-Test": "x-test-header" }, - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - maxRetries: 0, - responseType: "json", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - }), - ); - - const result = await fetcherImpl(mockArgs); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.body).toEqual({ data: "test" }); - } - - expect(global.fetch).toHaveBeenCalledWith( - "https://httpbin.org/post", - expect.objectContaining({ - method: "POST", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - body: JSON.stringify({ data: "test" }), - }), - ); - }); - - it("should send octet stream", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "POST", - headers: { "X-Test": "x-test-header" }, - contentType: "application/octet-stream", - requestType: "bytes", - maxRetries: 0, - responseType: "json", - body: fs.createReadStream(join(__dirname, "test-file.txt")), - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - }), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "POST", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - body: expect.any(fs.ReadStream), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.body).toEqual({ data: "test" }); - } - }); - - it("should receive file as stream", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.stream).toBe("function"); - const stream = body.stream(); - expect(stream).toBeInstanceOf(ReadableStream); - const readableStream = stream as ReadableStream; - const reader = readableStream.getReader(); - const { value } = await reader.read(); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as blob", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.blob).toBe("function"); - const blob = await body.blob(); - expect(blob).toBeInstanceOf(Blob); - const reader = blob.stream().getReader(); - const { value } = await reader.read(); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as arraybuffer", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.arrayBuffer).toBe("function"); - const arrayBuffer = await body.arrayBuffer(); - expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(new Uint8Array(arrayBuffer)); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as bytes", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.bytes).toBe("function"); - if (!body.bytes) { - return; - } - const bytes = await body.bytes(); - expect(bytes).toBeInstanceOf(Uint8Array); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(bytes); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts deleted file mode 100644 index 2ec008e581d8..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { HttpResponsePromise } from "../../../src/core/fetcher/HttpResponsePromise"; -import type { RawResponse, WithRawResponse } from "../../../src/core/fetcher/RawResponse"; - -describe("HttpResponsePromise", () => { - const mockRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 200, - statusText: "OK", - type: "basic" as ResponseType, - url: "https://example.com", - }; - const mockData = { id: "123", name: "test" }; - const mockWithRawResponse: WithRawResponse = { - data: mockData, - rawResponse: mockRawResponse, - }; - - describe("fromFunction", () => { - it("should create an HttpResponsePromise from a function", async () => { - const mockFn = vi - .fn<(arg1: string, arg2: string) => Promise>>() - .mockResolvedValue(mockWithRawResponse); - - const responsePromise = HttpResponsePromise.fromFunction(mockFn, "arg1", "arg2"); - - const result = await responsePromise; - expect(result).toEqual(mockData); - expect(mockFn).toHaveBeenCalledWith("arg1", "arg2"); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromPromise", () => { - it("should create an HttpResponsePromise from a promise", async () => { - const promise = Promise.resolve(mockWithRawResponse); - - const responsePromise = HttpResponsePromise.fromPromise(promise); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromExecutor", () => { - it("should create an HttpResponsePromise from an executor function", async () => { - const responsePromise = HttpResponsePromise.fromExecutor((resolve) => { - resolve(mockWithRawResponse); - }); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromResult", () => { - it("should create an HttpResponsePromise from a result", async () => { - const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("Promise methods", () => { - let responsePromise: HttpResponsePromise; - - beforeEach(() => { - responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - }); - - it("should support then() method", async () => { - const result = await responsePromise.then((data) => ({ - ...data, - modified: true, - })); - - expect(result).toEqual({ - ...mockData, - modified: true, - }); - }); - - it("should support catch() method", async () => { - const errorResponsePromise = HttpResponsePromise.fromExecutor((_, reject) => { - reject(new Error("Test error")); - }); - - const catchSpy = vi.fn(); - await errorResponsePromise.catch(catchSpy); - - expect(catchSpy).toHaveBeenCalled(); - const error = catchSpy.mock.calls[0]?.[0]; - expect(error).toBeInstanceOf(Error); - expect((error as Error).message).toBe("Test error"); - }); - - it("should support finally() method", async () => { - const finallySpy = vi.fn(); - await responsePromise.finally(finallySpy); - - expect(finallySpy).toHaveBeenCalled(); - }); - }); - - describe("withRawResponse", () => { - it("should return both data and raw response", async () => { - const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - - const result = await responsePromise.withRawResponse(); - - expect(result).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/RawResponse.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/RawResponse.test.ts deleted file mode 100644 index 375ee3f38064..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/RawResponse.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { toRawResponse } from "../../../src/core/fetcher/RawResponse"; - -describe("RawResponse", () => { - describe("toRawResponse", () => { - it("should convert Response to RawResponse by removing body, bodyUsed, and ok properties", () => { - const mockHeaders = new Headers({ "content-type": "application/json" }); - const mockResponse = { - body: "test body", - bodyUsed: false, - ok: true, - headers: mockHeaders, - redirected: false, - status: 200, - statusText: "OK", - type: "basic" as ResponseType, - url: "https://example.com", - }; - - const result = toRawResponse(mockResponse as unknown as Response); - - expect("body" in result).toBe(false); - expect("bodyUsed" in result).toBe(false); - expect("ok" in result).toBe(false); - expect(result.headers).toBe(mockHeaders); - expect(result.redirected).toBe(false); - expect(result.status).toBe(200); - expect(result.statusText).toBe("OK"); - expect(result.type).toBe("basic"); - expect(result.url).toBe("https://example.com"); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/createRequestUrl.test.ts deleted file mode 100644 index a92f1b5e81d1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/createRequestUrl.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; - -describe("Test createRequestUrl", () => { - const BASE_URL = "https://api.example.com"; - - interface TestCase { - description: string; - baseUrl: string; - queryParams?: Record; - expected: string; - } - - const testCases: TestCase[] = [ - { - description: "should return the base URL when no query parameters are provided", - baseUrl: BASE_URL, - expected: BASE_URL, - }, - { - description: "should append simple query parameters", - baseUrl: BASE_URL, - queryParams: { key: "value", another: "param" }, - expected: "https://api.example.com?key=value&another=param", - }, - { - description: "should handle array query parameters", - baseUrl: BASE_URL, - queryParams: { items: ["a", "b", "c"] }, - expected: "https://api.example.com?items=a&items=b&items=c", - }, - { - description: "should handle object query parameters", - baseUrl: BASE_URL, - queryParams: { filter: { name: "John", age: 30 } }, - expected: "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", - }, - { - description: "should handle mixed types of query parameters", - baseUrl: BASE_URL, - queryParams: { - simple: "value", - array: ["x", "y"], - object: { key: "value" }, - }, - expected: "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", - }, - { - description: "should handle empty query parameters object", - baseUrl: BASE_URL, - queryParams: {}, - expected: BASE_URL, - }, - { - description: "should encode special characters in query parameters", - baseUrl: BASE_URL, - queryParams: { special: "a&b=c d" }, - expected: "https://api.example.com?special=a%26b%3Dc%20d", - }, - { - description: "should handle numeric values", - baseUrl: BASE_URL, - queryParams: { count: 42, price: 19.99, active: 1, inactive: 0 }, - expected: "https://api.example.com?count=42&price=19.99&active=1&inactive=0", - }, - { - description: "should handle boolean values", - baseUrl: BASE_URL, - queryParams: { enabled: true, disabled: false }, - expected: "https://api.example.com?enabled=true&disabled=false", - }, - { - description: "should handle null and undefined values", - baseUrl: BASE_URL, - queryParams: { - valid: "value", - nullValue: null, - undefinedValue: undefined, - emptyString: "", - }, - expected: "https://api.example.com?valid=value&nullValue=&emptyString=", - }, - { - description: "should handle deeply nested objects", - baseUrl: BASE_URL, - queryParams: { - user: { - profile: { - name: "John", - settings: { theme: "dark" }, - }, - }, - }, - expected: - "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - }, - { - description: "should handle arrays of objects", - baseUrl: BASE_URL, - queryParams: { - users: [ - { name: "John", age: 30 }, - { name: "Jane", age: 25 }, - ], - }, - expected: - "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", - }, - { - description: "should handle mixed arrays", - baseUrl: BASE_URL, - queryParams: { - mixed: ["string", 42, true, { key: "value" }], - }, - expected: "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", - }, - { - description: "should handle empty arrays", - baseUrl: BASE_URL, - queryParams: { emptyArray: [] }, - expected: BASE_URL, - }, - { - description: "should handle empty objects", - baseUrl: BASE_URL, - queryParams: { emptyObject: {} }, - expected: BASE_URL, - }, - { - description: "should handle special characters in keys", - baseUrl: BASE_URL, - queryParams: { "key with spaces": "value", "key[with]brackets": "value" }, - expected: "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", - }, - { - description: "should handle URL with existing query parameters", - baseUrl: "https://api.example.com?existing=param", - queryParams: { new: "value" }, - expected: "https://api.example.com?existing=param?new=value", - }, - { - description: "should handle complex nested structures", - baseUrl: BASE_URL, - queryParams: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - expected: - "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - ]; - - testCases.forEach(({ description, baseUrl, queryParams, expected }) => { - it(description, () => { - expect(createRequestUrl(baseUrl, queryParams)).toBe(expected); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getRequestBody.test.ts deleted file mode 100644 index 8a6c3a57e211..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getRequestBody.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; -import { RUNTIME } from "../../../src/core/runtime"; - -describe("Test getRequestBody", () => { - interface TestCase { - description: string; - input: any; - type: "json" | "form" | "file" | "bytes" | "other"; - expected: any; - skipCondition?: () => boolean; - } - - const testCases: TestCase[] = [ - { - description: "should stringify body if not FormData in Node environment", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - skipCondition: () => RUNTIME.type !== "node", - }, - { - description: "should stringify body if not FormData in browser environment", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - skipCondition: () => RUNTIME.type !== "browser", - }, - { - description: "should return the Uint8Array", - input: new Uint8Array([1, 2, 3]), - type: "bytes", - expected: new Uint8Array([1, 2, 3]), - }, - { - description: "should serialize objects for form-urlencoded content type", - input: { username: "johndoe", email: "john@example.com" }, - type: "form", - expected: "username=johndoe&email=john%40example.com", - }, - { - description: "should serialize complex nested objects and arrays for form-urlencoded content type", - input: { - user: { - profile: { - name: "John Doe", - settings: { - theme: "dark", - notifications: true, - }, - }, - tags: ["admin", "user"], - contacts: [ - { type: "email", value: "john@example.com" }, - { type: "phone", value: "+1234567890" }, - ], - }, - filters: { - status: ["active", "pending"], - metadata: { - created: "2024-01-01", - categories: ["electronics", "books"], - }, - }, - preferences: ["notifications", "updates"], - }, - type: "form", - expected: - "user%5Bprofile%5D%5Bname%5D=John%20Doe&" + - "user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark&" + - "user%5Bprofile%5D%5Bsettings%5D%5Bnotifications%5D=true&" + - "user%5Btags%5D=admin&" + - "user%5Btags%5D=user&" + - "user%5Bcontacts%5D%5Btype%5D=email&" + - "user%5Bcontacts%5D%5Bvalue%5D=john%40example.com&" + - "user%5Bcontacts%5D%5Btype%5D=phone&" + - "user%5Bcontacts%5D%5Bvalue%5D=%2B1234567890&" + - "filters%5Bstatus%5D=active&" + - "filters%5Bstatus%5D=pending&" + - "filters%5Bmetadata%5D%5Bcreated%5D=2024-01-01&" + - "filters%5Bmetadata%5D%5Bcategories%5D=electronics&" + - "filters%5Bmetadata%5D%5Bcategories%5D=books&" + - "preferences=notifications&" + - "preferences=updates", - }, - { - description: "should return the input for pre-serialized form-urlencoded strings", - input: "key=value&another=param", - type: "other", - expected: "key=value&another=param", - }, - { - description: "should JSON stringify objects", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - }, - ]; - - testCases.forEach(({ description, input, type, expected, skipCondition }) => { - it(description, async () => { - if (skipCondition?.()) { - return; - } - - const result = await getRequestBody({ - body: input, - type, - }); - - if (input instanceof Uint8Array) { - expect(result).toBe(input); - } else { - expect(result).toBe(expected); - } - }); - }); - - it("should return FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const formData = new FormData(); - formData.append("key", "value"); - const result = await getRequestBody({ - body: formData, - type: "file", - }); - expect(result).toBe(formData); - } - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getResponseBody.test.ts deleted file mode 100644 index ad6be7fc2c9b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/getResponseBody.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; - -import { RUNTIME } from "../../../src/core/runtime"; - -describe("Test getResponseBody", () => { - interface SimpleTestCase { - description: string; - responseData: string | Record; - responseType?: "blob" | "sse" | "streaming" | "text"; - expected: any; - skipCondition?: () => boolean; - } - - const simpleTestCases: SimpleTestCase[] = [ - { - description: "should handle text response type", - responseData: "test text", - responseType: "text", - expected: "test text", - }, - { - description: "should handle JSON response", - responseData: { key: "value" }, - expected: { key: "value" }, - }, - { - description: "should handle empty response", - responseData: "", - expected: undefined, - }, - { - description: "should handle non-JSON response", - responseData: "invalid json", - expected: { - ok: false, - error: { - reason: "non-json", - statusCode: 200, - rawBody: "invalid json", - }, - }, - }, - ]; - - simpleTestCases.forEach(({ description, responseData, responseType, expected, skipCondition }) => { - it(description, async () => { - if (skipCondition?.()) { - return; - } - - const mockResponse = new Response( - typeof responseData === "string" ? responseData : JSON.stringify(responseData), - ); - const result = await getResponseBody(mockResponse, responseType); - expect(result).toEqual(expected); - }); - }); - - it("should handle blob response type", async () => { - const mockBlob = new Blob(["test"], { type: "text/plain" }); - const mockResponse = new Response(mockBlob); - const result = await getResponseBody(mockResponse, "blob"); - // @ts-expect-error - expect(result.constructor.name).toBe("Blob"); - }); - - it("should handle sse response type", async () => { - if (RUNTIME.type === "node") { - const mockStream = new ReadableStream(); - const mockResponse = new Response(mockStream); - const result = await getResponseBody(mockResponse, "sse"); - expect(result).toBe(mockStream); - } - }); - - it("should handle streaming response type", async () => { - const encoder = new TextEncoder(); - const testData = "test stream data"; - const mockStream = new ReadableStream({ - start(controller) { - controller.enqueue(encoder.encode(testData)); - controller.close(); - }, - }); - - const mockResponse = new Response(mockStream); - const result = (await getResponseBody(mockResponse, "streaming")) as ReadableStream; - - expect(result).toBeInstanceOf(ReadableStream); - - const reader = result.getReader(); - const decoder = new TextDecoder(); - const { value } = await reader.read(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe(testData); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/logging.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/logging.test.ts deleted file mode 100644 index 366c9b6ced61..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/logging.test.ts +++ /dev/null @@ -1,517 +0,0 @@ -import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -function mockErrorResponse(data: unknown = { error: "Error" }, status = 404, statusText = "Not Found") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -describe("Fetcher Logging Integration", () => { - describe("Request Logging", () => { - it("should log successful request at debug level", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - headers: { "Content-Type": "application/json" }, - body: { test: "data" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "POST", - url: "https://example.com/api", - headers: expect.toContainHeaders({ - "Content-Type": "application/json", - }), - hasBody: true, - }), - ); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - method: "POST", - url: "https://example.com/api", - statusCode: 200, - }), - ); - }); - - it("should not log debug messages at info level for successful requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "info", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - }); - - it("should log request with body flag", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - hasBody: true, - }), - ); - }); - - it("should log request without body flag", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - hasBody: false, - }), - ); - }); - - it("should not log when silent mode is enabled", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: true, - }, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).not.toHaveBeenCalled(); - }); - - it("should not log when no logging config is provided", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - }); - }); - - describe("Error Logging", () => { - it("should log 4xx errors at error level", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Not found" }, 404, "Not Found"); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - statusCode: 404, - }), - ); - }); - - it("should log 5xx errors at error level", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Internal error" }, 500, "Internal Server Error"); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - statusCode: 500, - }), - ); - }); - - it("should log aborted request errors", async () => { - const mockLogger = createMockLogger(); - - const abortController = new AbortController(); - abortController.abort(); - - global.fetch = vi.fn().mockRejectedValue(new Error("Aborted")); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - abortSignal: abortController.signal, - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request was aborted", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - }), - ); - }); - - it("should log timeout errors", async () => { - const mockLogger = createMockLogger(); - - const timeoutError = new Error("Request timeout"); - timeoutError.name = "AbortError"; - - global.fetch = vi.fn().mockRejectedValue(timeoutError); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request timed out", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - timeoutMs: undefined, - }), - ); - }); - - it("should log unknown errors", async () => { - const mockLogger = createMockLogger(); - - const unknownError = new Error("Unknown error"); - - global.fetch = vi.fn().mockRejectedValue(unknownError); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - errorMessage: "Unknown error", - }), - ); - }); - }); - - describe("Logging with Redaction", () => { - it("should redact sensitive data in error logs", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Unauthorized" }, 401, "Unauthorized"); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]", - }), - ); - }); - }); - - describe("Different HTTP Methods", () => { - it("should log GET requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "GET", - }), - ); - }); - - it("should log POST requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 201, "Created"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "POST", - }), - ); - }); - - it("should log PUT requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "PUT", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "PUT", - }), - ); - }); - - it("should log DELETE requests", async () => { - const mockLogger = createMockLogger(); - global.fetch = vi.fn().mockResolvedValue( - new Response(null, { - status: 200, - statusText: "OK", - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "DELETE", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "DELETE", - }), - ); - }); - }); - - describe("Status Code Logging", () => { - it("should log 2xx success status codes", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 201, "Created"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - statusCode: 201, - }), - ); - }); - - it("should log 3xx redirect status codes as success", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 301, "Moved Permanently"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - statusCode: 301, - }), - ); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/makeRequest.test.ts deleted file mode 100644 index ea49466a55fc..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/makeRequest.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { Mock } from "vitest"; -import { makeRequest } from "../../../src/core/fetcher/makeRequest"; - -describe("Test makeRequest", () => { - const mockPostUrl = "https://httpbin.org/post"; - const mockGetUrl = "https://httpbin.org/get"; - const mockHeaders = { "Content-Type": "application/json" }; - const mockBody = JSON.stringify({ key: "value" }); - - let mockFetch: Mock; - - beforeEach(() => { - mockFetch = vi.fn(); - mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); - }); - - it("should handle POST request correctly", async () => { - const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockPostUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "POST", - headers: mockHeaders, - body: mockBody, - credentials: undefined, - }), - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); - - it("should handle GET request correctly", async () => { - const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockGetUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "GET", - headers: mockHeaders, - body: undefined, - credentials: undefined, - }), - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/redacting.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/redacting.test.ts deleted file mode 100644 index d599376b9bcf..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/redacting.test.ts +++ /dev/null @@ -1,1115 +0,0 @@ -import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -describe("Redacting Logic", () => { - describe("Header Redaction", () => { - it("should redact authorization header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { Authorization: "Bearer secret-token-12345" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Authorization: "[REDACTED]", - }), - }), - ); - }); - - it("should redact api-key header (case-insensitive)", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-API-KEY": "secret-api-key" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-API-KEY": "[REDACTED]", - }), - }), - ); - }); - - it("should redact cookie header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { Cookie: "session=abc123; token=xyz789" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Cookie: "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-auth-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "x-auth-token": "auth-token-12345" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "x-auth-token": "[REDACTED]", - }), - }), - ); - }); - - it("should redact proxy-authorization header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "Proxy-Authorization": "Basic credentials" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "Proxy-Authorization": "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-csrf-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-CSRF-Token": "csrf-token-abc" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-CSRF-Token": "[REDACTED]", - }), - }), - ); - }); - - it("should redact www-authenticate header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "WWW-Authenticate": "Bearer realm=example" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "WWW-Authenticate": "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-session-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-Session-Token": "session-token-xyz" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-Session-Token": "[REDACTED]", - }), - }), - ); - }); - - it("should not redact non-sensitive headers", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { - "Content-Type": "application/json", - "User-Agent": "Test/1.0", - Accept: "application/json", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "Content-Type": "application/json", - "User-Agent": "Test/1.0", - Accept: "application/json", - }), - }), - ); - }); - - it("should redact multiple sensitive headers at once", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { - Authorization: "Bearer token", - "X-API-Key": "api-key", - Cookie: "session=123", - "Content-Type": "application/json", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Authorization: "[REDACTED]", - "X-API-Key": "[REDACTED]", - Cookie: "[REDACTED]", - "Content-Type": "application/json", - }), - }), - ); - }); - }); - - describe("Response Header Redaction", () => { - it("should redact Set-Cookie in response headers", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("Set-Cookie", "session=abc123; HttpOnly; Secure"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - "set-cookie": "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - - it("should redact authorization in response headers", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("Authorization", "Bearer token-123"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - authorization: "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - - it("should redact response headers in error responses", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("WWW-Authenticate", "Bearer realm=example"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ error: "Unauthorized" }), { - status: 401, - statusText: "Unauthorized", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - "www-authenticate": "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - }); - - describe("Query Parameter Redaction", () => { - it("should redact api_key query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { api_key: "secret-key" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - api_key: "[REDACTED]", - }), - }), - ); - }); - - it("should redact token query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { token: "secret-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - token: "[REDACTED]", - }), - }), - ); - }); - - it("should redact access_token query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { access_token: "secret-access-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - access_token: "[REDACTED]", - }), - }), - ); - }); - - it("should redact password query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { password: "secret-password" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - password: "[REDACTED]", - }), - }), - ); - }); - - it("should redact secret query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { secret: "secret-value" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - secret: "[REDACTED]", - }), - }), - ); - }); - - it("should redact session_id query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { session_id: "session-123" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - session_id: "[REDACTED]", - }), - }), - ); - }); - - it("should not redact non-sensitive query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { - page: "1", - limit: "10", - sort: "name", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - page: "1", - limit: "10", - sort: "name", - }), - }), - ); - }); - - it("should not redact parameters containing 'auth' substring like 'author'", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { - author: "john", - authenticate: "false", - authorization_level: "user", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - author: "john", - authenticate: "false", - authorization_level: "user", - }), - }), - ); - }); - - it("should handle undefined query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: undefined, - }), - ); - }); - - it("should redact case-insensitive query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { API_KEY: "secret-key", Token: "secret-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - API_KEY: "[REDACTED]", - Token: "[REDACTED]", - }), - }), - ); - }); - }); - - describe("URL Redaction", () => { - it("should redact credentials in URL", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:password@example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/api", - }), - ); - }); - - it("should redact api_key in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret-key&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]&page=1", - }), - ); - }); - - it("should redact token in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?token=secret-token", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?token=[REDACTED]", - }), - ); - }); - - it("should redact password in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?username=user&password=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?username=user&password=[REDACTED]", - }), - ); - }); - - it("should not redact non-sensitive query strings", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?page=1&limit=10&sort=name", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?page=1&limit=10&sort=name", - }), - ); - }); - - it("should not redact URL parameters containing 'auth' substring like 'author'", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?author=john&authenticate=false&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?author=john&authenticate=false&page=1", - }), - ); - }); - - it("should handle URL with fragment", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?token=secret#section", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?token=[REDACTED]#section", - }), - ); - }); - - it("should redact URL-encoded query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api%5Fkey=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api%5Fkey=[REDACTED]", - }), - ); - }); - - it("should handle URL without query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api", - }), - ); - }); - - it("should handle empty query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?", - }), - ); - }); - - it("should redact multiple sensitive parameters in URL", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret1&token=secret2&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]&token=[REDACTED]&page=1", - }), - ); - }); - - it("should redact both credentials and query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:pass@example.com/api?token=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/api?token=[REDACTED]", - }), - ); - }); - - it("should use fast path for URLs without sensitive keywords", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", - }), - ); - }); - - it("should handle query parameter without value", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?flag&token=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?flag&token=[REDACTED]", - }), - ); - }); - - it("should handle URL with multiple @ symbols in credentials", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user@example.com:pass@host.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@host.com/api", - }), - ); - }); - - it("should handle URL with @ in query parameter but not in credentials", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?email=user@example.com", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?email=user@example.com", - }), - ); - }); - - it("should handle URL with both credentials and @ in path", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:pass@example.com/users/@username", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/users/@username", - }), - ); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/requestWithRetries.test.ts deleted file mode 100644 index d22661367f4e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/requestWithRetries.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import type { Mock, MockInstance } from "vitest"; -import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; - -describe("requestWithRetries", () => { - let mockFetch: Mock; - let originalMathRandom: typeof Math.random; - let setTimeoutSpy: MockInstance; - - beforeEach(() => { - mockFetch = vi.fn(); - originalMathRandom = Math.random; - - Math.random = vi.fn(() => 0.5); - - vi.useFakeTimers({ - toFake: [ - "setTimeout", - "clearTimeout", - "setInterval", - "clearInterval", - "setImmediate", - "clearImmediate", - "Date", - "performance", - "requestAnimationFrame", - "cancelAnimationFrame", - "requestIdleCallback", - "cancelIdleCallback", - ], - }); - }); - - afterEach(() => { - Math.random = originalMathRandom; - vi.clearAllMocks(); - vi.clearAllTimers(); - }); - - it("should retry on retryable status codes", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const retryableStatuses = [408, 429, 500, 502]; - let callCount = 0; - - mockFetch.mockImplementation(async () => { - if (callCount < retryableStatuses.length) { - return new Response("", { status: retryableStatuses[callCount++] }); - } - return new Response("", { status: 200 }); - }); - - const responsePromise = requestWithRetries(() => mockFetch(), retryableStatuses.length); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(retryableStatuses.length + 1); - expect(response.status).toBe(200); - }); - - it("should respect maxRetries limit", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const maxRetries = 2; - mockFetch.mockResolvedValue(new Response("", { status: 500 })); - - const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); - expect(response.status).toBe(500); - }); - - it("should not retry on success status codes", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const successStatuses = [200, 201, 202]; - - for (const status of successStatuses) { - mockFetch.mockReset(); - setTimeoutSpy.mockClear(); - mockFetch.mockResolvedValueOnce(new Response("", { status })); - - const responsePromise = requestWithRetries(() => mockFetch(), 3); - await vi.runAllTimersAsync(); - await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(1); - expect(setTimeoutSpy).not.toHaveBeenCalled(); - } - }); - - interface RetryHeaderTestCase { - description: string; - headerName: string; - headerValue: string | (() => string); - expectedDelayMin: number; - expectedDelayMax: number; - } - - const retryHeaderTests: RetryHeaderTestCase[] = [ - { - description: "should respect retry-after header with seconds value", - headerName: "retry-after", - headerValue: "5", - expectedDelayMin: 4000, - expectedDelayMax: 6000, - }, - { - description: "should respect retry-after header with HTTP date value", - headerName: "retry-after", - headerValue: () => new Date(Date.now() + 3000).toUTCString(), - expectedDelayMin: 2000, - expectedDelayMax: 4000, - }, - { - description: "should respect x-ratelimit-reset header", - headerName: "x-ratelimit-reset", - headerValue: () => Math.floor((Date.now() + 4000) / 1000).toString(), - expectedDelayMin: 3000, - expectedDelayMax: 6000, - }, - ]; - - retryHeaderTests.forEach(({ description, headerName, headerValue, expectedDelayMin, expectedDelayMax }) => { - it(description, async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const value = typeof headerValue === "function" ? headerValue() : headerValue; - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ [headerName]: value }), - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); - const actualDelay = setTimeoutSpy.mock.calls[0][1]; - expect(actualDelay).toBeGreaterThan(expectedDelayMin); - expect(actualDelay).toBeLessThan(expectedDelayMax); - expect(response.status).toBe(200); - }); - }); - - it("should apply correct exponential backoff with jitter", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch.mockResolvedValue(new Response("", { status: 500 })); - const maxRetries = 3; - const expectedDelays = [1000, 2000, 4000]; - - const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); - await vi.runAllTimersAsync(); - await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); - - expectedDelays.forEach((delay, index) => { - expect(setTimeoutSpy).toHaveBeenNthCalledWith(index + 1, expect.any(Function), delay); - }); - - expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); - }); - - it("should handle concurrent retries independently", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch - .mockResolvedValueOnce(new Response("", { status: 500 })) - .mockResolvedValueOnce(new Response("", { status: 500 })) - .mockResolvedValueOnce(new Response("", { status: 200 })) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const promise1 = requestWithRetries(() => mockFetch(), 1); - const promise2 = requestWithRetries(() => mockFetch(), 1); - - await vi.runAllTimersAsync(); - const [response1, response2] = await Promise.all([promise1, promise2]); - - expect(response1.status).toBe(200); - expect(response2.status).toBe(200); - }); - - it("should cap delay at MAX_RETRY_DELAY for large header values", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ "retry-after": "120" }), // 120 seconds = 120000ms > MAX_RETRY_DELAY (60000ms) - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); - expect(response.status).toBe(200); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/signals.test.ts deleted file mode 100644 index d7b6d1e63caa..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/signals.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; - -describe("Test getTimeoutSignal", () => { - beforeEach(() => { - vi.useFakeTimers(); - }); - - afterEach(() => { - vi.useRealTimers(); - }); - - it("should return an object with signal and abortId", () => { - const { signal, abortId } = getTimeoutSignal(1000); - - expect(signal).toBeDefined(); - expect(abortId).toBeDefined(); - expect(signal).toBeInstanceOf(AbortSignal); - expect(signal.aborted).toBe(false); - }); - - it("should create a signal that aborts after the specified timeout", () => { - const timeoutMs = 5000; - const { signal } = getTimeoutSignal(timeoutMs); - - expect(signal.aborted).toBe(false); - - vi.advanceTimersByTime(timeoutMs - 1); - expect(signal.aborted).toBe(false); - - vi.advanceTimersByTime(1); - expect(signal.aborted).toBe(true); - }); -}); - -describe("Test anySignal", () => { - it("should return an AbortSignal", () => { - const signal = anySignal(new AbortController().signal); - expect(signal).toBeInstanceOf(AbortSignal); - }); - - it("should abort when any of the input signals is aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal(controller1.signal, controller2.signal); - - expect(signal.aborted).toBe(false); - controller1.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should handle an array of signals", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal([controller1.signal, controller2.signal]); - - expect(signal.aborted).toBe(false); - controller2.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should abort immediately if one of the input signals is already aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - controller1.abort(); - - const signal = anySignal(controller1.signal, controller2.signal); - expect(signal.aborted).toBe(true); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/test-file.txt deleted file mode 100644 index c66d471e359c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/fetcher/test-file.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test file! diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/logging/logger.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/logging/logger.test.ts deleted file mode 100644 index 2e0b5fe5040c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/logging/logger.test.ts +++ /dev/null @@ -1,454 +0,0 @@ -import { ConsoleLogger, createLogger, Logger, LogLevel } from "../../../src/core/logging/logger"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -describe("Logger", () => { - describe("LogLevel", () => { - it("should have correct log levels", () => { - expect(LogLevel.Debug).toBe("debug"); - expect(LogLevel.Info).toBe("info"); - expect(LogLevel.Warn).toBe("warn"); - expect(LogLevel.Error).toBe("error"); - }); - }); - - describe("ConsoleLogger", () => { - let consoleLogger: ConsoleLogger; - let consoleSpy: { - debug: ReturnType; - info: ReturnType; - warn: ReturnType; - error: ReturnType; - }; - - beforeEach(() => { - consoleLogger = new ConsoleLogger(); - consoleSpy = { - debug: vi.spyOn(console, "debug").mockImplementation(() => {}), - info: vi.spyOn(console, "info").mockImplementation(() => {}), - warn: vi.spyOn(console, "warn").mockImplementation(() => {}), - error: vi.spyOn(console, "error").mockImplementation(() => {}), - }; - }); - - afterEach(() => { - consoleSpy.debug.mockRestore(); - consoleSpy.info.mockRestore(); - consoleSpy.warn.mockRestore(); - consoleSpy.error.mockRestore(); - }); - - it("should log debug messages", () => { - consoleLogger.debug("debug message", { data: "test" }); - expect(consoleSpy.debug).toHaveBeenCalledWith("debug message", { data: "test" }); - }); - - it("should log info messages", () => { - consoleLogger.info("info message", { data: "test" }); - expect(consoleSpy.info).toHaveBeenCalledWith("info message", { data: "test" }); - }); - - it("should log warn messages", () => { - consoleLogger.warn("warn message", { data: "test" }); - expect(consoleSpy.warn).toHaveBeenCalledWith("warn message", { data: "test" }); - }); - - it("should log error messages", () => { - consoleLogger.error("error message", { data: "test" }); - expect(consoleSpy.error).toHaveBeenCalledWith("error message", { data: "test" }); - }); - - it("should handle multiple arguments", () => { - consoleLogger.debug("message", "arg1", "arg2", { key: "value" }); - expect(consoleSpy.debug).toHaveBeenCalledWith("message", "arg1", "arg2", { key: "value" }); - }); - }); - - describe("Logger with level filtering", () => { - let mockLogger: { - debug: ReturnType; - info: ReturnType; - warn: ReturnType; - error: ReturnType; - }; - - beforeEach(() => { - mockLogger = createMockLogger(); - }); - - describe("Debug level", () => { - it("should log all levels when set to debug", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).toHaveBeenCalledWith("debug"); - expect(mockLogger.info).toHaveBeenCalledWith("info"); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(true); - expect(logger.isInfo()).toBe(true); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Info level", () => { - it("should log info, warn, and error when set to info", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).toHaveBeenCalledWith("info"); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(true); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Warn level", () => { - it("should log warn and error when set to warn", () => { - const logger = new Logger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Error level", () => { - it("should only log error when set to error", () => { - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(false); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Silent mode", () => { - it("should not log anything when silent is true", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).not.toHaveBeenCalled(); - }); - - it("should report all level checks as false when silent", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(false); - expect(logger.isError()).toBe(false); - }); - }); - - describe("shouldLog", () => { - it("should correctly determine if level should be logged", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - expect(logger.shouldLog(LogLevel.Debug)).toBe(false); - expect(logger.shouldLog(LogLevel.Info)).toBe(true); - expect(logger.shouldLog(LogLevel.Warn)).toBe(true); - expect(logger.shouldLog(LogLevel.Error)).toBe(true); - }); - - it("should return false for all levels when silent", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - expect(logger.shouldLog(LogLevel.Debug)).toBe(false); - expect(logger.shouldLog(LogLevel.Info)).toBe(false); - expect(logger.shouldLog(LogLevel.Warn)).toBe(false); - expect(logger.shouldLog(LogLevel.Error)).toBe(false); - }); - }); - - describe("Multiple arguments", () => { - it("should pass multiple arguments to logger", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("message", "arg1", { key: "value" }, 123); - expect(mockLogger.debug).toHaveBeenCalledWith("message", "arg1", { key: "value" }, 123); - }); - }); - }); - - describe("createLogger", () => { - it("should return default logger when no config provided", () => { - const logger = createLogger(); - expect(logger).toBeInstanceOf(Logger); - }); - - it("should return same logger instance when Logger is passed", () => { - const customLogger = new Logger({ - level: LogLevel.Debug, - logger: new ConsoleLogger(), - silent: false, - }); - - const result = createLogger(customLogger); - expect(result).toBe(customLogger); - }); - - it("should create logger with custom config", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - expect(logger).toBeInstanceOf(Logger); - logger.warn("test"); - expect(mockLogger.warn).toHaveBeenCalledWith("test"); - }); - - it("should use default values for missing config", () => { - const logger = createLogger({}); - expect(logger).toBeInstanceOf(Logger); - }); - - it("should override default level", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("test"); - expect(mockLogger.debug).toHaveBeenCalledWith("test"); - }); - - it("should override default silent mode", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - logger: mockLogger, - silent: false, - }); - - logger.info("test"); - expect(mockLogger.info).toHaveBeenCalledWith("test"); - }); - - it("should use provided logger implementation", () => { - const customLogger = createMockLogger(); - - const logger = createLogger({ - logger: customLogger, - level: LogLevel.Debug, - silent: false, - }); - - logger.debug("test"); - expect(customLogger.debug).toHaveBeenCalledWith("test"); - }); - - it("should default to silent: true", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - logger: mockLogger, - level: LogLevel.Debug, - }); - - logger.debug("test"); - expect(mockLogger.debug).not.toHaveBeenCalled(); - }); - }); - - describe("Default logger", () => { - it("should have silent: true by default", () => { - const logger = createLogger(); - expect(logger.shouldLog(LogLevel.Info)).toBe(false); - }); - - it("should not log when using default logger", () => { - const logger = createLogger(); - - logger.info("test"); - expect(logger.isInfo()).toBe(false); - }); - }); - - describe("Edge cases", () => { - it("should handle empty message", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug(""); - expect(mockLogger.debug).toHaveBeenCalledWith(""); - }); - - it("should handle no arguments", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("message"); - expect(mockLogger.debug).toHaveBeenCalledWith("message"); - }); - - it("should handle complex objects", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - const complexObject = { - nested: { key: "value" }, - array: [1, 2, 3], - fn: () => "test", - }; - - logger.debug("message", complexObject); - expect(mockLogger.debug).toHaveBeenCalledWith("message", complexObject); - }); - - it("should handle errors as arguments", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - const error = new Error("Test error"); - logger.error("Error occurred", error); - expect(mockLogger.error).toHaveBeenCalledWith("Error occurred", error); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/join.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/join.test.ts deleted file mode 100644 index 123488f084ea..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/join.test.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { join } from "../../../src/core/url/index"; - -describe("join", () => { - interface TestCase { - description: string; - base: string; - segments: string[]; - expected: string; - } - - describe("basic functionality", () => { - const basicTests: TestCase[] = [ - { description: "should return empty string for empty base", base: "", segments: [], expected: "" }, - { - description: "should return empty string for empty base with path", - base: "", - segments: ["path"], - expected: "", - }, - { - description: "should handle single segment", - base: "base", - segments: ["segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with trailing slash on base", - base: "base/", - segments: ["segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with leading slash", - base: "base", - segments: ["/segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with both slashes", - base: "base/", - segments: ["/segment"], - expected: "base/segment", - }, - { - description: "should handle multiple segments", - base: "base", - segments: ["path1", "path2", "path3"], - expected: "base/path1/path2/path3", - }, - { - description: "should handle multiple segments with slashes", - base: "base/", - segments: ["/path1/", "/path2/", "/path3/"], - expected: "base/path1/path2/path3/", - }, - ]; - - basicTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("URL handling", () => { - const urlTests: TestCase[] = [ - { - description: "should handle absolute URLs", - base: "https://example.com", - segments: ["api", "v1"], - expected: "https://example.com/api/v1", - }, - { - description: "should handle absolute URLs with slashes", - base: "https://example.com/", - segments: ["/api/", "/v1/"], - expected: "https://example.com/api/v1/", - }, - { - description: "should handle absolute URLs with base path", - base: "https://example.com/base", - segments: ["api", "v1"], - expected: "https://example.com/base/api/v1", - }, - { - description: "should preserve URL query parameters", - base: "https://example.com?query=1", - segments: ["api"], - expected: "https://example.com/api?query=1", - }, - { - description: "should preserve URL fragments", - base: "https://example.com#fragment", - segments: ["api"], - expected: "https://example.com/api#fragment", - }, - { - description: "should preserve URL query and fragments", - base: "https://example.com?query=1#fragment", - segments: ["api"], - expected: "https://example.com/api?query=1#fragment", - }, - { - description: "should handle http protocol", - base: "http://example.com", - segments: ["api"], - expected: "http://example.com/api", - }, - { - description: "should handle ftp protocol", - base: "ftp://example.com", - segments: ["files"], - expected: "ftp://example.com/files", - }, - { - description: "should handle ws protocol", - base: "ws://example.com", - segments: ["socket"], - expected: "ws://example.com/socket", - }, - { - description: "should fallback to path joining for malformed URLs", - base: "not-a-url://", - segments: ["path"], - expected: "not-a-url:///path", - }, - ]; - - urlTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("edge cases", () => { - const edgeCaseTests: TestCase[] = [ - { - description: "should handle empty segments", - base: "base", - segments: ["", "path"], - expected: "base/path", - }, - { - description: "should handle null segments", - base: "base", - segments: [null as any, "path"], - expected: "base/path", - }, - { - description: "should handle undefined segments", - base: "base", - segments: [undefined as any, "path"], - expected: "base/path", - }, - { - description: "should handle segments with only single slash", - base: "base", - segments: ["/", "path"], - expected: "base/path", - }, - { - description: "should handle segments with only double slash", - base: "base", - segments: ["//", "path"], - expected: "base/path", - }, - { - description: "should handle base paths with trailing slashes", - base: "base/", - segments: ["path"], - expected: "base/path", - }, - { - description: "should handle complex nested paths", - base: "api/v1/", - segments: ["/users/", "/123/", "/profile"], - expected: "api/v1/users/123/profile", - }, - ]; - - edgeCaseTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("real-world scenarios", () => { - const realWorldTests: TestCase[] = [ - { - description: "should handle API endpoint construction", - base: "https://api.example.com/v1", - segments: ["users", "123", "posts"], - expected: "https://api.example.com/v1/users/123/posts", - }, - { - description: "should handle file path construction", - base: "/var/www", - segments: ["html", "assets", "images"], - expected: "/var/www/html/assets/images", - }, - { - description: "should handle relative path construction", - base: "../parent", - segments: ["child", "grandchild"], - expected: "../parent/child/grandchild", - }, - { - description: "should handle Windows-style paths", - base: "C:\\Users", - segments: ["Documents", "file.txt"], - expected: "C:\\Users/Documents/file.txt", - }, - ]; - - realWorldTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("performance scenarios", () => { - it("should handle many segments efficiently", () => { - const segments = Array(100).fill("segment"); - const result = join("base", ...segments); - expect(result).toBe(`base/${segments.join("/")}`); - }); - - it("should handle long URLs", () => { - const longPath = "a".repeat(1000); - expect(join("https://example.com", longPath)).toBe(`https://example.com/${longPath}`); - }); - }); - - describe("trailing slash preservation", () => { - const trailingSlashTests: TestCase[] = [ - { - description: - "should preserve trailing slash on final result when base has trailing slash and no segments", - base: "https://api.example.com/", - segments: [], - expected: "https://api.example.com/", - }, - { - description: "should preserve trailing slash on v1 path", - base: "https://api.example.com/v1/", - segments: [], - expected: "https://api.example.com/v1/", - }, - { - description: "should preserve trailing slash when last segment has trailing slash", - base: "https://api.example.com", - segments: ["users/"], - expected: "https://api.example.com/users/", - }, - { - description: "should preserve trailing slash with relative path", - base: "api/v1", - segments: ["users/"], - expected: "api/v1/users/", - }, - { - description: "should preserve trailing slash with multiple segments", - base: "https://api.example.com", - segments: ["v1", "collections/"], - expected: "https://api.example.com/v1/collections/", - }, - { - description: "should preserve trailing slash with base path", - base: "base", - segments: ["path1", "path2/"], - expected: "base/path1/path2/", - }, - ]; - - trailingSlashTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/qs.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/qs.test.ts deleted file mode 100644 index 42cdffb9e5ea..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/unit/url/qs.test.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { toQueryString } from "../../../src/core/url/index"; - -describe("Test qs toQueryString", () => { - interface BasicTestCase { - description: string; - input: any; - expected: string; - } - - describe("Basic functionality", () => { - const basicTests: BasicTestCase[] = [ - { description: "should return empty string for null", input: null, expected: "" }, - { description: "should return empty string for undefined", input: undefined, expected: "" }, - { description: "should return empty string for string primitive", input: "hello", expected: "" }, - { description: "should return empty string for number primitive", input: 42, expected: "" }, - { description: "should return empty string for true boolean", input: true, expected: "" }, - { description: "should return empty string for false boolean", input: false, expected: "" }, - { description: "should handle empty objects", input: {}, expected: "" }, - { - description: "should handle simple key-value pairs", - input: { name: "John", age: 30 }, - expected: "name=John&age=30", - }, - ]; - - basicTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Array handling", () => { - interface ArrayTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices" }; - expected: string; - } - - const arrayTests: ArrayTestCase[] = [ - { - description: "should handle arrays with indices format (default)", - input: { items: ["a", "b", "c"] }, - expected: "items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c", - }, - { - description: "should handle arrays with repeat format", - input: { items: ["a", "b", "c"] }, - options: { arrayFormat: "repeat" }, - expected: "items=a&items=b&items=c", - }, - { - description: "should handle empty arrays", - input: { items: [] }, - expected: "", - }, - { - description: "should handle arrays with mixed types", - input: { mixed: ["string", 42, true, false] }, - expected: "mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false", - }, - { - description: "should handle arrays with objects", - input: { users: [{ name: "John" }, { name: "Jane" }] }, - expected: "users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane", - }, - { - description: "should handle arrays with objects in repeat format", - input: { users: [{ name: "John" }, { name: "Jane" }] }, - options: { arrayFormat: "repeat" }, - expected: "users%5Bname%5D=John&users%5Bname%5D=Jane", - }, - ]; - - arrayTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Nested objects", () => { - const nestedTests: BasicTestCase[] = [ - { - description: "should handle nested objects", - input: { user: { name: "John", age: 30 } }, - expected: "user%5Bname%5D=John&user%5Bage%5D=30", - }, - { - description: "should handle deeply nested objects", - input: { user: { profile: { name: "John", settings: { theme: "dark" } } } }, - expected: "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - }, - { - description: "should handle empty nested objects", - input: { user: {} }, - expected: "", - }, - ]; - - nestedTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Encoding", () => { - interface EncodingTestCase { - description: string; - input: any; - options?: { encode?: boolean }; - expected: string; - } - - const encodingTests: EncodingTestCase[] = [ - { - description: "should encode by default", - input: { name: "John Doe", email: "john@example.com" }, - expected: "name=John%20Doe&email=john%40example.com", - }, - { - description: "should not encode when encode is false", - input: { name: "John Doe", email: "john@example.com" }, - options: { encode: false }, - expected: "name=John Doe&email=john@example.com", - }, - { - description: "should encode special characters in keys", - input: { "user name": "John", "email[primary]": "john@example.com" }, - expected: "user%20name=John&email%5Bprimary%5D=john%40example.com", - }, - { - description: "should not encode special characters in keys when encode is false", - input: { "user name": "John", "email[primary]": "john@example.com" }, - options: { encode: false }, - expected: "user name=John&email[primary]=john@example.com", - }, - ]; - - encodingTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Mixed scenarios", () => { - interface MixedTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices" }; - expected: string; - } - - const mixedTests: MixedTestCase[] = [ - { - description: "should handle complex nested structures", - input: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - expected: - "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - { - description: "should handle complex nested structures with repeat format", - input: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - options: { arrayFormat: "repeat" }, - expected: - "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - { - description: "should handle arrays with null/undefined values", - input: { items: ["a", null, "c", undefined, "e"] }, - expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e", - }, - { - description: "should handle objects with null/undefined values", - input: { name: "John", age: null, email: undefined, active: true }, - expected: "name=John&age=&active=true", - }, - ]; - - mixedTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Edge cases", () => { - const edgeCaseTests: BasicTestCase[] = [ - { - description: "should handle numeric keys", - input: { "0": "zero", "1": "one" }, - expected: "0=zero&1=one", - }, - { - description: "should handle boolean values in objects", - input: { enabled: true, disabled: false }, - expected: "enabled=true&disabled=false", - }, - { - description: "should handle empty strings", - input: { name: "", description: "test" }, - expected: "name=&description=test", - }, - { - description: "should handle zero values", - input: { count: 0, price: 0.0 }, - expected: "count=0&price=0", - }, - { - description: "should handle arrays with empty strings", - input: { items: ["a", "", "c"] }, - expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c", - }, - ]; - - edgeCaseTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Options combinations", () => { - interface OptionsTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices"; encode?: boolean }; - expected: string; - } - - const optionsTests: OptionsTestCase[] = [ - { - description: "should respect both arrayFormat and encode options", - input: { items: ["a & b", "c & d"] }, - options: { arrayFormat: "repeat", encode: false }, - expected: "items=a & b&items=c & d", - }, - { - description: "should use default options when none provided", - input: { items: ["a", "b"] }, - expected: "items%5B0%5D=a&items%5B1%5D=b", - }, - { - description: "should merge provided options with defaults", - input: { items: ["a", "b"], name: "John Doe" }, - options: { encode: false }, - expected: "items[0]=a&items[1]=b&name=John Doe", - }, - ]; - - optionsTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/.gitkeep b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/auth.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/auth.test.ts deleted file mode 100644 index c5f079fc2e35..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/auth.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "../../src/Client"; -import { mockServerPool } from "../mock-server/MockServerPool"; -import { mockOAuthScheme } from "./mockAuth"; - -describe("AuthClient", () => { - test("getTokenWithClientCredentials", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - maxRetries: 0, - clientId: "client_id", - clientSecret: "client_secret", - environment: server.baseUrl, - }); - const rawRequestBody = { - client_id: "client_id", - client_secret: "client_secret", - audience: "https://api.example.com", - grant_type: "client_credentials", - scope: "scope", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); - - const response = await client.auth.getTokenWithClientCredentials({ - client_id: "client_id", - client_secret: "client_secret", - scope: "scope", - }); - expect(response).toEqual({ - access_token: "access_token", - expires_in: 1, - refresh_token: "refresh_token", - }); - }); - - test("refreshToken", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - maxRetries: 0, - clientId: "client_id", - clientSecret: "client_secret", - environment: server.baseUrl, - }); - const rawRequestBody = { - client_id: "client_id", - client_secret: "client_secret", - refresh_token: "refresh_token", - audience: "https://api.example.com", - grant_type: "refresh_token", - scope: "scope", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); - - const response = await client.auth.refreshToken({ - client_id: "client_id", - client_secret: "client_secret", - refresh_token: "refresh_token", - scope: "scope", - }); - expect(response).toEqual({ - access_token: "access_token", - expires_in: 1, - refresh_token: "refresh_token", - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/mockAuth.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/mockAuth.ts deleted file mode 100644 index 0dbb0e6081c2..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/mockAuth.ts +++ /dev/null @@ -1,21 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { MockServer } from "../mock-server/MockServer"; - -export function mockOAuthScheme(server: MockServer): void { - const rawRequestBody = { - client_id: "client_id", - client_secret: "client_secret", - audience: "https://api.example.com", - grant_type: "client_credentials", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested-no-auth/api.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested-no-auth/api.test.ts deleted file mode 100644 index 05f934e579cb..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested-no-auth/api.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "../../../src/Client"; -import { mockServerPool } from "../../mock-server/MockServerPool"; -import { mockOAuthScheme } from "../mockAuth"; - -describe("ApiClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - maxRetries: 0, - clientId: "client_id", - clientSecret: "client_secret", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/nested-no-auth/get-something").respondWith().statusCode(200).build(); - - const response = await client.nestedNoAuth.api.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested/api.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested/api.test.ts deleted file mode 100644 index 60a506206185..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/nested/api.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "../../../src/Client"; -import { mockServerPool } from "../../mock-server/MockServerPool"; -import { mockOAuthScheme } from "../mockAuth"; - -describe("ApiClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - maxRetries: 0, - clientId: "client_id", - clientSecret: "client_secret", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/nested/get-something").respondWith().statusCode(200).build(); - - const response = await client.nested.api.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/simple.test.ts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/simple.test.ts deleted file mode 100644 index 18d80979cb98..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tests/wire/simple.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsEnvironmentVariablesClient } from "../../src/Client"; -import { mockServerPool } from "../mock-server/MockServerPool"; -import { mockOAuthScheme } from "./mockAuth"; - -describe("SimpleClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsEnvironmentVariablesClient({ - maxRetries: 0, - clientId: "client_id", - clientSecret: "client_secret", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/get-something").respondWith().statusCode(200).build(); - - const response = await client.simple.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.base.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.base.json deleted file mode 100644 index d7627675de20..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.base.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "extendedDiagnostics": true, - "strict": true, - "target": "ES6", - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": true, - "outDir": "dist", - "rootDir": "src", - "baseUrl": "src", - "isolatedModules": true, - "isolatedDeclarations": true - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.cjs.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.cjs.json deleted file mode 100644 index 5c11446f5984..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.cjs.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist/cjs" - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.esm.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.esm.json deleted file mode 100644 index 6ce909748b2c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.esm.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "module": "esnext", - "outDir": "dist/esm", - "verbatimModuleSyntax": true - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.json b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.json deleted file mode 100644 index d77fdf00d259..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./tsconfig.cjs.json" -} diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/vitest.config.mts b/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/vitest.config.mts deleted file mode 100644 index ba2ec4f9d45a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/token-override/vitest.config.mts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineConfig } from "vitest/config"; -export default defineConfig({ - test: { - projects: [ - { - test: { - globals: true, - name: "unit", - environment: "node", - root: "./tests", - include: ["**/*.test.{js,ts,jsx,tsx}"], - exclude: ["wire/**"], - setupFiles: ["./setup.ts"], - }, - }, - { - test: { - globals: true, - name: "wire", - environment: "node", - root: "./tests/wire", - setupFiles: ["../setup.ts", "../mock-server/setup.ts"], - }, - }, - ], - passWithNoTests: true, - }, -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/.fern/metadata.json b/seed/ts-sdk/oauth-client-credentials/token-override/.fern/metadata.json deleted file mode 100644 index a63ae087b8ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/.fern/metadata.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "cliVersion": "DUMMY", - "generatorName": "fernapi/fern-typescript-sdk", - "generatorVersion": "latest", - "generatorConfig": { - "oauthTokenOverride": true - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/.github/workflows/ci.yml b/seed/ts-sdk/oauth-client-credentials/token-override/.github/workflows/ci.yml deleted file mode 100644 index 836106996595..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/.github/workflows/ci.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: ci - -on: [push] - -jobs: - compile: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Compile - run: pnpm build - - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Test - run: pnpm test - - publish: - needs: [ compile, test ] - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Set up node - uses: actions/setup-node@v6 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Publish to npm - run: | - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} - publish() { # use latest npm to ensure OIDC support - npx -y npm@latest publish "$@" - } - if [[ ${GITHUB_REF} == *alpha* ]]; then - publish --access public --tag alpha - elif [[ ${GITHUB_REF} == *beta* ]]; then - publish --access public --tag beta - else - publish --access public - fi - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/.gitignore b/seed/ts-sdk/oauth-client-credentials/token-override/.gitignore deleted file mode 100644 index 72271e049c02..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -.DS_Store -/dist \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/CONTRIBUTING.md b/seed/ts-sdk/oauth-client-credentials/token-override/CONTRIBUTING.md deleted file mode 100644 index fe5bc2f77e0b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/CONTRIBUTING.md +++ /dev/null @@ -1,133 +0,0 @@ -# Contributing - -Thanks for your interest in contributing to this SDK! This document provides guidelines for contributing to the project. - -## Getting Started - -### Prerequisites - -- Node.js 20 or higher -- pnpm package manager - -### Installation - -Install the project dependencies: - -```bash -pnpm install -``` - -### Building - -Build the project: - -```bash -pnpm build -``` - -### Testing - -Run the test suite: - -```bash -pnpm test -``` - -Run specific test types: -- `pnpm test:unit` - Run unit tests -- `pnpm test:wire` - Run wire/integration tests - -### Linting and Formatting - -Check code style: - -```bash -pnpm run lint -pnpm run format:check -``` - -Fix code style issues: - -```bash -pnpm run lint:fix -pnpm run format:fix -``` - -Or use the combined check command: - -```bash -pnpm run check:fix -``` - -## About Generated Code - -**Important**: Most files in this SDK are automatically generated by [Fern](https://buildwithfern.com) from the API definition. Direct modifications to generated files will be overwritten the next time the SDK is generated. - -### Generated Files - -The following directories contain generated code: -- `src/api/` - API client classes and types -- `src/serialization/` - Serialization/deserialization logic -- Most TypeScript files in `src/` - -### How to Customize - -If you need to customize the SDK, you have two options: - -#### Option 1: Use `.fernignore` - -For custom code that should persist across SDK regenerations: - -1. Create a `.fernignore` file in the project root -2. Add file patterns for files you want to preserve (similar to `.gitignore` syntax) -3. Add your custom code to those files - -Files listed in `.fernignore` will not be overwritten when the SDK is regenerated. - -For more information, see the [Fern documentation on custom code](https://buildwithfern.com/learn/sdks/overview/custom-code). - -#### Option 2: Contribute to the Generator - -If you want to change how code is generated for all users of this SDK: - -1. The TypeScript SDK generator lives in the [Fern repository](https://github.com/fern-api/fern) -2. Generator code is located at `generators/typescript/sdk/` -3. Follow the [Fern contributing guidelines](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md) -4. Submit a pull request with your changes to the generator - -This approach is best for: -- Bug fixes in generated code -- New features that would benefit all users -- Improvements to code generation patterns - -## Making Changes - -### Workflow - -1. Create a new branch for your changes -2. Make your modifications -3. Run tests to ensure nothing breaks: `pnpm test` -4. Run linting and formatting: `pnpm run check:fix` -5. Build the project: `pnpm build` -6. Commit your changes with a clear commit message -7. Push your branch and create a pull request - -### Commit Messages - -Write clear, descriptive commit messages that explain what changed and why. - -### Code Style - -This project uses automated code formatting and linting. Run `pnpm run check:fix` before committing to ensure your code meets the project's style guidelines. - -## Questions or Issues? - -If you have questions or run into issues: - -1. Check the [Fern documentation](https://buildwithfern.com) -2. Search existing [GitHub issues](https://github.com/fern-api/fern/issues) -3. Open a new issue if your question hasn't been addressed - -## License - -By contributing to this project, you agree that your contributions will be licensed under the same license as the project. diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/README.md b/seed/ts-sdk/oauth-client-credentials/token-override/README.md deleted file mode 100644 index 5cb7545c8da5..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# Seed TypeScript Library - -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) -[![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials)](https://www.npmjs.com/package/@fern/oauth-client-credentials) - -The Seed TypeScript library provides convenient access to the Seed APIs from TypeScript. - -## Table of Contents - -- [Installation](#installation) -- [Reference](#reference) -- [Usage](#usage) -- [Authentication](#authentication) -- [Request and Response Types](#request-and-response-types) -- [Exception Handling](#exception-handling) -- [Advanced](#advanced) - - [Additional Headers](#additional-headers) - - [Additional Query String Parameters](#additional-query-string-parameters) - - [Retries](#retries) - - [Timeouts](#timeouts) - - [Aborting Requests](#aborting-requests) - - [Access Raw Response Data](#access-raw-response-data) - - [Logging](#logging) - - [Runtime Compatibility](#runtime-compatibility) -- [Contributing](#contributing) - -## Installation - -```sh -npm i -s @fern/oauth-client-credentials -``` - -## Reference - -A full reference for this library is available [here](./reference.md). - -## Usage - -Instantiate and use the client with the following: - -```typescript -import { SeedOauthClientCredentialsClient } from "@fern/oauth-client-credentials"; - -const client = new SeedOauthClientCredentialsClient({ environment: "YOUR_BASE_URL", clientId: "YOUR_CLIENT_ID", clientSecret: "YOUR_CLIENT_SECRET" }); -await client.auth.getTokenWithClientCredentials({ - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - scope: "read:users" -}); -``` - -## Authentication - -The SDK supports OAuth authentication with two options: - -**Option 1: OAuth Client Credentials Flow** - -Use this when you want the SDK to automatically handle OAuth token retrieval and refreshing: - -```typescript -import { SeedOauthClientCredentialsClient } from "@fern/oauth-client-credentials"; - -const client = new SeedOauthClientCredentialsClient({ - clientId: "YOUR_CLIENT_ID", - clientSecret: "YOUR_CLIENT_SECRET", - ... -}); -``` - -**Option 2: Token Override** - -Use this when you already have a valid bearer token and want to skip the OAuth flow: - -```typescript -import { SeedOauthClientCredentialsClient } from "@fern/oauth-client-credentials"; - -const client = new SeedOauthClientCredentialsClient({ - token: "my-pre-generated-bearer-token", - ... -}); -``` - -## Request and Response Types - -The SDK exports all request and response types as TypeScript interfaces. Simply import them with the -following namespace: - -```typescript -import { SeedOauthClientCredentials } from "@fern/oauth-client-credentials"; - -const request: SeedOauthClientCredentials.GetTokenRequest = { - ... -}; -``` - -## Exception Handling - -When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error -will be thrown. - -```typescript -import { SeedOauthClientCredentialsError } from "@fern/oauth-client-credentials"; - -try { - await client.auth.getTokenWithClientCredentials(...); -} catch (err) { - if (err instanceof SeedOauthClientCredentialsError) { - console.log(err.statusCode); - console.log(err.message); - console.log(err.body); - console.log(err.rawResponse); - } -} -``` - -## Advanced - -### Additional Headers - -If you would like to send additional headers as part of the request, use the `headers` request option. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - headers: { - 'X-Custom-Header': 'custom value' - } -}); -``` - -### Additional Query String Parameters - -If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - queryParams: { - 'customQueryParamKey': 'custom query param value' - } -}); -``` - -### Retries - -The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long -as the request is deemed retryable and the number of retry attempts has not grown larger than the configured -retry limit (default: 2). - -A request is deemed retryable when any of the following HTTP status codes is returned: - -- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) -- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) -- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) - -Use the `maxRetries` request option to configure this behavior. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - maxRetries: 0 // override maxRetries at the request level -}); -``` - -### Timeouts - -The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. - -```typescript -const response = await client.auth.getTokenWithClientCredentials(..., { - timeoutInSeconds: 30 // override timeout to 30s -}); -``` - -### Aborting Requests - -The SDK allows users to abort requests at any point by passing in an abort signal. - -```typescript -const controller = new AbortController(); -const response = await client.auth.getTokenWithClientCredentials(..., { - abortSignal: controller.signal -}); -controller.abort(); // aborts the request -``` - -### Access Raw Response Data - -The SDK provides access to raw response data, including headers, through the `.withRawResponse()` method. -The `.withRawResponse()` method returns a promise that results to an object with a `data` and a `rawResponse` property. - -```typescript -const { data, rawResponse } = await client.auth.getTokenWithClientCredentials(...).withRawResponse(); - -console.log(data); -console.log(rawResponse.headers['X-My-Header']); -``` - -### Logging - -The SDK supports logging. You can configure the logger by passing in a `logging` object to the client options. - -```typescript -import { SeedOauthClientCredentialsClient, logging } from "@fern/oauth-client-credentials"; - -const client = new SeedOauthClientCredentialsClient({ - ... - logging: { - level: logging.LogLevel.Debug, // defaults to logging.LogLevel.Info - logger: new logging.ConsoleLogger(), // defaults to ConsoleLogger - silent: false, // defaults to true, set to false to enable logging - } -}); -``` -The `logging` object can have the following properties: -- `level`: The log level to use. Defaults to `logging.LogLevel.Info`. -- `logger`: The logger to use. Defaults to a `logging.ConsoleLogger`. -- `silent`: Whether to silence the logger. Defaults to `true`. - -The `level` property can be one of the following values: -- `logging.LogLevel.Debug` -- `logging.LogLevel.Info` -- `logging.LogLevel.Warn` -- `logging.LogLevel.Error` - -To provide a custom logger, you can pass in an object that implements the `logging.ILogger` interface. - -
-Custom logger examples - -Here's an example using the popular `winston` logging library. -```ts -import winston from 'winston'; - -const winstonLogger = winston.createLogger({...}); - -const logger: logging.ILogger = { - debug: (msg, ...args) => winstonLogger.debug(msg, ...args), - info: (msg, ...args) => winstonLogger.info(msg, ...args), - warn: (msg, ...args) => winstonLogger.warn(msg, ...args), - error: (msg, ...args) => winstonLogger.error(msg, ...args), -}; -``` - -Here's an example using the popular `pino` logging library. - -```ts -import pino from 'pino'; - -const pinoLogger = pino({...}); - -const logger: logging.ILogger = { - debug: (msg, ...args) => pinoLogger.debug(args, msg), - info: (msg, ...args) => pinoLogger.info(args, msg), - warn: (msg, ...args) => pinoLogger.warn(args, msg), - error: (msg, ...args) => pinoLogger.error(args, msg), -}; -``` -
- - -### Runtime Compatibility - - -The SDK works in the following runtimes: - - - -- Node.js 18+ -- Vercel -- Cloudflare Workers -- Deno v1.25+ -- Bun 1.0+ -- React Native - -### Customizing Fetch Client - -The SDK provides a way for you to customize the underlying HTTP client / Fetch function. If you're running in an -unsupported environment, this provides a way for you to break glass and ensure the SDK works. - -```typescript -import { SeedOauthClientCredentialsClient } from "@fern/oauth-client-credentials"; - -const client = new SeedOauthClientCredentialsClient({ - ... - fetcher: // provide your implementation here -}); -``` - -## Contributing - -While we value open-source contributions to this SDK, this library is generated programmatically. -Additions made directly to this library would have to be moved over to our generation code, -otherwise they would be overwritten upon the next generated release. Feel free to open a PR as -a proof of concept, but know that we will not be able to merge it as-is. We suggest opening -an issue first to discuss with us! - -On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/biome.json b/seed/ts-sdk/oauth-client-credentials/token-override/biome.json deleted file mode 100644 index a777468e4ae2..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/biome.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json", - "root": true, - "vcs": { - "enabled": false - }, - "files": { - "ignoreUnknown": true, - "includes": [ - "**", - "!!dist", - "!!**/dist", - "!!lib", - "!!**/lib", - "!!_tmp_*", - "!!**/_tmp_*", - "!!*.tmp", - "!!**/*.tmp", - "!!.tmp/", - "!!**/.tmp/", - "!!*.log", - "!!**/*.log", - "!!**/.DS_Store", - "!!**/Thumbs.db" - ] - }, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 4, - "lineWidth": 120 - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } - } - }, - "linter": { - "rules": { - "style": { - "useNodejsImportProtocol": "off" - }, - "suspicious": { - "noAssignInExpressions": "warn", - "noUselessEscapeInString": { - "level": "warn", - "fix": "none", - "options": {} - }, - "noThenProperty": "warn", - "useIterableCallbackReturn": "warn", - "noShadowRestrictedNames": "warn", - "noTsIgnore": { - "level": "warn", - "fix": "none", - "options": {} - }, - "noConfusingVoidType": { - "level": "warn", - "fix": "none", - "options": {} - } - } - } - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/package.json b/seed/ts-sdk/oauth-client-credentials/token-override/package.json deleted file mode 100644 index e2eb2b0de49a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "@fern/oauth-client-credentials", - "version": "0.0.1", - "private": false, - "repository": "github:oauth-client-credentials/fern", - "type": "commonjs", - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.mjs", - "types": "./dist/cjs/index.d.ts", - "exports": { - ".": { - "types": "./dist/cjs/index.d.ts", - "import": { - "types": "./dist/esm/index.d.mts", - "default": "./dist/esm/index.mjs" - }, - "require": { - "types": "./dist/cjs/index.d.ts", - "default": "./dist/cjs/index.js" - }, - "default": "./dist/cjs/index.js" - }, - "./package.json": "./package.json" - }, - "files": [ - "dist", - "reference.md", - "README.md", - "LICENSE" - ], - "scripts": { - "format": "biome format --write --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "format:check": "biome format --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "lint": "biome lint --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "lint:fix": "biome lint --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "check": "biome check --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "check:fix": "biome check --fix --unsafe --skip-parse-errors --no-errors-on-unmatched --max-diagnostics=none", - "build": "pnpm build:cjs && pnpm build:esm", - "build:cjs": "tsc --project ./tsconfig.cjs.json", - "build:esm": "tsc --project ./tsconfig.esm.json && node scripts/rename-to-esm-files.js dist/esm", - "test": "vitest", - "test:unit": "vitest --project unit", - "test:wire": "vitest --project wire" - }, - "dependencies": {}, - "devDependencies": { - "webpack": "^5.97.1", - "ts-loader": "^9.5.1", - "vitest": "^3.2.4", - "msw": "2.11.2", - "@types/node": "^18.19.70", - "typescript": "~5.7.2", - "@biomejs/biome": "2.3.1" - }, - "browser": { - "fs": false, - "os": false, - "path": false, - "stream": false - }, - "packageManager": "pnpm@10.20.0", - "engines": { - "node": ">=18.0.0" - }, - "sideEffects": false -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/pnpm-workspace.yaml b/seed/ts-sdk/oauth-client-credentials/token-override/pnpm-workspace.yaml deleted file mode 100644 index 6e4c395107df..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/pnpm-workspace.yaml +++ /dev/null @@ -1 +0,0 @@ -packages: ['.'] \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/reference.md b/seed/ts-sdk/oauth-client-credentials/token-override/reference.md deleted file mode 100644 index ca375b340db1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/reference.md +++ /dev/null @@ -1,234 +0,0 @@ -# Reference -## Auth -
client.auth.getTokenWithClientCredentials({ ...params }) -> SeedOauthClientCredentials.TokenResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.auth.getTokenWithClientCredentials({ - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - scope: "read:users" -}); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `SeedOauthClientCredentials.GetTokenRequest` - -
-
- -
-
- -**requestOptions:** `AuthClient.RequestOptions` - -
-
-
-
- - -
-
-
- -
client.auth.refreshToken({ ...params }) -> SeedOauthClientCredentials.TokenResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.auth.refreshToken({ - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - refresh_token: "refresh_token", - scope: "read:users" -}); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request:** `SeedOauthClientCredentials.RefreshTokenRequest` - -
-
- -
-
- -**requestOptions:** `AuthClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## NestedNoAuth Api -
client.nestedNoAuth.api.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.nestedNoAuth.api.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `ApiClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## Nested Api -
client.nested.api.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.nested.api.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `ApiClient.RequestOptions` - -
-
-
-
- - -
-
-
- -## Simple -
client.simple.getSomething() -> void -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.simple.getSomething(); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `SimpleClient.RequestOptions` - -
-
-
-
- - -
-
-
diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/scripts/rename-to-esm-files.js b/seed/ts-sdk/oauth-client-credentials/token-override/scripts/rename-to-esm-files.js deleted file mode 100644 index dc1df1cbbacb..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/scripts/rename-to-esm-files.js +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env node - -const fs = require("fs").promises; -const path = require("path"); - -const extensionMap = { - ".js": ".mjs", - ".d.ts": ".d.mts", -}; -const oldExtensions = Object.keys(extensionMap); - -async function findFiles(rootPath) { - const files = []; - - async function scan(directory) { - const entries = await fs.readdir(directory, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(directory, entry.name); - - if (entry.isDirectory()) { - if (entry.name !== "node_modules" && !entry.name.startsWith(".")) { - await scan(fullPath); - } - } else if (entry.isFile()) { - if (oldExtensions.some((ext) => entry.name.endsWith(ext))) { - files.push(fullPath); - } - } - } - } - - await scan(rootPath); - return files; -} - -async function updateFiles(files) { - const updatedFiles = []; - for (const file of files) { - const updated = await updateFileContents(file); - updatedFiles.push(updated); - } - - console.log(`Updated imports in ${updatedFiles.length} files.`); -} - -async function updateFileContents(file) { - const content = await fs.readFile(file, "utf8"); - - let newContent = content; - // Update each extension type defined in the map - for (const [oldExt, newExt] of Object.entries(extensionMap)) { - // Handle static imports/exports - const staticRegex = new RegExp(`(import|export)(.+from\\s+['"])(\\.\\.?\\/[^'"]+)(\\${oldExt})(['"])`, "g"); - newContent = newContent.replace(staticRegex, `$1$2$3${newExt}$5`); - - // Handle dynamic imports (yield import, await import, regular import()) - const dynamicRegex = new RegExp( - `(yield\\s+import|await\\s+import|import)\\s*\\(\\s*['"](\\.\\.\?\\/[^'"]+)(\\${oldExt})['"]\\s*\\)`, - "g", - ); - newContent = newContent.replace(dynamicRegex, `$1("$2${newExt}")`); - } - - if (content !== newContent) { - await fs.writeFile(file, newContent, "utf8"); - return true; - } - return false; -} - -async function renameFiles(files) { - let counter = 0; - for (const file of files) { - const ext = oldExtensions.find((ext) => file.endsWith(ext)); - const newExt = extensionMap[ext]; - - if (newExt) { - const newPath = file.slice(0, -ext.length) + newExt; - await fs.rename(file, newPath); - counter++; - } - } - - console.log(`Renamed ${counter} files.`); -} - -async function main() { - try { - const targetDir = process.argv[2]; - if (!targetDir) { - console.error("Please provide a target directory"); - process.exit(1); - } - - const targetPath = path.resolve(targetDir); - const targetStats = await fs.stat(targetPath); - - if (!targetStats.isDirectory()) { - console.error("The provided path is not a directory"); - process.exit(1); - } - - console.log(`Scanning directory: ${targetDir}`); - - const files = await findFiles(targetDir); - - if (files.length === 0) { - console.log("No matching files found."); - process.exit(0); - } - - console.log(`Found ${files.length} files.`); - await updateFiles(files); - await renameFiles(files); - console.log("\nDone!"); - } catch (error) { - console.error("An error occurred:", error.message); - process.exit(1); - } -} - -main(); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/snippet.json b/seed/ts-sdk/oauth-client-credentials/token-override/snippet.json deleted file mode 100644 index 0a2169bafea6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/snippet.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "endpoints": [ - { - "id": { - "path": "/token", - "method": "POST", - "identifier_override": "endpoint_auth.getTokenWithClientCredentials" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsClient } from \"@fern/oauth-client-credentials\";\n\nconst client = new SeedOauthClientCredentialsClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.auth.getTokenWithClientCredentials({\n client_id: \"my_oauth_app_123\",\n client_secret: \"sk_live_abcdef123456789\",\n scope: \"read:users\"\n});\n" - } - }, - { - "id": { - "path": "/token", - "method": "POST", - "identifier_override": "endpoint_auth.refreshToken" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsClient } from \"@fern/oauth-client-credentials\";\n\nconst client = new SeedOauthClientCredentialsClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.auth.refreshToken({\n client_id: \"my_oauth_app_123\",\n client_secret: \"sk_live_abcdef123456789\",\n refresh_token: \"refresh_token\",\n scope: \"read:users\"\n});\n" - } - }, - { - "id": { - "path": "/nested-no-auth/get-something", - "method": "GET", - "identifier_override": "endpoint_nested-no-auth/api.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsClient } from \"@fern/oauth-client-credentials\";\n\nconst client = new SeedOauthClientCredentialsClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.nestedNoAuth.api.getSomething();\n" - } - }, - { - "id": { - "path": "/nested/get-something", - "method": "GET", - "identifier_override": "endpoint_nested/api.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsClient } from \"@fern/oauth-client-credentials\";\n\nconst client = new SeedOauthClientCredentialsClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.nested.api.getSomething();\n" - } - }, - { - "id": { - "path": "/get-something", - "method": "GET", - "identifier_override": "endpoint_simple.getSomething" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedOauthClientCredentialsClient } from \"@fern/oauth-client-credentials\";\n\nconst client = new SeedOauthClientCredentialsClient({ environment: \"YOUR_BASE_URL\", clientId: \"YOUR_CLIENT_ID\", clientSecret: \"YOUR_CLIENT_SECRET\" });\nawait client.simple.getSomething();\n" - } - } - ], - "types": {} -} \ No newline at end of file diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/BaseClient.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/BaseClient.ts deleted file mode 100644 index 259f18dba387..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/BaseClient.ts +++ /dev/null @@ -1,81 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { OAuthAuthProvider } from "./auth/OAuthAuthProvider.js"; -import { mergeHeaders } from "./core/headers.js"; -import * as core from "./core/index.js"; - -export type BaseClientOptions = { - environment: core.Supplier; - /** Specify a custom URL to connect the client to. */ - baseUrl?: core.Supplier; - /** Additional headers to include in requests. */ - headers?: Record | null | undefined>; - /** The default maximum time to wait for a response in seconds. */ - timeoutInSeconds?: number; - /** The default number of times to retry the request. Defaults to 2. */ - maxRetries?: number; - /** Provide a custom fetch implementation. Useful for platforms that don't have a built-in fetch or need a custom implementation. */ - fetch?: typeof fetch; - /** Configure logging for the client. */ - logging?: core.logging.LogConfig | core.logging.Logger; -} & OAuthAuthProvider.AuthOptions; - -export interface BaseRequestOptions { - /** The maximum time to wait for a response in seconds. */ - timeoutInSeconds?: number; - /** The number of times to retry the request. Defaults to 2. */ - maxRetries?: number; - /** A hook to abort the request. */ - abortSignal?: AbortSignal; - /** Additional query string parameters to include in the request. */ - queryParams?: Record; - /** Additional headers to include in the request. */ - headers?: Record | null | undefined>; -} - -export type NormalizedClientOptions = T & { - logging: core.logging.Logger; - authProvider?: core.AuthProvider; -}; - -export type NormalizedClientOptionsWithAuth = NormalizedClientOptions & { - authProvider: core.AuthProvider; -}; - -export function normalizeClientOptions(options: T): NormalizedClientOptions { - const headers = mergeHeaders( - { - "X-Fern-Language": "JavaScript", - "X-Fern-SDK-Name": "@fern/oauth-client-credentials", - "X-Fern-SDK-Version": "0.0.1", - "User-Agent": "@fern/oauth-client-credentials/0.0.1", - "X-Fern-Runtime": core.RUNTIME.type, - "X-Fern-Runtime-Version": core.RUNTIME.version, - }, - options?.headers, - ); - - return { - ...options, - logging: core.logging.createLogger(options?.logging), - headers, - } as NormalizedClientOptions; -} - -export function normalizeClientOptionsWithAuth( - options: T, -): NormalizedClientOptionsWithAuth { - const normalized = normalizeClientOptions(options) as NormalizedClientOptionsWithAuth; - const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized); - normalized.authProvider ??= OAuthAuthProvider.createInstance(normalizedWithNoOpAuthProvider); - return normalized; -} - -function withNoOpAuthProvider( - options: NormalizedClientOptions, -): NormalizedClientOptionsWithAuth { - return { - ...options, - authProvider: new core.NoOpAuthProvider(), - }; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/Client.ts deleted file mode 100644 index 00fde72e2479..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/Client.ts +++ /dev/null @@ -1,42 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { AuthClient } from "./api/resources/auth/client/Client.js"; -import { NestedClient } from "./api/resources/nested/client/Client.js"; -import { NestedNoAuthClient } from "./api/resources/nestedNoAuth/client/Client.js"; -import { SimpleClient } from "./api/resources/simple/client/Client.js"; -import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "./BaseClient.js"; - -export declare namespace SeedOauthClientCredentialsClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class SeedOauthClientCredentialsClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - protected _auth: AuthClient | undefined; - protected _nestedNoAuth: NestedNoAuthClient | undefined; - protected _nested: NestedClient | undefined; - protected _simple: SimpleClient | undefined; - - constructor(options: SeedOauthClientCredentialsClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - public get auth(): AuthClient { - return (this._auth ??= new AuthClient(this._options)); - } - - public get nestedNoAuth(): NestedNoAuthClient { - return (this._nestedNoAuth ??= new NestedNoAuthClient(this._options)); - } - - public get nested(): NestedClient { - return (this._nested ??= new NestedClient(this._options)); - } - - public get simple(): SimpleClient { - return (this._simple ??= new SimpleClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/index.ts deleted file mode 100644 index e445af0d831e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/Client.ts deleted file mode 100644 index 6fbe7c16db59..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/Client.ts +++ /dev/null @@ -1,142 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../core/headers.js"; -import * as core from "../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../errors/index.js"; -import type * as SeedOauthClientCredentials from "../../../index.js"; - -export declare namespace AuthClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class AuthClient { - protected readonly _options: NormalizedClientOptions; - - constructor(options: AuthClient.Options) { - this._options = normalizeClientOptions(options); - } - - /** - * @param {SeedOauthClientCredentials.GetTokenRequest} request - * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.auth.getTokenWithClientCredentials({ - * client_id: "my_oauth_app_123", - * client_secret: "sk_live_abcdef123456789", - * scope: "read:users" - * }) - */ - public getTokenWithClientCredentials( - request: SeedOauthClientCredentials.GetTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getTokenWithClientCredentials(request, requestOptions)); - } - - private async __getTokenWithClientCredentials( - request: SeedOauthClientCredentials.GetTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/token", - ), - method: "POST", - headers: _headers, - contentType: "application/json", - queryParameters: requestOptions?.queryParams, - requestType: "json", - body: { ...request, audience: "https://api.example.com", grant_type: "client_credentials" }, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { - data: _response.body as SeedOauthClientCredentials.TokenResponse, - rawResponse: _response.rawResponse, - }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/token"); - } - - /** - * @param {SeedOauthClientCredentials.RefreshTokenRequest} request - * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.auth.refreshToken({ - * client_id: "my_oauth_app_123", - * client_secret: "sk_live_abcdef123456789", - * refresh_token: "refresh_token", - * scope: "read:users" - * }) - */ - public refreshToken( - request: SeedOauthClientCredentials.RefreshTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__refreshToken(request, requestOptions)); - } - - private async __refreshToken( - request: SeedOauthClientCredentials.RefreshTokenRequest, - requestOptions?: AuthClient.RequestOptions, - ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/token", - ), - method: "POST", - headers: _headers, - contentType: "application/json", - queryParameters: requestOptions?.queryParams, - requestType: "json", - body: { ...request, audience: "https://api.example.com", grant_type: "refresh_token" }, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { - data: _response.body as SeedOauthClientCredentials.TokenResponse, - rawResponse: _response.rawResponse, - }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/token"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/index.ts deleted file mode 100644 index 195f9aa8a846..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./requests/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts deleted file mode 100644 index 8d27e911452a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/GetTokenRequest.ts +++ /dev/null @@ -1,15 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * @example - * { - * client_id: "my_oauth_app_123", - * client_secret: "sk_live_abcdef123456789", - * scope: "read:users" - * } - */ -export interface GetTokenRequest { - client_id: string; - client_secret: string; - scope?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts deleted file mode 100644 index 28887dee33f8..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/RefreshTokenRequest.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * @example - * { - * client_id: "my_oauth_app_123", - * client_secret: "sk_live_abcdef123456789", - * refresh_token: "refresh_token", - * scope: "read:users" - * } - */ -export interface RefreshTokenRequest { - client_id: string; - client_secret: string; - refresh_token: string; - scope?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/index.ts deleted file mode 100644 index a5e87cc70d74..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/client/requests/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { GetTokenRequest } from "./GetTokenRequest.js"; -export type { RefreshTokenRequest } from "./RefreshTokenRequest.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/index.ts deleted file mode 100644 index d9adb1af9a93..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./types/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/TokenResponse.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/TokenResponse.ts deleted file mode 100644 index b410f59491c1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/TokenResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -/** - * An OAuth token response. - */ -export interface TokenResponse { - access_token: string; - expires_in: number; - refresh_token?: string; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/index.ts deleted file mode 100644 index 67937a969229..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/auth/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./TokenResponse.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/index.ts deleted file mode 100644 index 951213c3d797..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./auth/client/requests/index.js"; -export * as auth from "./auth/index.js"; -export * from "./auth/types/index.js"; -export * as nested from "./nested/index.js"; -export * as nestedNoAuth from "./nestedNoAuth/index.js"; -export * as simple from "./simple/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/Client.ts deleted file mode 100644 index 07288b1cd312..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/Client.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; -import { ApiClient } from "../resources/api/client/Client.js"; - -export declare namespace NestedClient { - export type Options = BaseClientOptions; -} - -export class NestedClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - protected _api: ApiClient | undefined; - - constructor(options: NestedClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - public get api(): ApiClient { - return (this._api ??= new ApiClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/index.ts deleted file mode 100644 index 9eb1192dcc32..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/Client.ts deleted file mode 100644 index 64e0f0c6fd9b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/Client.ts +++ /dev/null @@ -1,69 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../../../core/headers.js"; -import * as core from "../../../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../../../errors/index.js"; - -export declare namespace ApiClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class ApiClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - - constructor(options: ApiClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - /** - * @param {ApiClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.nested.api.getSomething() - */ - public getSomething(requestOptions?: ApiClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: ApiClient.RequestOptions): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/nested/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/nested/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/index.ts deleted file mode 100644 index e2d4d5d890ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nested/resources/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as api from "./api/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/Client.ts deleted file mode 100644 index 32241ef71181..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/Client.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; -import { ApiClient } from "../resources/api/client/Client.js"; - -export declare namespace NestedNoAuthClient { - export type Options = BaseClientOptions; -} - -export class NestedNoAuthClient { - protected readonly _options: NormalizedClientOptions; - protected _api: ApiClient | undefined; - - constructor(options: NestedNoAuthClient.Options) { - this._options = normalizeClientOptions(options); - } - - public get api(): ApiClient { - return (this._api ??= new ApiClient(this._options)); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/index.ts deleted file mode 100644 index 9eb1192dcc32..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./client/index.js"; -export * from "./resources/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts deleted file mode 100644 index a84066079dee..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/Client.ts +++ /dev/null @@ -1,64 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../../../core/headers.js"; -import * as core from "../../../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../../../errors/index.js"; - -export declare namespace ApiClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class ApiClient { - protected readonly _options: NormalizedClientOptions; - - constructor(options: ApiClient.Options) { - this._options = normalizeClientOptions(options); - } - - /** - * @param {ApiClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.nestedNoAuth.api.getSomething() - */ - public getSomething(requestOptions?: ApiClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: ApiClient.RequestOptions): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/nested-no-auth/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/nested-no-auth/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/index.ts deleted file mode 100644 index e2d4d5d890ce..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/nestedNoAuth/resources/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as api from "./api/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/Client.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/Client.ts deleted file mode 100644 index 477434bf9538..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/Client.ts +++ /dev/null @@ -1,69 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; -import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; -import { mergeHeaders } from "../../../../core/headers.js"; -import * as core from "../../../../core/index.js"; -import { handleNonStatusCodeError } from "../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../errors/index.js"; - -export declare namespace SimpleClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class SimpleClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - - constructor(options: SimpleClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - /** - * @param {SimpleClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.simple.getSomething() - */ - public getSomething(requestOptions?: SimpleClient.RequestOptions): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__getSomething(requestOptions)); - } - - private async __getSomething(requestOptions?: SimpleClient.RequestOptions): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await core.fetcher({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)), - "/get-something", - ), - method: "GET", - headers: _headers, - queryParameters: requestOptions?.queryParams, - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedOauthClientCredentialsError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - - return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/get-something"); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/index.ts deleted file mode 100644 index cb0ff5c3b541..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/index.ts deleted file mode 100644 index 914b8c3c7214..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/api/resources/simple/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/OAuthAuthProvider.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/OAuthAuthProvider.ts deleted file mode 100644 index 33f3ef7fad48..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/OAuthAuthProvider.ts +++ /dev/null @@ -1,147 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { AuthClient } from "../api/resources/auth/client/Client.js"; -import type { BaseClientOptions } from "../BaseClient.js"; -import * as core from "../core/index.js"; -import * as errors from "../errors/index.js"; - -export class OAuthAuthProvider implements core.AuthProvider { - private readonly BUFFER_IN_MINUTES: number = 2; - private readonly _clientId: core.Supplier; - private readonly _clientSecret: core.Supplier; - private readonly _authClient: AuthClient; - private _accessToken: string | undefined; - private _expiresAt: Date; - private _refreshPromise: Promise | undefined; - - constructor(options: OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.ClientCredentials) { - if (options.clientId == null) { - throw new errors.SeedOauthClientCredentialsError({ - message: "clientId is required. Please provide it in options.", - }); - } - this._clientId = options.clientId; - if (options.clientSecret == null) { - throw new errors.SeedOauthClientCredentialsError({ - message: "clientSecret is required. Please provide it in options.", - }); - } - this._clientSecret = options.clientSecret; - this._authClient = new AuthClient(options); - this._expiresAt = new Date(); - } - - public static canCreate( - options: OAuthAuthProvider.Options, - ): options is OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.ClientCredentials { - return ( - "clientId" in options && - options.clientId != null && - "clientSecret" in options && - options.clientSecret != null - ); - } - - public async getAuthRequest(arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - const token = await this.getToken(arg); - - return { - headers: { - Authorization: `Bearer ${token}`, - }, - }; - } - - private async getToken(arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - if (this._accessToken && this._expiresAt > new Date()) { - return this._accessToken; - } - // If a refresh is already in progress, return the existing promise - if (this._refreshPromise != null) { - return this._refreshPromise; - } - return this.refresh(arg); - } - - private async refresh(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - this._refreshPromise = (async () => { - try { - const clientId = await core.Supplier.get(this._clientId); - - const clientSecret = await core.Supplier.get(this._clientSecret); - const tokenResponse = await this._authClient.getTokenWithClientCredentials({ - client_id: clientId, - client_secret: clientSecret, - }); - - this._accessToken = tokenResponse.access_token; - this._expiresAt = this.getExpiresAt(tokenResponse.expires_in, this.BUFFER_IN_MINUTES); - return this._accessToken; - } finally { - this._refreshPromise = undefined; - } - })(); - return this._refreshPromise; - } - - private getExpiresAt(expiresInSeconds: number, bufferInMinutes: number): Date { - const now = new Date(); - return new Date(now.getTime() + expiresInSeconds * 1000 - bufferInMinutes * 60 * 1000); - } -} - -export class OAuthTokenOverrideAuthProvider implements core.AuthProvider { - private readonly _token: core.Supplier; - - constructor(options: OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.TokenOverride) { - if (options.token == null) { - throw new errors.SeedOauthClientCredentialsError({ - message: "token is required. Please provide it in options.", - }); - } - this._token = options.token; - } - - public static canCreate( - options: OAuthAuthProvider.Options, - ): options is OAuthAuthProvider.Options & OAuthAuthProvider.AuthOptions.TokenOverride { - return "token" in options && options.token != null; - } - - public async getAuthRequest(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { - return { - headers: { - Authorization: `Bearer ${await core.Supplier.get(this._token)}`, - }, - }; - } -} - -export namespace OAuthAuthProvider { - export type AuthOptions = AuthOptions.ClientCredentials | AuthOptions.TokenOverride; - - export namespace AuthOptions { - export interface ClientCredentials { - clientId: core.Supplier; - clientSecret: core.Supplier; - } - - export interface TokenOverride { - token: core.Supplier; - } - } - - export type Options = BaseClientOptions; - - export function createInstance(options: Options): core.AuthProvider { - if (OAuthTokenOverrideAuthProvider.canCreate(options)) { - return new OAuthTokenOverrideAuthProvider(options); - } else if (OAuthAuthProvider.canCreate(options)) { - return new OAuthAuthProvider(options); - } - throw new errors.SeedOauthClientCredentialsError({ - message: - "Insufficient options to create OAuthAuthProvider. Please provide either clientId and clientSecret, or token.", - }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/index.ts deleted file mode 100644 index 83e326d1824d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OAuthAuthProvider } from "./OAuthAuthProvider.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthProvider.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthProvider.ts deleted file mode 100644 index 895a50ff30da..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthProvider.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { EndpointMetadata } from "../fetcher/EndpointMetadata.js"; -import type { AuthRequest } from "./AuthRequest.js"; - -export interface AuthProvider { - getAuthRequest(arg?: { endpointMetadata?: EndpointMetadata }): Promise; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthRequest.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthRequest.ts deleted file mode 100644 index f6218b42211e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/AuthRequest.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Request parameters for authentication requests. - */ -export interface AuthRequest { - /** - * The headers to be included in the request. - */ - headers: Record; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BasicAuth.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BasicAuth.ts deleted file mode 100644 index a64235910062..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BasicAuth.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { base64Decode, base64Encode } from "../base64.js"; - -export interface BasicAuth { - username: string; - password: string; -} - -const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; - -export const BasicAuth = { - toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { - if (basicAuth == null) { - return undefined; - } - const token = base64Encode(`${basicAuth.username}:${basicAuth.password}`); - return `Basic ${token}`; - }, - fromAuthorizationHeader: (header: string): BasicAuth => { - const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); - const decoded = base64Decode(credentials); - const [username, ...passwordParts] = decoded.split(":"); - const password = passwordParts.length > 0 ? passwordParts.join(":") : undefined; - - if (username == null || password == null) { - throw new Error("Invalid basic auth"); - } - return { - username, - password, - }; - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BearerToken.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BearerToken.ts deleted file mode 100644 index c44a06c38f06..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/BearerToken.ts +++ /dev/null @@ -1,20 +0,0 @@ -export type BearerToken = string; - -const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; - -function toAuthorizationHeader(token: string | undefined): string | undefined { - if (token == null) { - return undefined; - } - return `Bearer ${token}`; -} - -export const BearerToken: { - toAuthorizationHeader: typeof toAuthorizationHeader; - fromAuthorizationHeader: (header: string) => BearerToken; -} = { - toAuthorizationHeader: toAuthorizationHeader, - fromAuthorizationHeader: (header: string): BearerToken => { - return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/NoOpAuthProvider.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/NoOpAuthProvider.ts deleted file mode 100644 index 5b7acfd2bd8b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/NoOpAuthProvider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { AuthProvider } from "./AuthProvider.js"; -import type { AuthRequest } from "./AuthRequest.js"; - -export class NoOpAuthProvider implements AuthProvider { - public getAuthRequest(): Promise { - return Promise.resolve({ headers: {} }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/index.ts deleted file mode 100644 index 2215b227709e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/auth/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type { AuthProvider } from "./AuthProvider.js"; -export type { AuthRequest } from "./AuthRequest.js"; -export { BasicAuth } from "./BasicAuth.js"; -export { BearerToken } from "./BearerToken.js"; -export { NoOpAuthProvider } from "./NoOpAuthProvider.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/base64.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/base64.ts deleted file mode 100644 index 448a0db638a6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/base64.ts +++ /dev/null @@ -1,27 +0,0 @@ -function base64ToBytes(base64: string): Uint8Array { - const binString = atob(base64); - return Uint8Array.from(binString, (m) => m.codePointAt(0)!); -} - -function bytesToBase64(bytes: Uint8Array): string { - const binString = String.fromCodePoint(...bytes); - return btoa(binString); -} - -export function base64Encode(input: string): string { - if (typeof Buffer !== "undefined") { - return Buffer.from(input, "utf8").toString("base64"); - } - - const bytes = new TextEncoder().encode(input); - return bytesToBase64(bytes); -} - -export function base64Decode(input: string): string { - if (typeof Buffer !== "undefined") { - return Buffer.from(input, "base64").toString("utf8"); - } - - const bytes = base64ToBytes(input); - return new TextDecoder().decode(bytes); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/exports.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/exports.ts deleted file mode 100644 index 69296d7100d6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/exports.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/APIResponse.ts deleted file mode 100644 index 97ab83c2b195..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/APIResponse.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { RawResponse } from "./RawResponse.js"; - -/** - * The response of an API call. - * It is a successful response or a failed response. - */ -export type APIResponse = SuccessfulResponse | FailedResponse; - -export interface SuccessfulResponse { - ok: true; - body: T; - /** - * @deprecated Use `rawResponse` instead - */ - headers?: Record; - rawResponse: RawResponse; -} - -export interface FailedResponse { - ok: false; - error: T; - rawResponse: RawResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/BinaryResponse.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/BinaryResponse.ts deleted file mode 100644 index bca7f4c77981..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/BinaryResponse.ts +++ /dev/null @@ -1,34 +0,0 @@ -export type BinaryResponse = { - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ - bodyUsed: Response["bodyUsed"]; - /** - * Returns a ReadableStream of the response body. - * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) - */ - stream: () => Response["body"]; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ - arrayBuffer: () => ReturnType; - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ - blob: () => ReturnType; - /** - * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) - * Some versions of the Fetch API may not support this method. - */ - bytes?(): ReturnType; -}; - -export function getBinaryResponse(response: Response): BinaryResponse { - const binaryResponse: BinaryResponse = { - get bodyUsed() { - return response.bodyUsed; - }, - stream: () => response.body, - arrayBuffer: response.arrayBuffer.bind(response), - blob: response.blob.bind(response), - }; - if ("bytes" in response && typeof response.bytes === "function") { - binaryResponse.bytes = response.bytes.bind(response); - } - - return binaryResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointMetadata.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointMetadata.ts deleted file mode 100644 index 998d68f5c20c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointMetadata.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type SecuritySchemeKey = string; -/** - * A collection of security schemes, where the key is the name of the security scheme and the value is the list of scopes required for that scheme. - * All schemes in the collection must be satisfied for authentication to be successful. - */ -export type SecuritySchemeCollection = Record; -export type AuthScope = string; -export type EndpointMetadata = { - /** - * An array of security scheme collections. Each collection represents an alternative way to authenticate. - */ - security?: SecuritySchemeCollection[]; -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointSupplier.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointSupplier.ts deleted file mode 100644 index 8079841c4062..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/EndpointSupplier.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { EndpointMetadata } from "./EndpointMetadata.js"; -import type { Supplier } from "./Supplier.js"; - -type EndpointSupplierFn = (arg: { endpointMetadata: EndpointMetadata }) => T | Promise; -export type EndpointSupplier = Supplier | EndpointSupplierFn; -export const EndpointSupplier = { - get: async (supplier: EndpointSupplier, arg: { endpointMetadata: EndpointMetadata }): Promise => { - if (typeof supplier === "function") { - return (supplier as EndpointSupplierFn)(arg); - } else { - return supplier; - } - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Fetcher.ts deleted file mode 100644 index 58bb0e3ef7d9..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Fetcher.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { toJson } from "../json.js"; -import { createLogger, type LogConfig, type Logger } from "../logging/logger.js"; -import type { APIResponse } from "./APIResponse.js"; -import { createRequestUrl } from "./createRequestUrl.js"; -import type { EndpointMetadata } from "./EndpointMetadata.js"; -import { EndpointSupplier } from "./EndpointSupplier.js"; -import { getErrorResponseBody } from "./getErrorResponseBody.js"; -import { getFetchFn } from "./getFetchFn.js"; -import { getRequestBody } from "./getRequestBody.js"; -import { getResponseBody } from "./getResponseBody.js"; -import { Headers } from "./Headers.js"; -import { makeRequest } from "./makeRequest.js"; -import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -import { requestWithRetries } from "./requestWithRetries.js"; - -export type FetchFunction = (args: Fetcher.Args) => Promise>; - -export declare namespace Fetcher { - export interface Args { - url: string; - method: string; - contentType?: string; - headers?: Record | null | undefined>; - queryParameters?: Record; - body?: unknown; - timeoutMs?: number; - maxRetries?: number; - withCredentials?: boolean; - abortSignal?: AbortSignal; - requestType?: "json" | "file" | "bytes" | "form" | "other"; - responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer" | "binary-response"; - duplex?: "half"; - endpointMetadata?: EndpointMetadata; - fetchFn?: typeof fetch; - logging?: LogConfig | Logger; - } - - export type Error = FailedStatusCodeError | NonJsonError | BodyIsNullError | TimeoutError | UnknownError; - - export interface FailedStatusCodeError { - reason: "status-code"; - statusCode: number; - body: unknown; - } - - export interface NonJsonError { - reason: "non-json"; - statusCode: number; - rawBody: string; - } - - export interface BodyIsNullError { - reason: "body-is-null"; - statusCode: number; - } - - export interface TimeoutError { - reason: "timeout"; - } - - export interface UnknownError { - reason: "unknown"; - errorMessage: string; - } -} - -const SENSITIVE_HEADERS = new Set([ - "authorization", - "www-authenticate", - "x-api-key", - "api-key", - "apikey", - "x-api-token", - "x-auth-token", - "auth-token", - "cookie", - "set-cookie", - "proxy-authorization", - "proxy-authenticate", - "x-csrf-token", - "x-xsrf-token", - "x-session-token", - "x-access-token", -]); - -function redactHeaders(headers: Headers | Record): Record { - const filtered: Record = {}; - for (const [key, value] of headers instanceof Headers ? headers.entries() : Object.entries(headers)) { - if (SENSITIVE_HEADERS.has(key.toLowerCase())) { - filtered[key] = "[REDACTED]"; - } else { - filtered[key] = value; - } - } - return filtered; -} - -const SENSITIVE_QUERY_PARAMS = new Set([ - "api_key", - "api-key", - "apikey", - "token", - "access_token", - "access-token", - "auth_token", - "auth-token", - "password", - "passwd", - "secret", - "api_secret", - "api-secret", - "apisecret", - "key", - "session", - "session_id", - "session-id", -]); - -function redactQueryParameters(queryParameters?: Record): Record | undefined { - if (queryParameters == null) { - return queryParameters; - } - const redacted: Record = {}; - for (const [key, value] of Object.entries(queryParameters)) { - if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) { - redacted[key] = "[REDACTED]"; - } else { - redacted[key] = value; - } - } - return redacted; -} - -function redactUrl(url: string): string { - const protocolIndex = url.indexOf("://"); - if (protocolIndex === -1) return url; - - const afterProtocol = protocolIndex + 3; - - // Find the first delimiter that marks the end of the authority section - const pathStart = url.indexOf("/", afterProtocol); - let queryStart = url.indexOf("?", afterProtocol); - let fragmentStart = url.indexOf("#", afterProtocol); - - const firstDelimiter = Math.min( - pathStart === -1 ? url.length : pathStart, - queryStart === -1 ? url.length : queryStart, - fragmentStart === -1 ? url.length : fragmentStart, - ); - - // Find the LAST @ before the delimiter (handles multiple @ in credentials) - let atIndex = -1; - for (let i = afterProtocol; i < firstDelimiter; i++) { - if (url[i] === "@") { - atIndex = i; - } - } - - if (atIndex !== -1) { - url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`; - } - - // Recalculate queryStart since url might have changed - queryStart = url.indexOf("?"); - if (queryStart === -1) return url; - - fragmentStart = url.indexOf("#", queryStart); - const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length; - const queryString = url.slice(queryStart + 1, queryEnd); - - if (queryString.length === 0) return url; - - // FAST PATH: Quick check if any sensitive keywords present - // Using indexOf is faster than regex for simple substring matching - const lower = queryString.toLowerCase(); - const hasSensitive = - lower.includes("token") || - lower.includes("key") || - lower.includes("password") || - lower.includes("passwd") || - lower.includes("secret") || - lower.includes("session") || - lower.includes("auth"); - - if (!hasSensitive) { - return url; - } - - // SLOW PATH: Parse and redact - const redactedParams: string[] = []; - const params = queryString.split("&"); - - for (const param of params) { - const equalIndex = param.indexOf("="); - if (equalIndex === -1) { - redactedParams.push(param); - continue; - } - - const key = param.slice(0, equalIndex); - let shouldRedact = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()); - - if (!shouldRedact && key.includes("%")) { - try { - const decodedKey = decodeURIComponent(key); - shouldRedact = SENSITIVE_QUERY_PARAMS.has(decodedKey.toLowerCase()); - } catch {} - } - - redactedParams.push(shouldRedact ? `${key}=[REDACTED]` : param); - } - - return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd); -} - -async function getHeaders(args: Fetcher.Args): Promise { - const newHeaders: Headers = new Headers(); - - newHeaders.set( - "Accept", - args.responseType === "json" ? "application/json" : args.responseType === "text" ? "text/plain" : "*/*", - ); - if (args.body !== undefined && args.contentType != null) { - newHeaders.set("Content-Type", args.contentType); - } - - if (args.headers == null) { - return newHeaders; - } - - for (const [key, value] of Object.entries(args.headers)) { - const result = await EndpointSupplier.get(value, { endpointMetadata: args.endpointMetadata ?? {} }); - if (typeof result === "string") { - newHeaders.set(key, result); - continue; - } - if (result == null) { - continue; - } - newHeaders.set(key, `${result}`); - } - return newHeaders; -} - -export async function fetcherImpl(args: Fetcher.Args): Promise> { - const url = createRequestUrl(args.url, args.queryParameters); - const requestBody: BodyInit | undefined = await getRequestBody({ - body: args.body, - type: args.requestType ?? "other", - }); - const fetchFn = args.fetchFn ?? (await getFetchFn()); - const headers = await getHeaders(args); - const logger = createLogger(args.logging); - - if (logger.isDebug()) { - const metadata = { - method: args.method, - url: redactUrl(url), - headers: redactHeaders(headers), - queryParameters: redactQueryParameters(args.queryParameters), - hasBody: requestBody != null, - }; - logger.debug("Making HTTP request", metadata); - } - - try { - const response = await requestWithRetries( - async () => - makeRequest( - fetchFn, - url, - args.method, - headers, - requestBody, - args.timeoutMs, - args.abortSignal, - args.withCredentials, - args.duplex, - ), - args.maxRetries, - ); - - if (response.status >= 200 && response.status < 400) { - if (logger.isDebug()) { - const metadata = { - method: args.method, - url: redactUrl(url), - statusCode: response.status, - responseHeaders: redactHeaders(response.headers), - }; - logger.debug("HTTP request succeeded", metadata); - } - const body = await getResponseBody(response, args.responseType); - return { - ok: true, - body: body as R, - headers: response.headers, - rawResponse: toRawResponse(response), - }; - } else { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - statusCode: response.status, - responseHeaders: redactHeaders(Object.fromEntries(response.headers.entries())), - }; - logger.error("HTTP request failed with error status", metadata); - } - return { - ok: false, - error: { - reason: "status-code", - statusCode: response.status, - body: await getErrorResponseBody(response), - }, - rawResponse: toRawResponse(response), - }; - } - } catch (error) { - if (args.abortSignal?.aborted) { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - }; - logger.error("HTTP request was aborted", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: "The user aborted a request", - }, - rawResponse: abortRawResponse, - }; - } else if (error instanceof Error && error.name === "AbortError") { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - timeoutMs: args.timeoutMs, - }; - logger.error("HTTP request timed out", metadata); - } - return { - ok: false, - error: { - reason: "timeout", - }, - rawResponse: abortRawResponse, - }; - } else if (error instanceof Error) { - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - errorMessage: error.message, - }; - logger.error("HTTP request failed with error", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: error.message, - }, - rawResponse: unknownRawResponse, - }; - } - - if (logger.isError()) { - const metadata = { - method: args.method, - url: redactUrl(url), - error: toJson(error), - }; - logger.error("HTTP request failed with unknown error", metadata); - } - return { - ok: false, - error: { - reason: "unknown", - errorMessage: toJson(error), - }, - rawResponse: unknownRawResponse, - }; - } -} - -export const fetcher: FetchFunction = fetcherImpl; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Headers.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Headers.ts deleted file mode 100644 index af841aa24f55..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Headers.ts +++ /dev/null @@ -1,93 +0,0 @@ -let Headers: typeof globalThis.Headers; - -if (typeof globalThis.Headers !== "undefined") { - Headers = globalThis.Headers; -} else { - Headers = class Headers implements Headers { - private headers: Map; - - constructor(init?: HeadersInit) { - this.headers = new Map(); - - if (init) { - if (init instanceof Headers) { - init.forEach((value, key) => this.append(key, value)); - } else if (Array.isArray(init)) { - for (const [key, value] of init) { - if (typeof key === "string" && typeof value === "string") { - this.append(key, value); - } else { - throw new TypeError("Each header entry must be a [string, string] tuple"); - } - } - } else { - for (const [key, value] of Object.entries(init)) { - if (typeof value === "string") { - this.append(key, value); - } else { - throw new TypeError("Header values must be strings"); - } - } - } - } - } - - append(name: string, value: string): void { - const key = name.toLowerCase(); - const existing = this.headers.get(key) || []; - this.headers.set(key, [...existing, value]); - } - - delete(name: string): void { - const key = name.toLowerCase(); - this.headers.delete(key); - } - - get(name: string): string | null { - const key = name.toLowerCase(); - const values = this.headers.get(key); - return values ? values.join(", ") : null; - } - - has(name: string): boolean { - const key = name.toLowerCase(); - return this.headers.has(key); - } - - set(name: string, value: string): void { - const key = name.toLowerCase(); - this.headers.set(key, [value]); - } - - forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: unknown): void { - const boundCallback = thisArg ? callbackfn.bind(thisArg) : callbackfn; - this.headers.forEach((values, key) => boundCallback(values.join(", "), key, this)); - } - - getSetCookie(): string[] { - return this.headers.get("set-cookie") || []; - } - - *entries(): HeadersIterator<[string, string]> { - for (const [key, values] of this.headers.entries()) { - yield [key, values.join(", ")]; - } - } - - *keys(): HeadersIterator { - yield* this.headers.keys(); - } - - *values(): HeadersIterator { - for (const values of this.headers.values()) { - yield values.join(", "); - } - } - - [Symbol.iterator](): HeadersIterator<[string, string]> { - return this.entries(); - } - }; -} - -export { Headers }; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/HttpResponsePromise.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/HttpResponsePromise.ts deleted file mode 100644 index 692ca7d795f0..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/HttpResponsePromise.ts +++ /dev/null @@ -1,116 +0,0 @@ -import type { WithRawResponse } from "./RawResponse.js"; - -/** - * A promise that returns the parsed response and lets you retrieve the raw response too. - */ -export class HttpResponsePromise extends Promise { - private innerPromise: Promise>; - private unwrappedPromise: Promise | undefined; - - private constructor(promise: Promise>) { - // Initialize with a no-op to avoid premature parsing - super((resolve) => { - resolve(undefined as unknown as T); - }); - this.innerPromise = promise; - } - - /** - * Creates an `HttpResponsePromise` from a function that returns a promise. - * - * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. - * @param args - Arguments to pass to the function. - * @returns An `HttpResponsePromise` instance. - */ - public static fromFunction Promise>, T>( - fn: F, - ...args: Parameters - ): HttpResponsePromise { - return new HttpResponsePromise(fn(...args)); - } - - /** - * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. - * - * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. - * @returns A function that returns an `HttpResponsePromise` instance. - */ - public static interceptFunction< - F extends (...args: never[]) => Promise>, - T = Awaited>["data"], - >(fn: F): (...args: Parameters) => HttpResponsePromise { - return (...args: Parameters): HttpResponsePromise => { - return HttpResponsePromise.fromPromise(fn(...args)); - }; - } - - /** - * Creates an `HttpResponsePromise` from an existing promise. - * - * @param promise - A promise resolving to a `WithRawResponse` object. - * @returns An `HttpResponsePromise` instance. - */ - public static fromPromise(promise: Promise>): HttpResponsePromise { - return new HttpResponsePromise(promise); - } - - /** - * Creates an `HttpResponsePromise` from an executor function. - * - * @param executor - A function that takes resolve and reject callbacks to create a promise. - * @returns An `HttpResponsePromise` instance. - */ - public static fromExecutor( - executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, - ): HttpResponsePromise { - const promise = new Promise>(executor); - return new HttpResponsePromise(promise); - } - - /** - * Creates an `HttpResponsePromise` from a resolved result. - * - * @param result - A `WithRawResponse` object to resolve immediately. - * @returns An `HttpResponsePromise` instance. - */ - public static fromResult(result: WithRawResponse): HttpResponsePromise { - const promise = Promise.resolve(result); - return new HttpResponsePromise(promise); - } - - private unwrap(): Promise { - if (!this.unwrappedPromise) { - this.unwrappedPromise = this.innerPromise.then(({ data }) => data); - } - return this.unwrappedPromise; - } - - /** @inheritdoc */ - public override then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, - ): Promise { - return this.unwrap().then(onfulfilled, onrejected); - } - - /** @inheritdoc */ - public override catch( - onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, - ): Promise { - return this.unwrap().catch(onrejected); - } - - /** @inheritdoc */ - public override finally(onfinally?: (() => void) | null): Promise { - return this.unwrap().finally(onfinally); - } - - /** - * Retrieves the data and raw response. - * - * @returns A promise resolving to a `WithRawResponse` object. - */ - public async withRawResponse(): Promise> { - return await this.innerPromise; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/RawResponse.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/RawResponse.ts deleted file mode 100644 index 37fb44e2aa99..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/RawResponse.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Headers } from "./Headers.js"; - -/** - * The raw response from the fetch call excluding the body. - */ -export type RawResponse = Omit< - { - [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions - }, - "ok" | "body" | "bodyUsed" ->; // strips out body and bodyUsed - -/** - * A raw response indicating that the request was aborted. - */ -export const abortRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 499, - statusText: "Client Closed Request", - type: "error", - url: "", -} as const; - -/** - * A raw response indicating an unknown error. - */ -export const unknownRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 0, - statusText: "Unknown Error", - type: "error", - url: "", -} as const; - -/** - * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, - * excluding the `body` and `bodyUsed` fields. - * - * @param response - The `RawResponse` object to convert. - * @returns A `RawResponse` object containing the extracted properties of the input response. - */ -export function toRawResponse(response: Response): RawResponse { - return { - headers: response.headers, - redirected: response.redirected, - status: response.status, - statusText: response.statusText, - type: response.type, - url: response.url, - }; -} - -/** - * Creates a `RawResponse` from a standard `Response` object. - */ -export interface WithRawResponse { - readonly data: T; - readonly rawResponse: RawResponse; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Supplier.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Supplier.ts deleted file mode 100644 index 867c931c02f4..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/Supplier.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type Supplier = T | Promise | (() => T | Promise); - -export const Supplier = { - get: async (supplier: Supplier): Promise => { - if (typeof supplier === "function") { - return (supplier as () => T)(); - } else { - return supplier; - } - }, -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/createRequestUrl.ts deleted file mode 100644 index 88e13265e112..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/createRequestUrl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { toQueryString } from "../url/qs.js"; - -export function createRequestUrl(baseUrl: string, queryParameters?: Record): string { - const queryString = toQueryString(queryParameters, { arrayFormat: "repeat" }); - return queryString ? `${baseUrl}?${queryString}` : baseUrl; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getErrorResponseBody.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getErrorResponseBody.ts deleted file mode 100644 index 7cf4e623c2f5..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getErrorResponseBody.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { fromJson } from "../json.js"; -import { getResponseBody } from "./getResponseBody.js"; - -export async function getErrorResponseBody(response: Response): Promise { - let contentType = response.headers.get("Content-Type")?.toLowerCase(); - if (contentType == null || contentType.length === 0) { - return getResponseBody(response); - } - - if (contentType.indexOf(";") !== -1) { - contentType = contentType.split(";")[0]?.trim() ?? ""; - } - switch (contentType) { - case "application/hal+json": - case "application/json": - case "application/ld+json": - case "application/problem+json": - case "application/vnd.api+json": - case "text/json": { - const text = await response.text(); - return text.length > 0 ? fromJson(text) : undefined; - } - default: - if (contentType.startsWith("application/vnd.") && contentType.endsWith("+json")) { - const text = await response.text(); - return text.length > 0 ? fromJson(text) : undefined; - } - - // Fallback to plain text if content type is not recognized - // Even if no body is present, the response will be an empty string - return await response.text(); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getFetchFn.ts deleted file mode 100644 index 9f845b956392..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getFetchFn.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function getFetchFn(): Promise { - return fetch; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getHeader.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getHeader.ts deleted file mode 100644 index 50f922b0e87f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getHeader.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function getHeader(headers: Record, header: string): string | undefined { - for (const [headerKey, headerValue] of Object.entries(headers)) { - if (headerKey.toLowerCase() === header.toLowerCase()) { - return headerValue; - } - } - return undefined; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getRequestBody.ts deleted file mode 100644 index 91d9d81f50e5..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getRequestBody.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { toJson } from "../json.js"; -import { toQueryString } from "../url/qs.js"; - -export declare namespace GetRequestBody { - interface Args { - body: unknown; - type: "json" | "file" | "bytes" | "form" | "other"; - } -} - -export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { - if (type === "form") { - return toQueryString(body, { arrayFormat: "repeat", encode: true }); - } - if (type.includes("json")) { - return toJson(body); - } else { - return body as BodyInit; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getResponseBody.ts deleted file mode 100644 index 708d55728f2b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/getResponseBody.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { fromJson } from "../json.js"; -import { getBinaryResponse } from "./BinaryResponse.js"; - -export async function getResponseBody(response: Response, responseType?: string): Promise { - switch (responseType) { - case "binary-response": - return getBinaryResponse(response); - case "blob": - return await response.blob(); - case "arrayBuffer": - return await response.arrayBuffer(); - case "sse": - if (response.body == null) { - return { - ok: false, - error: { - reason: "body-is-null", - statusCode: response.status, - }, - }; - } - return response.body; - case "streaming": - if (response.body == null) { - return { - ok: false, - error: { - reason: "body-is-null", - statusCode: response.status, - }, - }; - } - - return response.body; - - case "text": - return await response.text(); - } - - // if responseType is "json" or not specified, try to parse as JSON - const text = await response.text(); - if (text.length > 0) { - try { - const responseBody = fromJson(text); - return responseBody; - } catch (_err) { - return { - ok: false, - error: { - reason: "non-json", - statusCode: response.status, - rawBody: text, - }, - }; - } - } - return undefined; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/index.ts deleted file mode 100644 index c3bc6da20f49..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type { APIResponse } from "./APIResponse.js"; -export type { BinaryResponse } from "./BinaryResponse.js"; -export type { EndpointMetadata } from "./EndpointMetadata.js"; -export { EndpointSupplier } from "./EndpointSupplier.js"; -export type { Fetcher, FetchFunction } from "./Fetcher.js"; -export { fetcher } from "./Fetcher.js"; -export { getHeader } from "./getHeader.js"; -export { HttpResponsePromise } from "./HttpResponsePromise.js"; -export type { RawResponse, WithRawResponse } from "./RawResponse.js"; -export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -export { Supplier } from "./Supplier.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/makeRequest.ts deleted file mode 100644 index 921565eb0063..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/makeRequest.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { anySignal, getTimeoutSignal } from "./signals.js"; - -export const makeRequest = async ( - fetchFn: (url: string, init: RequestInit) => Promise, - url: string, - method: string, - headers: Headers | Record, - requestBody: BodyInit | undefined, - timeoutMs?: number, - abortSignal?: AbortSignal, - withCredentials?: boolean, - duplex?: "half", -): Promise => { - const signals: AbortSignal[] = []; - - let timeoutAbortId: ReturnType | undefined; - if (timeoutMs != null) { - const { signal, abortId } = getTimeoutSignal(timeoutMs); - timeoutAbortId = abortId; - signals.push(signal); - } - - if (abortSignal != null) { - signals.push(abortSignal); - } - const newSignals = anySignal(signals); - const response = await fetchFn(url, { - method: method, - headers, - body: requestBody, - signal: newSignals, - credentials: withCredentials ? "include" : undefined, - // @ts-ignore - duplex, - }); - - if (timeoutAbortId != null) { - clearTimeout(timeoutAbortId); - } - - return response; -}; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/requestWithRetries.ts deleted file mode 100644 index 1f689688c4b2..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/requestWithRetries.ts +++ /dev/null @@ -1,64 +0,0 @@ -const INITIAL_RETRY_DELAY = 1000; // in milliseconds -const MAX_RETRY_DELAY = 60000; // in milliseconds -const DEFAULT_MAX_RETRIES = 2; -const JITTER_FACTOR = 0.2; // 20% random jitter - -function addPositiveJitter(delay: number): number { - const jitterMultiplier = 1 + Math.random() * JITTER_FACTOR; - return delay * jitterMultiplier; -} - -function addSymmetricJitter(delay: number): number { - const jitterMultiplier = 1 + (Math.random() - 0.5) * JITTER_FACTOR; - return delay * jitterMultiplier; -} - -function getRetryDelayFromHeaders(response: Response, retryAttempt: number): number { - const retryAfter = response.headers.get("Retry-After"); - if (retryAfter) { - const retryAfterSeconds = parseInt(retryAfter, 10); - if (!Number.isNaN(retryAfterSeconds) && retryAfterSeconds > 0) { - return Math.min(retryAfterSeconds * 1000, MAX_RETRY_DELAY); - } - - const retryAfterDate = new Date(retryAfter); - if (!Number.isNaN(retryAfterDate.getTime())) { - const delay = retryAfterDate.getTime() - Date.now(); - if (delay > 0) { - return Math.min(Math.max(delay, 0), MAX_RETRY_DELAY); - } - } - } - - const rateLimitReset = response.headers.get("X-RateLimit-Reset"); - if (rateLimitReset) { - const resetTime = parseInt(rateLimitReset, 10); - if (!Number.isNaN(resetTime)) { - const delay = resetTime * 1000 - Date.now(); - if (delay > 0) { - return addPositiveJitter(Math.min(delay, MAX_RETRY_DELAY)); - } - } - } - - return addSymmetricJitter(Math.min(INITIAL_RETRY_DELAY * 2 ** retryAttempt, MAX_RETRY_DELAY)); -} - -export async function requestWithRetries( - requestFn: () => Promise, - maxRetries: number = DEFAULT_MAX_RETRIES, -): Promise { - let response: Response = await requestFn(); - - for (let i = 0; i < maxRetries; ++i) { - if ([408, 429].includes(response.status) || response.status >= 500) { - const delay = getRetryDelayFromHeaders(response, i); - - await new Promise((resolve) => setTimeout(resolve, delay)); - response = await requestFn(); - } else { - break; - } - } - return response!; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/signals.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/signals.ts deleted file mode 100644 index 7bd3757ec3a7..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/fetcher/signals.ts +++ /dev/null @@ -1,26 +0,0 @@ -const TIMEOUT = "timeout"; - -export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: ReturnType } { - const controller = new AbortController(); - const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); - return { signal: controller.signal, abortId }; -} - -export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { - const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; - - const controller = new AbortController(); - - for (const signal of signals) { - if (signal.aborted) { - controller.abort((signal as any)?.reason); - break; - } - - signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { - signal: controller.signal, - }); - } - - return controller.signal; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/headers.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/headers.ts deleted file mode 100644 index 78ed8b500c95..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/headers.ts +++ /dev/null @@ -1,35 +0,0 @@ -export function mergeHeaders( - ...headersArray: (Record | null | undefined)[] -): Record { - const result: Record = {}; - - for (const [key, value] of headersArray - .filter((headers) => headers != null) - .flatMap((headers) => Object.entries(headers))) { - const insensitiveKey = key.toLowerCase(); - if (value != null) { - result[insensitiveKey] = value; - } else if (insensitiveKey in result) { - delete result[insensitiveKey]; - } - } - - return result; -} - -export function mergeOnlyDefinedHeaders( - ...headersArray: (Record | null | undefined)[] -): Record { - const result: Record = {}; - - for (const [key, value] of headersArray - .filter((headers) => headers != null) - .flatMap((headers) => Object.entries(headers))) { - const insensitiveKey = key.toLowerCase(); - if (value != null) { - result[insensitiveKey] = value; - } - } - - return result; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/index.ts deleted file mode 100644 index 92290bfadcac..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./auth/index.js"; -export * from "./base64.js"; -export * from "./fetcher/index.js"; -export * as logging from "./logging/index.js"; -export * from "./runtime/index.js"; -export * as url from "./url/index.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/json.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/json.ts deleted file mode 100644 index c052f3249f4f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/json.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Serialize a value to JSON - * @param value A JavaScript value, usually an object or array, to be converted. - * @param replacer A function that transforms the results. - * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. - * @returns JSON string - */ -export const toJson = ( - value: unknown, - replacer?: (this: unknown, key: string, value: unknown) => unknown, - space?: string | number, -): string => { - return JSON.stringify(value, replacer, space); -}; - -/** - * Parse JSON string to object, array, or other type - * @param text A valid JSON string. - * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. - * @returns Parsed object, array, or other type - */ -export function fromJson( - text: string, - reviver?: (this: unknown, key: string, value: unknown) => unknown, -): T { - return JSON.parse(text, reviver); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/exports.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/exports.ts deleted file mode 100644 index 88f6c00db0cf..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/exports.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as logger from "./logger.js"; - -export namespace logging { - /** - * Configuration for logger instances. - */ - export type LogConfig = logger.LogConfig; - export type LogLevel = logger.LogLevel; - export const LogLevel: typeof logger.LogLevel = logger.LogLevel; - export type ILogger = logger.ILogger; - /** - * Console logger implementation that outputs to the console. - */ - export type ConsoleLogger = logger.ConsoleLogger; - /** - * Console logger implementation that outputs to the console. - */ - export const ConsoleLogger: typeof logger.ConsoleLogger = logger.ConsoleLogger; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/index.ts deleted file mode 100644 index d81cc32c40f9..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./logger.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/logger.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/logger.ts deleted file mode 100644 index a3f3673cda93..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/logging/logger.ts +++ /dev/null @@ -1,203 +0,0 @@ -export const LogLevel = { - Debug: "debug", - Info: "info", - Warn: "warn", - Error: "error", -} as const; -export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; -const logLevelMap: Record = { - [LogLevel.Debug]: 1, - [LogLevel.Info]: 2, - [LogLevel.Warn]: 3, - [LogLevel.Error]: 4, -}; - -export interface ILogger { - /** - * Logs a debug message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - debug(message: string, ...args: unknown[]): void; - /** - * Logs an info message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - info(message: string, ...args: unknown[]): void; - /** - * Logs a warning message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - warn(message: string, ...args: unknown[]): void; - /** - * Logs an error message. - * @param message - The message to log - * @param args - Additional arguments to log - */ - error(message: string, ...args: unknown[]): void; -} - -/** - * Configuration for logger initialization. - */ -export interface LogConfig { - /** - * Minimum log level to output. - * @default LogLevel.Info - */ - level?: LogLevel; - /** - * Logger implementation to use. - * @default new ConsoleLogger() - */ - logger?: ILogger; - /** - * Whether logging should be silenced. - * @default true - */ - silent?: boolean; -} - -/** - * Default console-based logger implementation. - */ -export class ConsoleLogger implements ILogger { - debug(message: string, ...args: unknown[]): void { - console.debug(message, ...args); - } - info(message: string, ...args: unknown[]): void { - console.info(message, ...args); - } - warn(message: string, ...args: unknown[]): void { - console.warn(message, ...args); - } - error(message: string, ...args: unknown[]): void { - console.error(message, ...args); - } -} - -/** - * Logger class that provides level-based logging functionality. - */ -export class Logger { - private readonly level: number; - private readonly logger: ILogger; - private readonly silent: boolean; - - /** - * Creates a new logger instance. - * @param config - Logger configuration - */ - constructor(config: Required) { - this.level = logLevelMap[config.level]; - this.logger = config.logger; - this.silent = config.silent; - } - - /** - * Checks if a log level should be output based on configuration. - * @param level - The log level to check - * @returns True if the level should be logged - */ - public shouldLog(level: LogLevel): boolean { - return !this.silent && this.level <= logLevelMap[level]; - } - - /** - * Checks if debug logging is enabled. - * @returns True if debug logs should be output - */ - public isDebug(): boolean { - return this.shouldLog(LogLevel.Debug); - } - - /** - * Logs a debug message if debug logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public debug(message: string, ...args: unknown[]): void { - if (this.isDebug()) { - this.logger.debug(message, ...args); - } - } - - /** - * Checks if info logging is enabled. - * @returns True if info logs should be output - */ - public isInfo(): boolean { - return this.shouldLog(LogLevel.Info); - } - - /** - * Logs an info message if info logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public info(message: string, ...args: unknown[]): void { - if (this.isInfo()) { - this.logger.info(message, ...args); - } - } - - /** - * Checks if warning logging is enabled. - * @returns True if warning logs should be output - */ - public isWarn(): boolean { - return this.shouldLog(LogLevel.Warn); - } - - /** - * Logs a warning message if warning logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public warn(message: string, ...args: unknown[]): void { - if (this.isWarn()) { - this.logger.warn(message, ...args); - } - } - - /** - * Checks if error logging is enabled. - * @returns True if error logs should be output - */ - public isError(): boolean { - return this.shouldLog(LogLevel.Error); - } - - /** - * Logs an error message if error logging is enabled. - * @param message - The message to log - * @param args - Additional arguments to log - */ - public error(message: string, ...args: unknown[]): void { - if (this.isError()) { - this.logger.error(message, ...args); - } - } -} - -export function createLogger(config?: LogConfig | Logger): Logger { - if (config == null) { - return defaultLogger; - } - if (config instanceof Logger) { - return config; - } - config = config ?? {}; - config.level ??= LogLevel.Info; - config.logger ??= new ConsoleLogger(); - config.silent ??= true; - return new Logger(config as Required); -} - -const defaultLogger: Logger = new Logger({ - level: LogLevel.Info, - logger: new ConsoleLogger(), - silent: true, -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/index.ts deleted file mode 100644 index cfab23f9a834..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { RUNTIME } from "./runtime.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/runtime.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/runtime.ts deleted file mode 100644 index 56ebbb87c4d3..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/runtime/runtime.ts +++ /dev/null @@ -1,134 +0,0 @@ -interface DenoGlobal { - version: { - deno: string; - }; -} - -interface BunGlobal { - version: string; -} - -declare const Deno: DenoGlobal | undefined; -declare const Bun: BunGlobal | undefined; -declare const EdgeRuntime: string | undefined; -declare const self: typeof globalThis.self & { - importScripts?: unknown; -}; - -/** - * A constant that indicates which environment and version the SDK is running in. - */ -export const RUNTIME: Runtime = evaluateRuntime(); - -export interface Runtime { - type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; - version?: string; - parsedVersion?: number; -} - -function evaluateRuntime(): Runtime { - /** - * A constant that indicates whether the environment the code is running is a Web Browser. - */ - const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; - if (isBrowser) { - return { - type: "browser", - version: window.navigator.userAgent, - }; - } - - /** - * A constant that indicates whether the environment the code is running is Cloudflare. - * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent - */ - const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; - if (isCloudflare) { - return { - type: "workerd", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Edge Runtime. - * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime - */ - const isEdgeRuntime = typeof EdgeRuntime === "string"; - if (isEdgeRuntime) { - return { - type: "edge-runtime", - }; - } - - /** - * A constant that indicates whether the environment the code is running is a Web Worker. - */ - const isWebWorker = - typeof self === "object" && - typeof self?.importScripts === "function" && - (self.constructor?.name === "DedicatedWorkerGlobalScope" || - self.constructor?.name === "ServiceWorkerGlobalScope" || - self.constructor?.name === "SharedWorkerGlobalScope"); - if (isWebWorker) { - return { - type: "web-worker", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Deno. - * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions - */ - const isDeno = - typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; - if (isDeno) { - return { - type: "deno", - version: Deno.version.deno, - }; - } - - /** - * A constant that indicates whether the environment the code is running is Bun.sh. - */ - const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; - if (isBun) { - return { - type: "bun", - version: Bun.version, - }; - } - - /** - * A constant that indicates whether the environment the code is running is in React-Native. - * This check should come before Node.js detection since React Native may have a process polyfill. - * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js - */ - const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; - if (isReactNative) { - return { - type: "react-native", - }; - } - - /** - * A constant that indicates whether the environment the code is running is Node.JS. - */ - const isNode = - typeof process !== "undefined" && - "version" in process && - !!process.version && - "versions" in process && - !!process.versions?.node; - if (isNode) { - return { - type: "node", - version: process.versions.node, - parsedVersion: Number(process.versions.node.split(".")[0]), - }; - } - - return { - type: "unknown", - }; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/encodePathParam.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/encodePathParam.ts deleted file mode 100644 index 19b901244218..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/encodePathParam.ts +++ /dev/null @@ -1,18 +0,0 @@ -export function encodePathParam(param: unknown): string { - if (param === null) { - return "null"; - } - const typeofParam = typeof param; - switch (typeofParam) { - case "undefined": - return "undefined"; - case "string": - case "number": - case "boolean": - break; - default: - param = String(param); - break; - } - return encodeURIComponent(param as string | number | boolean); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/index.ts deleted file mode 100644 index f2e0fa2d2221..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { encodePathParam } from "./encodePathParam.js"; -export { join } from "./join.js"; -export { toQueryString } from "./qs.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/join.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/join.ts deleted file mode 100644 index 7ca7daef094d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/join.ts +++ /dev/null @@ -1,79 +0,0 @@ -export function join(base: string, ...segments: string[]): string { - if (!base) { - return ""; - } - - if (segments.length === 0) { - return base; - } - - if (base.includes("://")) { - let url: URL; - try { - url = new URL(base); - } catch { - return joinPath(base, ...segments); - } - - const lastSegment = segments[segments.length - 1]; - const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); - - for (const segment of segments) { - const cleanSegment = trimSlashes(segment); - if (cleanSegment) { - url.pathname = joinPathSegments(url.pathname, cleanSegment); - } - } - - if (shouldPreserveTrailingSlash && !url.pathname.endsWith("/")) { - url.pathname += "/"; - } - - return url.toString(); - } - - return joinPath(base, ...segments); -} - -function joinPath(base: string, ...segments: string[]): string { - if (segments.length === 0) { - return base; - } - - let result = base; - - const lastSegment = segments[segments.length - 1]; - const shouldPreserveTrailingSlash = lastSegment?.endsWith("/"); - - for (const segment of segments) { - const cleanSegment = trimSlashes(segment); - if (cleanSegment) { - result = joinPathSegments(result, cleanSegment); - } - } - - if (shouldPreserveTrailingSlash && !result.endsWith("/")) { - result += "/"; - } - - return result; -} - -function joinPathSegments(left: string, right: string): string { - if (left.endsWith("/")) { - return left + right; - } - return `${left}/${right}`; -} - -function trimSlashes(str: string): string { - if (!str) return str; - - let start = 0; - let end = str.length; - - if (str.startsWith("/")) start = 1; - if (str.endsWith("/")) end = str.length - 1; - - return start === 0 && end === str.length ? str : str.slice(start, end); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/qs.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/qs.ts deleted file mode 100644 index 13e89be9d9a6..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/core/url/qs.ts +++ /dev/null @@ -1,74 +0,0 @@ -interface QueryStringOptions { - arrayFormat?: "indices" | "repeat"; - encode?: boolean; -} - -const defaultQsOptions: Required = { - arrayFormat: "indices", - encode: true, -} as const; - -function encodeValue(value: unknown, shouldEncode: boolean): string { - if (value === undefined) { - return ""; - } - if (value === null) { - return ""; - } - const stringValue = String(value); - return shouldEncode ? encodeURIComponent(stringValue) : stringValue; -} - -function stringifyObject(obj: Record, prefix = "", options: Required): string[] { - const parts: string[] = []; - - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}[${key}]` : key; - - if (value === undefined) { - continue; - } - - if (Array.isArray(value)) { - if (value.length === 0) { - continue; - } - for (let i = 0; i < value.length; i++) { - const item = value[i]; - if (item === undefined) { - continue; - } - if (typeof item === "object" && !Array.isArray(item) && item !== null) { - const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; - parts.push(...stringifyObject(item as Record, arrayKey, options)); - } else { - const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; - const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey; - parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`); - } - } - } else if (typeof value === "object" && value !== null) { - if (Object.keys(value as Record).length === 0) { - continue; - } - parts.push(...stringifyObject(value as Record, fullKey, options)); - } else { - const encodedKey = options.encode ? encodeURIComponent(fullKey) : fullKey; - parts.push(`${encodedKey}=${encodeValue(value, options.encode)}`); - } - } - - return parts; -} - -export function toQueryString(obj: unknown, options?: QueryStringOptions): string { - if (obj == null || typeof obj !== "object") { - return ""; - } - - const parts = stringifyObject(obj as Record, "", { - ...defaultQsOptions, - ...options, - }); - return parts.join("&"); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsError.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsError.ts deleted file mode 100644 index 18a09173893a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsError.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as core from "../core/index.js"; -import { toJson } from "../core/json.js"; - -export class SeedOauthClientCredentialsError extends Error { - public readonly statusCode?: number; - public readonly body?: unknown; - public readonly rawResponse?: core.RawResponse; - - constructor({ - message, - statusCode, - body, - rawResponse, - }: { - message?: string; - statusCode?: number; - body?: unknown; - rawResponse?: core.RawResponse; - }) { - super(buildMessage({ message, statusCode, body })); - Object.setPrototypeOf(this, new.target.prototype); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = this.constructor.name; - this.statusCode = statusCode; - this.body = body; - this.rawResponse = rawResponse; - } -} - -function buildMessage({ - message, - statusCode, - body, -}: { - message: string | undefined; - statusCode: number | undefined; - body: unknown | undefined; -}): string { - const lines: string[] = []; - if (message != null) { - lines.push(message); - } - - if (statusCode != null) { - lines.push(`Status code: ${statusCode.toString()}`); - } - - if (body != null) { - lines.push(`Body: ${toJson(body, undefined, 2)}`); - } - - return lines.join("\n"); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsTimeoutError.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsTimeoutError.ts deleted file mode 100644 index 587a4ace7b4d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/SeedOauthClientCredentialsTimeoutError.ts +++ /dev/null @@ -1,13 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -export class SeedOauthClientCredentialsTimeoutError extends Error { - constructor(message: string) { - super(message); - Object.setPrototypeOf(this, new.target.prototype); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = this.constructor.name; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/handleNonStatusCodeError.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/handleNonStatusCodeError.ts deleted file mode 100644 index 8ee3af3010e1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/handleNonStatusCodeError.ts +++ /dev/null @@ -1,37 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type * as core from "../core/index.js"; -import * as errors from "./index.js"; - -export function handleNonStatusCodeError( - error: core.Fetcher.Error, - rawResponse: core.RawResponse, - method: string, - path: string, -): never { - switch (error.reason) { - case "non-json": - throw new errors.SeedOauthClientCredentialsError({ - statusCode: error.statusCode, - body: error.rawBody, - rawResponse: rawResponse, - }); - case "body-is-null": - throw new errors.SeedOauthClientCredentialsError({ - statusCode: error.statusCode, - rawResponse: rawResponse, - }); - case "timeout": - throw new errors.SeedOauthClientCredentialsTimeoutError(`Timeout exceeded when calling ${method} ${path}.`); - case "unknown": - throw new errors.SeedOauthClientCredentialsError({ - message: error.errorMessage, - rawResponse: rawResponse, - }); - default: - throw new errors.SeedOauthClientCredentialsError({ - message: "Unknown error", - rawResponse: rawResponse, - }); - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/index.ts deleted file mode 100644 index d81e7da3c5df..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { SeedOauthClientCredentialsError } from "./SeedOauthClientCredentialsError.js"; -export { SeedOauthClientCredentialsTimeoutError } from "./SeedOauthClientCredentialsTimeoutError.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/exports.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/exports.ts deleted file mode 100644 index 7b70ee14fc02..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/exports.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./core/exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/index.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/index.ts deleted file mode 100644 index 802718cd2dfa..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * as SeedOauthClientCredentials from "./api/index.js"; -export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -export { SeedOauthClientCredentialsClient } from "./Client.js"; -export { SeedOauthClientCredentialsError, SeedOauthClientCredentialsTimeoutError } from "./errors/index.js"; -export * from "./exports.js"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/src/version.ts b/seed/ts-sdk/oauth-client-credentials/token-override/src/version.ts deleted file mode 100644 index b643a3e3ea27..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/src/version.ts +++ /dev/null @@ -1 +0,0 @@ -export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/custom.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/custom.test.ts deleted file mode 100644 index 7f5e031c8396..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/custom.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * This is a custom test file, if you wish to add more tests - * to your SDK. - * Be sure to mark this file in `.fernignore`. - * - * If you include example requests/responses in your fern definition, - * you will have tests automatically generated for you. - */ -describe("test", () => { - it("default", () => { - expect(true).toBe(true); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServer.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServer.ts deleted file mode 100644 index 954872157d52..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServer.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { RequestHandlerOptions } from "msw"; -import type { SetupServer } from "msw/node"; - -import { mockEndpointBuilder } from "./mockEndpointBuilder"; - -export interface MockServerOptions { - baseUrl: string; - server: SetupServer; -} - -export class MockServer { - private readonly server: SetupServer; - public readonly baseUrl: string; - - constructor({ baseUrl, server }: MockServerOptions) { - this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; - this.server = server; - } - - public mockEndpoint(options?: RequestHandlerOptions): ReturnType { - const builder = mockEndpointBuilder({ - once: options?.once ?? true, - onBuild: (handler) => { - this.server.use(handler); - }, - }).baseUrl(this.baseUrl); - return builder; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServerPool.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServerPool.ts deleted file mode 100644 index e1a90f7fb2e3..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/MockServerPool.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { setupServer } from "msw/node"; - -import { fromJson, toJson } from "../../src/core/json"; -import { MockServer } from "./MockServer"; -import { randomBaseUrl } from "./randomBaseUrl"; - -const mswServer = setupServer(); -interface MockServerOptions { - baseUrl?: string; -} - -async function formatHttpRequest(request: Request, id?: string): Promise { - try { - const clone = request.clone(); - const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); - - let body = ""; - try { - const contentType = clone.headers.get("content-type"); - if (contentType?.includes("application/json")) { - body = toJson(fromJson(await clone.text()), undefined, 2); - } else if (clone.body) { - body = await clone.text(); - } - } catch (_e) { - body = "(unable to parse body)"; - } - - const title = id ? `### Request ${id} ###\n` : ""; - const firstLine = `${title}${request.method} ${request.url.toString()} HTTP/1.1`; - - return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; - } catch (e) { - return `Error formatting request: ${e}`; - } -} - -async function formatHttpResponse(response: Response, id?: string): Promise { - try { - const clone = response.clone(); - const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); - - let body = ""; - try { - const contentType = clone.headers.get("content-type"); - if (contentType?.includes("application/json")) { - body = toJson(fromJson(await clone.text()), undefined, 2); - } else if (clone.body) { - body = await clone.text(); - } - } catch (_e) { - body = "(unable to parse body)"; - } - - const title = id ? `### Response for ${id} ###\n` : ""; - const firstLine = `${title}HTTP/1.1 ${response.status} ${response.statusText}`; - - return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; - } catch (e) { - return `Error formatting response: ${e}`; - } -} - -class MockServerPool { - private servers: MockServer[] = []; - - public createServer(options?: Partial): MockServer { - const baseUrl = options?.baseUrl || randomBaseUrl(); - const server = new MockServer({ baseUrl, server: mswServer }); - this.servers.push(server); - return server; - } - - public getServers(): MockServer[] { - return [...this.servers]; - } - - public listen(): void { - const onUnhandledRequest = process.env.LOG_LEVEL === "debug" ? "warn" : "bypass"; - mswServer.listen({ onUnhandledRequest }); - - if (process.env.LOG_LEVEL === "debug") { - mswServer.events.on("request:start", async ({ request, requestId }) => { - const formattedRequest = await formatHttpRequest(request, requestId); - console.debug(`request:start\n${formattedRequest}`); - }); - - mswServer.events.on("request:unhandled", async ({ request, requestId }) => { - const formattedRequest = await formatHttpRequest(request, requestId); - console.debug(`request:unhandled\n${formattedRequest}`); - }); - - mswServer.events.on("response:mocked", async ({ request, response, requestId }) => { - const formattedResponse = await formatHttpResponse(response, requestId); - console.debug(`response:mocked\n${formattedResponse}`); - }); - } - } - - public close(): void { - this.servers = []; - mswServer.close(); - } -} - -export const mockServerPool = new MockServerPool(); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/mockEndpointBuilder.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/mockEndpointBuilder.ts deleted file mode 100644 index 1b0e51079e6b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/mockEndpointBuilder.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { type DefaultBodyType, type HttpHandler, HttpResponse, type HttpResponseResolver, http } from "msw"; - -import { url } from "../../src/core"; -import { toJson } from "../../src/core/json"; -import { withFormUrlEncoded } from "./withFormUrlEncoded"; -import { withHeaders } from "./withHeaders"; -import { withJson } from "./withJson"; - -type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; - -interface MethodStage { - baseUrl(baseUrl: string): MethodStage; - all(path: string): RequestHeadersStage; - get(path: string): RequestHeadersStage; - post(path: string): RequestHeadersStage; - put(path: string): RequestHeadersStage; - delete(path: string): RequestHeadersStage; - patch(path: string): RequestHeadersStage; - options(path: string): RequestHeadersStage; - head(path: string): RequestHeadersStage; -} - -interface RequestHeadersStage extends RequestBodyStage, ResponseStage { - header(name: string, value: string): RequestHeadersStage; - headers(headers: Record): RequestBodyStage; -} - -interface RequestBodyStage extends ResponseStage { - jsonBody(body: unknown): ResponseStage; - formUrlEncodedBody(body: unknown): ResponseStage; -} - -interface ResponseStage { - respondWith(): ResponseStatusStage; -} -interface ResponseStatusStage { - statusCode(statusCode: number): ResponseHeaderStage; -} - -interface ResponseHeaderStage extends ResponseBodyStage, BuildStage { - header(name: string, value: string): ResponseHeaderStage; - headers(headers: Record): ResponseHeaderStage; -} - -interface ResponseBodyStage { - jsonBody(body: unknown): BuildStage; -} - -interface BuildStage { - build(): HttpHandler; -} - -export interface HttpHandlerBuilderOptions { - onBuild?: (handler: HttpHandler) => void; - once?: boolean; -} - -class RequestBuilder implements MethodStage, RequestHeadersStage, RequestBodyStage, ResponseStage { - private method: HttpMethod = "get"; - private _baseUrl: string = ""; - private path: string = "/"; - private readonly predicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[] = []; - private readonly handlerOptions?: HttpHandlerBuilderOptions; - - constructor(options?: HttpHandlerBuilderOptions) { - this.handlerOptions = options; - } - - baseUrl(baseUrl: string): MethodStage { - this._baseUrl = baseUrl; - return this; - } - - all(path: string): RequestHeadersStage { - this.method = "all"; - this.path = path; - return this; - } - - get(path: string): RequestHeadersStage { - this.method = "get"; - this.path = path; - return this; - } - - post(path: string): RequestHeadersStage { - this.method = "post"; - this.path = path; - return this; - } - - put(path: string): RequestHeadersStage { - this.method = "put"; - this.path = path; - return this; - } - - delete(path: string): RequestHeadersStage { - this.method = "delete"; - this.path = path; - return this; - } - - patch(path: string): RequestHeadersStage { - this.method = "patch"; - this.path = path; - return this; - } - - options(path: string): RequestHeadersStage { - this.method = "options"; - this.path = path; - return this; - } - - head(path: string): RequestHeadersStage { - this.method = "head"; - this.path = path; - return this; - } - - header(name: string, value: string): RequestHeadersStage { - this.predicates.push((resolver) => withHeaders({ [name]: value }, resolver)); - return this; - } - - headers(headers: Record): RequestBodyStage { - this.predicates.push((resolver) => withHeaders(headers, resolver)); - return this; - } - - jsonBody(body: unknown): ResponseStage { - if (body === undefined) { - throw new Error("Undefined is not valid JSON. Do not call jsonBody if you want an empty body."); - } - this.predicates.push((resolver) => withJson(body, resolver)); - return this; - } - - formUrlEncodedBody(body: unknown): ResponseStage { - if (body === undefined) { - throw new Error( - "Undefined is not valid for form-urlencoded. Do not call formUrlEncodedBody if you want an empty body.", - ); - } - this.predicates.push((resolver) => withFormUrlEncoded(body, resolver)); - return this; - } - - respondWith(): ResponseStatusStage { - return new ResponseBuilder(this.method, this.buildUrl(), this.predicates, this.handlerOptions); - } - - private buildUrl(): string { - return url.join(this._baseUrl, this.path); - } -} - -class ResponseBuilder implements ResponseStatusStage, ResponseHeaderStage, ResponseBodyStage, BuildStage { - private readonly method: HttpMethod; - private readonly url: string; - private readonly requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[]; - private readonly handlerOptions?: HttpHandlerBuilderOptions; - - private responseStatusCode: number = 200; - private responseHeaders: Record = {}; - private responseBody: DefaultBodyType = undefined; - - constructor( - method: HttpMethod, - url: string, - requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[], - options?: HttpHandlerBuilderOptions, - ) { - this.method = method; - this.url = url; - this.requestPredicates = requestPredicates; - this.handlerOptions = options; - } - - public statusCode(code: number): ResponseHeaderStage { - this.responseStatusCode = code; - return this; - } - - public header(name: string, value: string): ResponseHeaderStage { - this.responseHeaders[name] = value; - return this; - } - - public headers(headers: Record): ResponseHeaderStage { - this.responseHeaders = { ...this.responseHeaders, ...headers }; - return this; - } - - public jsonBody(body: unknown): BuildStage { - if (body === undefined) { - throw new Error("Undefined is not valid JSON. Do not call jsonBody if you expect an empty body."); - } - this.responseBody = toJson(body); - return this; - } - - public build(): HttpHandler { - const responseResolver: HttpResponseResolver = () => { - const response = new HttpResponse(this.responseBody, { - status: this.responseStatusCode, - headers: this.responseHeaders, - }); - // if no Content-Type header is set, delete the default text content type that is set - if (Object.keys(this.responseHeaders).some((key) => key.toLowerCase() === "content-type") === false) { - response.headers.delete("Content-Type"); - } - return response; - }; - - const finalResolver = this.requestPredicates.reduceRight((acc, predicate) => predicate(acc), responseResolver); - - const handler = http[this.method](this.url, finalResolver, this.handlerOptions); - this.handlerOptions?.onBuild?.(handler); - return handler; - } -} - -export function mockEndpointBuilder(options?: HttpHandlerBuilderOptions): MethodStage { - return new RequestBuilder(options); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/randomBaseUrl.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/randomBaseUrl.ts deleted file mode 100644 index 031aa6408aca..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/randomBaseUrl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function randomBaseUrl(): string { - const randomString = Math.random().toString(36).substring(2, 15); - return `http://${randomString}.localhost`; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/setup.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/setup.ts deleted file mode 100644 index aeb3a95af7dc..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/setup.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { afterAll, beforeAll } from "vitest"; - -import { mockServerPool } from "./MockServerPool"; - -beforeAll(() => { - mockServerPool.listen(); -}); -afterAll(() => { - mockServerPool.close(); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withFormUrlEncoded.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withFormUrlEncoded.ts deleted file mode 100644 index e9e6ff2d9cf1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withFormUrlEncoded.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -import { toJson } from "../../src/core/json"; - -/** - * Creates a request matcher that validates if the request form-urlencoded body exactly matches the expected object - * @param expectedBody - The exact body object to match against - * @param resolver - Response resolver to execute if body matches - */ -export function withFormUrlEncoded(expectedBody: unknown, resolver: HttpResponseResolver): HttpResponseResolver { - return async (args) => { - const { request } = args; - - let clonedRequest: Request; - let bodyText: string | undefined; - let actualBody: Record; - try { - clonedRequest = request.clone(); - bodyText = await clonedRequest.text(); - if (bodyText === "") { - console.error("Request body is empty, expected a form-urlencoded body."); - return passthrough(); - } - const params = new URLSearchParams(bodyText); - actualBody = {}; - for (const [key, value] of params.entries()) { - actualBody[key] = value; - } - } catch (error) { - console.error(`Error processing form-urlencoded request body:\n\tError: ${error}\n\tBody: ${bodyText}`); - return passthrough(); - } - - const mismatches = findMismatches(actualBody, expectedBody); - if (Object.keys(mismatches).length > 0) { - console.error("Form-urlencoded body mismatch:", toJson(mismatches, undefined, 2)); - return passthrough(); - } - - return resolver(args); - }; -} - -function findMismatches(actual: any, expected: any): Record { - const mismatches: Record = {}; - - if (typeof actual !== typeof expected) { - return { value: { actual, expected } }; - } - - if (typeof actual !== "object" || actual === null || expected === null) { - if (actual !== expected) { - return { value: { actual, expected } }; - } - return {}; - } - - const actualKeys = Object.keys(actual); - const expectedKeys = Object.keys(expected); - - const allKeys = new Set([...actualKeys, ...expectedKeys]); - - for (const key of allKeys) { - if (!expectedKeys.includes(key)) { - if (actual[key] === undefined) { - continue; - } - mismatches[key] = { actual: actual[key], expected: undefined }; - } else if (!actualKeys.includes(key)) { - if (expected[key] === undefined) { - continue; - } - mismatches[key] = { actual: undefined, expected: expected[key] }; - } else if (actual[key] !== expected[key]) { - mismatches[key] = { actual: actual[key], expected: expected[key] }; - } - } - - return mismatches; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withHeaders.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withHeaders.ts deleted file mode 100644 index 6599d2b4a92d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withHeaders.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -/** - * Creates a request matcher that validates if request headers match specified criteria - * @param expectedHeaders - Headers to match against - * @param resolver - Response resolver to execute if headers match - */ -export function withHeaders( - expectedHeaders: Record boolean)>, - resolver: HttpResponseResolver, -): HttpResponseResolver { - return (args) => { - const { request } = args; - const { headers } = request; - - const mismatches: Record< - string, - { actual: string | null; expected: string | RegExp | ((value: string) => boolean) } - > = {}; - - for (const [key, expectedValue] of Object.entries(expectedHeaders)) { - const actualValue = headers.get(key); - - if (actualValue === null) { - mismatches[key] = { actual: null, expected: expectedValue }; - continue; - } - - if (typeof expectedValue === "function") { - if (!expectedValue(actualValue)) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } else if (expectedValue instanceof RegExp) { - if (!expectedValue.test(actualValue)) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } else if (expectedValue !== actualValue) { - mismatches[key] = { actual: actualValue, expected: expectedValue }; - } - } - - if (Object.keys(mismatches).length > 0) { - const formattedMismatches = formatHeaderMismatches(mismatches); - console.error("Header mismatch:", formattedMismatches); - return passthrough(); - } - - return resolver(args); - }; -} - -function formatHeaderMismatches( - mismatches: Record boolean) }>, -): Record { - const formatted: Record = {}; - - for (const [key, { actual, expected }] of Object.entries(mismatches)) { - formatted[key] = { - actual, - expected: - expected instanceof RegExp - ? expected.toString() - : typeof expected === "function" - ? "[Function]" - : expected, - }; - } - - return formatted; -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withJson.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withJson.ts deleted file mode 100644 index b627638b015f..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/mock-server/withJson.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { type HttpResponseResolver, passthrough } from "msw"; - -import { fromJson, toJson } from "../../src/core/json"; - -/** - * Creates a request matcher that validates if the request JSON body exactly matches the expected object - * @param expectedBody - The exact body object to match against - * @param resolver - Response resolver to execute if body matches - */ -export function withJson(expectedBody: unknown, resolver: HttpResponseResolver): HttpResponseResolver { - return async (args) => { - const { request } = args; - - let clonedRequest: Request; - let bodyText: string | undefined; - let actualBody: unknown; - try { - clonedRequest = request.clone(); - bodyText = await clonedRequest.text(); - if (bodyText === "") { - console.error("Request body is empty, expected a JSON object."); - return passthrough(); - } - actualBody = fromJson(bodyText); - } catch (error) { - console.error(`Error processing request body:\n\tError: ${error}\n\tBody: ${bodyText}`); - return passthrough(); - } - - const mismatches = findMismatches(actualBody, expectedBody); - if (Object.keys(mismatches).filter((key) => !key.startsWith("pagination.")).length > 0) { - console.error("JSON body mismatch:", toJson(mismatches, undefined, 2)); - return passthrough(); - } - - return resolver(args); - }; -} - -function findMismatches(actual: any, expected: any): Record { - const mismatches: Record = {}; - - if (typeof actual !== typeof expected) { - if (areEquivalent(actual, expected)) { - return {}; - } - return { value: { actual, expected } }; - } - - if (typeof actual !== "object" || actual === null || expected === null) { - if (actual !== expected) { - if (areEquivalent(actual, expected)) { - return {}; - } - return { value: { actual, expected } }; - } - return {}; - } - - if (Array.isArray(actual) && Array.isArray(expected)) { - if (actual.length !== expected.length) { - return { length: { actual: actual.length, expected: expected.length } }; - } - - const arrayMismatches: Record = {}; - for (let i = 0; i < actual.length; i++) { - const itemMismatches = findMismatches(actual[i], expected[i]); - if (Object.keys(itemMismatches).length > 0) { - for (const [mismatchKey, mismatchValue] of Object.entries(itemMismatches)) { - arrayMismatches[`[${i}]${mismatchKey === "value" ? "" : `.${mismatchKey}`}`] = mismatchValue; - } - } - } - return arrayMismatches; - } - - const actualKeys = Object.keys(actual); - const expectedKeys = Object.keys(expected); - - const allKeys = new Set([...actualKeys, ...expectedKeys]); - - for (const key of allKeys) { - if (!expectedKeys.includes(key)) { - if (actual[key] === undefined) { - continue; // Skip undefined values in actual - } - mismatches[key] = { actual: actual[key], expected: undefined }; - } else if (!actualKeys.includes(key)) { - if (expected[key] === undefined) { - continue; // Skip undefined values in expected - } - mismatches[key] = { actual: undefined, expected: expected[key] }; - } else if ( - typeof actual[key] === "object" && - actual[key] !== null && - typeof expected[key] === "object" && - expected[key] !== null - ) { - const nestedMismatches = findMismatches(actual[key], expected[key]); - if (Object.keys(nestedMismatches).length > 0) { - for (const [nestedKey, nestedValue] of Object.entries(nestedMismatches)) { - mismatches[`${key}${nestedKey === "value" ? "" : `.${nestedKey}`}`] = nestedValue; - } - } - } else if (actual[key] !== expected[key]) { - if (areEquivalent(actual[key], expected[key])) { - continue; - } - mismatches[key] = { actual: actual[key], expected: expected[key] }; - } - } - - return mismatches; -} - -function areEquivalent(actual: unknown, expected: unknown): boolean { - if (actual === expected) { - return true; - } - if (isEquivalentBigInt(actual, expected)) { - return true; - } - if (isEquivalentDatetime(actual, expected)) { - return true; - } - return false; -} - -function isEquivalentBigInt(actual: unknown, expected: unknown) { - if (typeof actual === "number") { - actual = BigInt(actual); - } - if (typeof expected === "number") { - expected = BigInt(expected); - } - if (typeof actual === "bigint" && typeof expected === "bigint") { - return actual === expected; - } - return false; -} - -function isEquivalentDatetime(str1: unknown, str2: unknown): boolean { - if (typeof str1 !== "string" || typeof str2 !== "string") { - return false; - } - const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/; - if (!isoDatePattern.test(str1) || !isoDatePattern.test(str2)) { - return false; - } - - try { - const date1 = new Date(str1).getTime(); - const date2 = new Date(str2).getTime(); - return date1 === date2; - } catch { - return false; - } -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/setup.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/setup.ts deleted file mode 100644 index a5651f81ba10..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/setup.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { expect } from "vitest"; - -interface CustomMatchers { - toContainHeaders(expectedHeaders: Record): R; -} - -declare module "vitest" { - interface Assertion extends CustomMatchers {} - interface AsymmetricMatchersContaining extends CustomMatchers {} -} - -expect.extend({ - toContainHeaders(actual: unknown, expectedHeaders: Record) { - const isHeaders = actual instanceof Headers; - const isPlainObject = typeof actual === "object" && actual !== null && !Array.isArray(actual); - - if (!isHeaders && !isPlainObject) { - throw new TypeError("Received value must be an instance of Headers or a plain object!"); - } - - if (typeof expectedHeaders !== "object" || expectedHeaders === null || Array.isArray(expectedHeaders)) { - throw new TypeError("Expected headers must be a plain object!"); - } - - const missingHeaders: string[] = []; - const mismatchedHeaders: Array<{ key: string; expected: string; actual: string | null }> = []; - - for (const [key, value] of Object.entries(expectedHeaders)) { - let actualValue: string | null = null; - - if (isHeaders) { - // Headers.get() is already case-insensitive - actualValue = (actual as Headers).get(key); - } else { - // For plain objects, do case-insensitive lookup - const actualObj = actual as Record; - const lowerKey = key.toLowerCase(); - const foundKey = Object.keys(actualObj).find((k) => k.toLowerCase() === lowerKey); - actualValue = foundKey ? actualObj[foundKey] : null; - } - - if (actualValue === null || actualValue === undefined) { - missingHeaders.push(key); - } else if (actualValue !== value) { - mismatchedHeaders.push({ key, expected: value, actual: actualValue }); - } - } - - const pass = missingHeaders.length === 0 && mismatchedHeaders.length === 0; - - const actualType = isHeaders ? "Headers" : "object"; - - if (pass) { - return { - message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, - pass: true, - }; - } else { - const messages: string[] = []; - - if (missingHeaders.length > 0) { - messages.push(`Missing headers: ${this.utils.printExpected(missingHeaders.join(", "))}`); - } - - if (mismatchedHeaders.length > 0) { - const mismatches = mismatchedHeaders.map( - ({ key, expected, actual }) => - `${key}: expected ${this.utils.printExpected(expected)} but got ${this.utils.printReceived(actual)}`, - ); - messages.push(mismatches.join("\n")); - } - - return { - message: () => - `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, - pass: false, - }; - } - }, -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/tsconfig.json b/seed/ts-sdk/oauth-client-credentials/token-override/tests/tsconfig.json deleted file mode 100644 index a477df47920c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": null, - "rootDir": "..", - "baseUrl": "..", - "types": ["vitest/globals"] - }, - "include": ["../src", "../tests"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BasicAuth.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BasicAuth.test.ts deleted file mode 100644 index 9b5123364c47..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BasicAuth.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BasicAuth } from "../../../src/core/auth/BasicAuth"; - -describe("BasicAuth", () => { - interface ToHeaderTestCase { - description: string; - input: { username: string; password: string }; - expected: string; - } - - interface FromHeaderTestCase { - description: string; - input: string; - expected: { username: string; password: string }; - } - - interface ErrorTestCase { - description: string; - input: string; - expectedError: string; - } - - describe("toAuthorizationHeader", () => { - const toHeaderTests: ToHeaderTestCase[] = [ - { - description: "correctly converts to header", - input: { username: "username", password: "password" }, - expected: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", - }, - ]; - - toHeaderTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(BasicAuth.toAuthorizationHeader(input)).toBe(expected); - }); - }); - }); - - describe("fromAuthorizationHeader", () => { - const fromHeaderTests: FromHeaderTestCase[] = [ - { - description: "correctly parses header", - input: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", - expected: { username: "username", password: "password" }, - }, - { - description: "handles password with colons", - input: "Basic dXNlcjpwYXNzOndvcmQ=", - expected: { username: "user", password: "pass:word" }, - }, - { - description: "handles empty username and password (just colon)", - input: "Basic Og==", - expected: { username: "", password: "" }, - }, - { - description: "handles empty username", - input: "Basic OnBhc3N3b3Jk", - expected: { username: "", password: "password" }, - }, - { - description: "handles empty password", - input: "Basic dXNlcm5hbWU6", - expected: { username: "username", password: "" }, - }, - ]; - - fromHeaderTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(BasicAuth.fromAuthorizationHeader(input)).toEqual(expected); - }); - }); - - const errorTests: ErrorTestCase[] = [ - { - description: "throws error for completely empty credentials", - input: "Basic ", - expectedError: "Invalid basic auth", - }, - { - description: "throws error for credentials without colon", - input: "Basic dXNlcm5hbWU=", - expectedError: "Invalid basic auth", - }, - ]; - - errorTests.forEach(({ description, input, expectedError }) => { - it(description, () => { - expect(() => BasicAuth.fromAuthorizationHeader(input)).toThrow(expectedError); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BearerToken.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BearerToken.test.ts deleted file mode 100644 index 7757b87cb97e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/auth/BearerToken.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BearerToken } from "../../../src/core/auth/BearerToken"; - -describe("BearerToken", () => { - describe("toAuthorizationHeader", () => { - it("correctly converts to header", () => { - expect(BearerToken.toAuthorizationHeader("my-token")).toBe("Bearer my-token"); - }); - }); - describe("fromAuthorizationHeader", () => { - it("correctly parses header", () => { - expect(BearerToken.fromAuthorizationHeader("Bearer my-token")).toBe("my-token"); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/base64.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/base64.test.ts deleted file mode 100644 index 939594ca277b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/base64.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { base64Decode, base64Encode } from "../../src/core/base64"; - -describe("base64", () => { - describe("base64Encode", () => { - it("should encode ASCII strings", () => { - expect(base64Encode("hello")).toBe("aGVsbG8="); - expect(base64Encode("")).toBe(""); - }); - - it("should encode UTF-8 strings", () => { - expect(base64Encode("café")).toBe("Y2Fmw6k="); - expect(base64Encode("🎉")).toBe("8J+OiQ=="); - }); - - it("should handle basic auth credentials", () => { - expect(base64Encode("username:password")).toBe("dXNlcm5hbWU6cGFzc3dvcmQ="); - }); - }); - - describe("base64Decode", () => { - it("should decode ASCII strings", () => { - expect(base64Decode("aGVsbG8=")).toBe("hello"); - expect(base64Decode("")).toBe(""); - }); - - it("should decode UTF-8 strings", () => { - expect(base64Decode("Y2Fmw6k=")).toBe("café"); - expect(base64Decode("8J+OiQ==")).toBe("🎉"); - }); - - it("should handle basic auth credentials", () => { - expect(base64Decode("dXNlcm5hbWU6cGFzc3dvcmQ=")).toBe("username:password"); - }); - }); - - describe("round-trip encoding", () => { - const testStrings = [ - "hello world", - "test@example.com", - "café", - "username:password", - "user@domain.com:super$ecret123!", - ]; - - testStrings.forEach((testString) => { - it(`should round-trip encode/decode: "${testString}"`, () => { - const encoded = base64Encode(testString); - const decoded = base64Decode(encoded); - expect(decoded).toBe(testString); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/Fetcher.test.ts deleted file mode 100644 index 2f98ede81d78..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/Fetcher.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -import fs from "fs"; -import { join } from "path"; -import stream from "stream"; -import type { BinaryResponse } from "../../../src/core"; -import { type Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -describe("Test fetcherImpl", () => { - it("should handle successful request", async () => { - const mockArgs: Fetcher.Args = { - url: "https://httpbin.org/post", - method: "POST", - headers: { "X-Test": "x-test-header" }, - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - maxRetries: 0, - responseType: "json", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - }), - ); - - const result = await fetcherImpl(mockArgs); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.body).toEqual({ data: "test" }); - } - - expect(global.fetch).toHaveBeenCalledWith( - "https://httpbin.org/post", - expect.objectContaining({ - method: "POST", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - body: JSON.stringify({ data: "test" }), - }), - ); - }); - - it("should send octet stream", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "POST", - headers: { "X-Test": "x-test-header" }, - contentType: "application/octet-stream", - requestType: "bytes", - maxRetries: 0, - responseType: "json", - body: fs.createReadStream(join(__dirname, "test-file.txt")), - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - }), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "POST", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - body: expect.any(fs.ReadStream), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.body).toEqual({ data: "test" }); - } - }); - - it("should receive file as stream", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.stream).toBe("function"); - const stream = body.stream(); - expect(stream).toBeInstanceOf(ReadableStream); - const readableStream = stream as ReadableStream; - const reader = readableStream.getReader(); - const { value } = await reader.read(); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as blob", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.blob).toBe("function"); - const blob = await body.blob(); - expect(blob).toBeInstanceOf(Blob); - const reader = blob.stream().getReader(); - const { value } = await reader.read(); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as arraybuffer", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.arrayBuffer).toBe("function"); - const arrayBuffer = await body.arrayBuffer(); - expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(new Uint8Array(arrayBuffer)); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); - - it("should receive file as bytes", async () => { - const url = "https://httpbin.org/post/file"; - const mockArgs: Fetcher.Args = { - url, - method: "GET", - headers: { "X-Test": "x-test-header" }, - maxRetries: 0, - responseType: "binary-response", - }; - - global.fetch = vi.fn().mockResolvedValue( - new Response( - stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, - { - status: 200, - statusText: "OK", - }, - ), - ); - - const result = await fetcherImpl(mockArgs); - - expect(global.fetch).toHaveBeenCalledWith( - url, - expect.objectContaining({ - method: "GET", - headers: expect.toContainHeaders({ "X-Test": "x-test-header" }), - }), - ); - expect(result.ok).toBe(true); - if (result.ok) { - const body = result.body as BinaryResponse; - expect(body).toBeDefined(); - expect(body.bodyUsed).toBe(false); - expect(typeof body.bytes).toBe("function"); - if (!body.bytes) { - return; - } - const bytes = await body.bytes(); - expect(bytes).toBeInstanceOf(Uint8Array); - const decoder = new TextDecoder(); - const streamContent = decoder.decode(bytes); - expect(streamContent).toBe("This is a test file!\n"); - expect(body.bodyUsed).toBe(true); - } - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts deleted file mode 100644 index 2ec008e581d8..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/HttpResponsePromise.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -import { HttpResponsePromise } from "../../../src/core/fetcher/HttpResponsePromise"; -import type { RawResponse, WithRawResponse } from "../../../src/core/fetcher/RawResponse"; - -describe("HttpResponsePromise", () => { - const mockRawResponse: RawResponse = { - headers: new Headers(), - redirected: false, - status: 200, - statusText: "OK", - type: "basic" as ResponseType, - url: "https://example.com", - }; - const mockData = { id: "123", name: "test" }; - const mockWithRawResponse: WithRawResponse = { - data: mockData, - rawResponse: mockRawResponse, - }; - - describe("fromFunction", () => { - it("should create an HttpResponsePromise from a function", async () => { - const mockFn = vi - .fn<(arg1: string, arg2: string) => Promise>>() - .mockResolvedValue(mockWithRawResponse); - - const responsePromise = HttpResponsePromise.fromFunction(mockFn, "arg1", "arg2"); - - const result = await responsePromise; - expect(result).toEqual(mockData); - expect(mockFn).toHaveBeenCalledWith("arg1", "arg2"); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromPromise", () => { - it("should create an HttpResponsePromise from a promise", async () => { - const promise = Promise.resolve(mockWithRawResponse); - - const responsePromise = HttpResponsePromise.fromPromise(promise); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromExecutor", () => { - it("should create an HttpResponsePromise from an executor function", async () => { - const responsePromise = HttpResponsePromise.fromExecutor((resolve) => { - resolve(mockWithRawResponse); - }); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("fromResult", () => { - it("should create an HttpResponsePromise from a result", async () => { - const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - - const result = await responsePromise; - expect(result).toEqual(mockData); - - const resultWithRawResponse = await responsePromise.withRawResponse(); - expect(resultWithRawResponse).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); - - describe("Promise methods", () => { - let responsePromise: HttpResponsePromise; - - beforeEach(() => { - responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - }); - - it("should support then() method", async () => { - const result = await responsePromise.then((data) => ({ - ...data, - modified: true, - })); - - expect(result).toEqual({ - ...mockData, - modified: true, - }); - }); - - it("should support catch() method", async () => { - const errorResponsePromise = HttpResponsePromise.fromExecutor((_, reject) => { - reject(new Error("Test error")); - }); - - const catchSpy = vi.fn(); - await errorResponsePromise.catch(catchSpy); - - expect(catchSpy).toHaveBeenCalled(); - const error = catchSpy.mock.calls[0]?.[0]; - expect(error).toBeInstanceOf(Error); - expect((error as Error).message).toBe("Test error"); - }); - - it("should support finally() method", async () => { - const finallySpy = vi.fn(); - await responsePromise.finally(finallySpy); - - expect(finallySpy).toHaveBeenCalled(); - }); - }); - - describe("withRawResponse", () => { - it("should return both data and raw response", async () => { - const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); - - const result = await responsePromise.withRawResponse(); - - expect(result).toEqual({ - data: mockData, - rawResponse: mockRawResponse, - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/RawResponse.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/RawResponse.test.ts deleted file mode 100644 index 375ee3f38064..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/RawResponse.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { toRawResponse } from "../../../src/core/fetcher/RawResponse"; - -describe("RawResponse", () => { - describe("toRawResponse", () => { - it("should convert Response to RawResponse by removing body, bodyUsed, and ok properties", () => { - const mockHeaders = new Headers({ "content-type": "application/json" }); - const mockResponse = { - body: "test body", - bodyUsed: false, - ok: true, - headers: mockHeaders, - redirected: false, - status: 200, - statusText: "OK", - type: "basic" as ResponseType, - url: "https://example.com", - }; - - const result = toRawResponse(mockResponse as unknown as Response); - - expect("body" in result).toBe(false); - expect("bodyUsed" in result).toBe(false); - expect("ok" in result).toBe(false); - expect(result.headers).toBe(mockHeaders); - expect(result.redirected).toBe(false); - expect(result.status).toBe(200); - expect(result.statusText).toBe("OK"); - expect(result.type).toBe("basic"); - expect(result.url).toBe("https://example.com"); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/createRequestUrl.test.ts deleted file mode 100644 index a92f1b5e81d1..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/createRequestUrl.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; - -describe("Test createRequestUrl", () => { - const BASE_URL = "https://api.example.com"; - - interface TestCase { - description: string; - baseUrl: string; - queryParams?: Record; - expected: string; - } - - const testCases: TestCase[] = [ - { - description: "should return the base URL when no query parameters are provided", - baseUrl: BASE_URL, - expected: BASE_URL, - }, - { - description: "should append simple query parameters", - baseUrl: BASE_URL, - queryParams: { key: "value", another: "param" }, - expected: "https://api.example.com?key=value&another=param", - }, - { - description: "should handle array query parameters", - baseUrl: BASE_URL, - queryParams: { items: ["a", "b", "c"] }, - expected: "https://api.example.com?items=a&items=b&items=c", - }, - { - description: "should handle object query parameters", - baseUrl: BASE_URL, - queryParams: { filter: { name: "John", age: 30 } }, - expected: "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", - }, - { - description: "should handle mixed types of query parameters", - baseUrl: BASE_URL, - queryParams: { - simple: "value", - array: ["x", "y"], - object: { key: "value" }, - }, - expected: "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", - }, - { - description: "should handle empty query parameters object", - baseUrl: BASE_URL, - queryParams: {}, - expected: BASE_URL, - }, - { - description: "should encode special characters in query parameters", - baseUrl: BASE_URL, - queryParams: { special: "a&b=c d" }, - expected: "https://api.example.com?special=a%26b%3Dc%20d", - }, - { - description: "should handle numeric values", - baseUrl: BASE_URL, - queryParams: { count: 42, price: 19.99, active: 1, inactive: 0 }, - expected: "https://api.example.com?count=42&price=19.99&active=1&inactive=0", - }, - { - description: "should handle boolean values", - baseUrl: BASE_URL, - queryParams: { enabled: true, disabled: false }, - expected: "https://api.example.com?enabled=true&disabled=false", - }, - { - description: "should handle null and undefined values", - baseUrl: BASE_URL, - queryParams: { - valid: "value", - nullValue: null, - undefinedValue: undefined, - emptyString: "", - }, - expected: "https://api.example.com?valid=value&nullValue=&emptyString=", - }, - { - description: "should handle deeply nested objects", - baseUrl: BASE_URL, - queryParams: { - user: { - profile: { - name: "John", - settings: { theme: "dark" }, - }, - }, - }, - expected: - "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - }, - { - description: "should handle arrays of objects", - baseUrl: BASE_URL, - queryParams: { - users: [ - { name: "John", age: 30 }, - { name: "Jane", age: 25 }, - ], - }, - expected: - "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", - }, - { - description: "should handle mixed arrays", - baseUrl: BASE_URL, - queryParams: { - mixed: ["string", 42, true, { key: "value" }], - }, - expected: "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", - }, - { - description: "should handle empty arrays", - baseUrl: BASE_URL, - queryParams: { emptyArray: [] }, - expected: BASE_URL, - }, - { - description: "should handle empty objects", - baseUrl: BASE_URL, - queryParams: { emptyObject: {} }, - expected: BASE_URL, - }, - { - description: "should handle special characters in keys", - baseUrl: BASE_URL, - queryParams: { "key with spaces": "value", "key[with]brackets": "value" }, - expected: "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", - }, - { - description: "should handle URL with existing query parameters", - baseUrl: "https://api.example.com?existing=param", - queryParams: { new: "value" }, - expected: "https://api.example.com?existing=param?new=value", - }, - { - description: "should handle complex nested structures", - baseUrl: BASE_URL, - queryParams: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - expected: - "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - ]; - - testCases.forEach(({ description, baseUrl, queryParams, expected }) => { - it(description, () => { - expect(createRequestUrl(baseUrl, queryParams)).toBe(expected); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getRequestBody.test.ts deleted file mode 100644 index 8a6c3a57e211..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getRequestBody.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; -import { RUNTIME } from "../../../src/core/runtime"; - -describe("Test getRequestBody", () => { - interface TestCase { - description: string; - input: any; - type: "json" | "form" | "file" | "bytes" | "other"; - expected: any; - skipCondition?: () => boolean; - } - - const testCases: TestCase[] = [ - { - description: "should stringify body if not FormData in Node environment", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - skipCondition: () => RUNTIME.type !== "node", - }, - { - description: "should stringify body if not FormData in browser environment", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - skipCondition: () => RUNTIME.type !== "browser", - }, - { - description: "should return the Uint8Array", - input: new Uint8Array([1, 2, 3]), - type: "bytes", - expected: new Uint8Array([1, 2, 3]), - }, - { - description: "should serialize objects for form-urlencoded content type", - input: { username: "johndoe", email: "john@example.com" }, - type: "form", - expected: "username=johndoe&email=john%40example.com", - }, - { - description: "should serialize complex nested objects and arrays for form-urlencoded content type", - input: { - user: { - profile: { - name: "John Doe", - settings: { - theme: "dark", - notifications: true, - }, - }, - tags: ["admin", "user"], - contacts: [ - { type: "email", value: "john@example.com" }, - { type: "phone", value: "+1234567890" }, - ], - }, - filters: { - status: ["active", "pending"], - metadata: { - created: "2024-01-01", - categories: ["electronics", "books"], - }, - }, - preferences: ["notifications", "updates"], - }, - type: "form", - expected: - "user%5Bprofile%5D%5Bname%5D=John%20Doe&" + - "user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark&" + - "user%5Bprofile%5D%5Bsettings%5D%5Bnotifications%5D=true&" + - "user%5Btags%5D=admin&" + - "user%5Btags%5D=user&" + - "user%5Bcontacts%5D%5Btype%5D=email&" + - "user%5Bcontacts%5D%5Bvalue%5D=john%40example.com&" + - "user%5Bcontacts%5D%5Btype%5D=phone&" + - "user%5Bcontacts%5D%5Bvalue%5D=%2B1234567890&" + - "filters%5Bstatus%5D=active&" + - "filters%5Bstatus%5D=pending&" + - "filters%5Bmetadata%5D%5Bcreated%5D=2024-01-01&" + - "filters%5Bmetadata%5D%5Bcategories%5D=electronics&" + - "filters%5Bmetadata%5D%5Bcategories%5D=books&" + - "preferences=notifications&" + - "preferences=updates", - }, - { - description: "should return the input for pre-serialized form-urlencoded strings", - input: "key=value&another=param", - type: "other", - expected: "key=value&another=param", - }, - { - description: "should JSON stringify objects", - input: { key: "value" }, - type: "json", - expected: '{"key":"value"}', - }, - ]; - - testCases.forEach(({ description, input, type, expected, skipCondition }) => { - it(description, async () => { - if (skipCondition?.()) { - return; - } - - const result = await getRequestBody({ - body: input, - type, - }); - - if (input instanceof Uint8Array) { - expect(result).toBe(input); - } else { - expect(result).toBe(expected); - } - }); - }); - - it("should return FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const formData = new FormData(); - formData.append("key", "value"); - const result = await getRequestBody({ - body: formData, - type: "file", - }); - expect(result).toBe(formData); - } - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getResponseBody.test.ts deleted file mode 100644 index ad6be7fc2c9b..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/getResponseBody.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; - -import { RUNTIME } from "../../../src/core/runtime"; - -describe("Test getResponseBody", () => { - interface SimpleTestCase { - description: string; - responseData: string | Record; - responseType?: "blob" | "sse" | "streaming" | "text"; - expected: any; - skipCondition?: () => boolean; - } - - const simpleTestCases: SimpleTestCase[] = [ - { - description: "should handle text response type", - responseData: "test text", - responseType: "text", - expected: "test text", - }, - { - description: "should handle JSON response", - responseData: { key: "value" }, - expected: { key: "value" }, - }, - { - description: "should handle empty response", - responseData: "", - expected: undefined, - }, - { - description: "should handle non-JSON response", - responseData: "invalid json", - expected: { - ok: false, - error: { - reason: "non-json", - statusCode: 200, - rawBody: "invalid json", - }, - }, - }, - ]; - - simpleTestCases.forEach(({ description, responseData, responseType, expected, skipCondition }) => { - it(description, async () => { - if (skipCondition?.()) { - return; - } - - const mockResponse = new Response( - typeof responseData === "string" ? responseData : JSON.stringify(responseData), - ); - const result = await getResponseBody(mockResponse, responseType); - expect(result).toEqual(expected); - }); - }); - - it("should handle blob response type", async () => { - const mockBlob = new Blob(["test"], { type: "text/plain" }); - const mockResponse = new Response(mockBlob); - const result = await getResponseBody(mockResponse, "blob"); - // @ts-expect-error - expect(result.constructor.name).toBe("Blob"); - }); - - it("should handle sse response type", async () => { - if (RUNTIME.type === "node") { - const mockStream = new ReadableStream(); - const mockResponse = new Response(mockStream); - const result = await getResponseBody(mockResponse, "sse"); - expect(result).toBe(mockStream); - } - }); - - it("should handle streaming response type", async () => { - const encoder = new TextEncoder(); - const testData = "test stream data"; - const mockStream = new ReadableStream({ - start(controller) { - controller.enqueue(encoder.encode(testData)); - controller.close(); - }, - }); - - const mockResponse = new Response(mockStream); - const result = (await getResponseBody(mockResponse, "streaming")) as ReadableStream; - - expect(result).toBeInstanceOf(ReadableStream); - - const reader = result.getReader(); - const decoder = new TextDecoder(); - const { value } = await reader.read(); - const streamContent = decoder.decode(value); - expect(streamContent).toBe(testData); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/logging.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/logging.test.ts deleted file mode 100644 index 366c9b6ced61..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/logging.test.ts +++ /dev/null @@ -1,517 +0,0 @@ -import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -function mockErrorResponse(data: unknown = { error: "Error" }, status = 404, statusText = "Not Found") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -describe("Fetcher Logging Integration", () => { - describe("Request Logging", () => { - it("should log successful request at debug level", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - headers: { "Content-Type": "application/json" }, - body: { test: "data" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "POST", - url: "https://example.com/api", - headers: expect.toContainHeaders({ - "Content-Type": "application/json", - }), - hasBody: true, - }), - ); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - method: "POST", - url: "https://example.com/api", - statusCode: 200, - }), - ); - }); - - it("should not log debug messages at info level for successful requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "info", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - }); - - it("should log request with body flag", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - hasBody: true, - }), - ); - }); - - it("should log request without body flag", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - hasBody: false, - }), - ); - }); - - it("should not log when silent mode is enabled", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: true, - }, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).not.toHaveBeenCalled(); - }); - - it("should not log when no logging config is provided", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - }); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - }); - }); - - describe("Error Logging", () => { - it("should log 4xx errors at error level", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Not found" }, 404, "Not Found"); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - statusCode: 404, - }), - ); - }); - - it("should log 5xx errors at error level", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Internal error" }, 500, "Internal Server Error"); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - statusCode: 500, - }), - ); - }); - - it("should log aborted request errors", async () => { - const mockLogger = createMockLogger(); - - const abortController = new AbortController(); - abortController.abort(); - - global.fetch = vi.fn().mockRejectedValue(new Error("Aborted")); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - abortSignal: abortController.signal, - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request was aborted", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - }), - ); - }); - - it("should log timeout errors", async () => { - const mockLogger = createMockLogger(); - - const timeoutError = new Error("Request timeout"); - timeoutError.name = "AbortError"; - - global.fetch = vi.fn().mockRejectedValue(timeoutError); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request timed out", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - timeoutMs: undefined, - }), - ); - }); - - it("should log unknown errors", async () => { - const mockLogger = createMockLogger(); - - const unknownError = new Error("Unknown error"); - - global.fetch = vi.fn().mockRejectedValue(unknownError); - - const result = await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(result.ok).toBe(false); - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error", - expect.objectContaining({ - method: "GET", - url: "https://example.com/api", - errorMessage: "Unknown error", - }), - ); - }); - }); - - describe("Logging with Redaction", () => { - it("should redact sensitive data in error logs", async () => { - const mockLogger = createMockLogger(); - mockErrorResponse({ error: "Unauthorized" }, 401, "Unauthorized"); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]", - }), - ); - }); - }); - - describe("Different HTTP Methods", () => { - it("should log GET requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "GET", - }), - ); - }); - - it("should log POST requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 201, "Created"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "POST", - }), - ); - }); - - it("should log PUT requests", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "PUT", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "PUT", - }), - ); - }); - - it("should log DELETE requests", async () => { - const mockLogger = createMockLogger(); - global.fetch = vi.fn().mockResolvedValue( - new Response(null, { - status: 200, - statusText: "OK", - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "DELETE", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - method: "DELETE", - }), - ); - }); - }); - - describe("Status Code Logging", () => { - it("should log 2xx success status codes", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 201, "Created"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "POST", - body: { data: "test" }, - contentType: "application/json", - requestType: "json", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - statusCode: 201, - }), - ); - }); - - it("should log 3xx redirect status codes as success", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse({ data: "test" }, 301, "Moved Permanently"); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - statusCode: 301, - }), - ); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/makeRequest.test.ts deleted file mode 100644 index ea49466a55fc..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/makeRequest.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { Mock } from "vitest"; -import { makeRequest } from "../../../src/core/fetcher/makeRequest"; - -describe("Test makeRequest", () => { - const mockPostUrl = "https://httpbin.org/post"; - const mockGetUrl = "https://httpbin.org/get"; - const mockHeaders = { "Content-Type": "application/json" }; - const mockBody = JSON.stringify({ key: "value" }); - - let mockFetch: Mock; - - beforeEach(() => { - mockFetch = vi.fn(); - mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); - }); - - it("should handle POST request correctly", async () => { - const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockPostUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "POST", - headers: mockHeaders, - body: mockBody, - credentials: undefined, - }), - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); - - it("should handle GET request correctly", async () => { - const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockGetUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "GET", - headers: mockHeaders, - body: undefined, - credentials: undefined, - }), - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/redacting.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/redacting.test.ts deleted file mode 100644 index d599376b9bcf..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/redacting.test.ts +++ /dev/null @@ -1,1115 +0,0 @@ -import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify(data), { - status, - statusText, - }), - ); -} - -describe("Redacting Logic", () => { - describe("Header Redaction", () => { - it("should redact authorization header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { Authorization: "Bearer secret-token-12345" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Authorization: "[REDACTED]", - }), - }), - ); - }); - - it("should redact api-key header (case-insensitive)", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-API-KEY": "secret-api-key" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-API-KEY": "[REDACTED]", - }), - }), - ); - }); - - it("should redact cookie header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { Cookie: "session=abc123; token=xyz789" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Cookie: "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-auth-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "x-auth-token": "auth-token-12345" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "x-auth-token": "[REDACTED]", - }), - }), - ); - }); - - it("should redact proxy-authorization header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "Proxy-Authorization": "Basic credentials" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "Proxy-Authorization": "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-csrf-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-CSRF-Token": "csrf-token-abc" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-CSRF-Token": "[REDACTED]", - }), - }), - ); - }); - - it("should redact www-authenticate header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "WWW-Authenticate": "Bearer realm=example" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "WWW-Authenticate": "[REDACTED]", - }), - }), - ); - }); - - it("should redact x-session-token header", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { "X-Session-Token": "session-token-xyz" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "X-Session-Token": "[REDACTED]", - }), - }), - ); - }); - - it("should not redact non-sensitive headers", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { - "Content-Type": "application/json", - "User-Agent": "Test/1.0", - Accept: "application/json", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - "Content-Type": "application/json", - "User-Agent": "Test/1.0", - Accept: "application/json", - }), - }), - ); - }); - - it("should redact multiple sensitive headers at once", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - headers: { - Authorization: "Bearer token", - "X-API-Key": "api-key", - Cookie: "session=123", - "Content-Type": "application/json", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - headers: expect.toContainHeaders({ - Authorization: "[REDACTED]", - "X-API-Key": "[REDACTED]", - Cookie: "[REDACTED]", - "Content-Type": "application/json", - }), - }), - ); - }); - }); - - describe("Response Header Redaction", () => { - it("should redact Set-Cookie in response headers", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("Set-Cookie", "session=abc123; HttpOnly; Secure"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - "set-cookie": "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - - it("should redact authorization in response headers", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("Authorization", "Bearer token-123"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ data: "test" }), { - status: 200, - statusText: "OK", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "HTTP request succeeded", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - authorization: "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - - it("should redact response headers in error responses", async () => { - const mockLogger = createMockLogger(); - - const mockHeaders = new Headers(); - mockHeaders.set("WWW-Authenticate", "Bearer realm=example"); - mockHeaders.set("Content-Type", "application/json"); - - global.fetch = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ error: "Unauthorized" }), { - status: 401, - statusText: "Unauthorized", - headers: mockHeaders, - }), - ); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "error", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.error).toHaveBeenCalledWith( - "HTTP request failed with error status", - expect.objectContaining({ - responseHeaders: expect.toContainHeaders({ - "www-authenticate": "[REDACTED]", - "content-type": "application/json", - }), - }), - ); - }); - }); - - describe("Query Parameter Redaction", () => { - it("should redact api_key query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { api_key: "secret-key" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - api_key: "[REDACTED]", - }), - }), - ); - }); - - it("should redact token query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { token: "secret-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - token: "[REDACTED]", - }), - }), - ); - }); - - it("should redact access_token query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { access_token: "secret-access-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - access_token: "[REDACTED]", - }), - }), - ); - }); - - it("should redact password query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { password: "secret-password" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - password: "[REDACTED]", - }), - }), - ); - }); - - it("should redact secret query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { secret: "secret-value" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - secret: "[REDACTED]", - }), - }), - ); - }); - - it("should redact session_id query parameter", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { session_id: "session-123" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - session_id: "[REDACTED]", - }), - }), - ); - }); - - it("should not redact non-sensitive query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { - page: "1", - limit: "10", - sort: "name", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - page: "1", - limit: "10", - sort: "name", - }), - }), - ); - }); - - it("should not redact parameters containing 'auth' substring like 'author'", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { - author: "john", - authenticate: "false", - authorization_level: "user", - }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - author: "john", - authenticate: "false", - authorization_level: "user", - }), - }), - ); - }); - - it("should handle undefined query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: undefined, - }), - ); - }); - - it("should redact case-insensitive query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - queryParameters: { API_KEY: "secret-key", Token: "secret-token" }, - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - queryParameters: expect.objectContaining({ - API_KEY: "[REDACTED]", - Token: "[REDACTED]", - }), - }), - ); - }); - }); - - describe("URL Redaction", () => { - it("should redact credentials in URL", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:password@example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/api", - }), - ); - }); - - it("should redact api_key in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret-key&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]&page=1", - }), - ); - }); - - it("should redact token in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?token=secret-token", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?token=[REDACTED]", - }), - ); - }); - - it("should redact password in query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?username=user&password=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?username=user&password=[REDACTED]", - }), - ); - }); - - it("should not redact non-sensitive query strings", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?page=1&limit=10&sort=name", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?page=1&limit=10&sort=name", - }), - ); - }); - - it("should not redact URL parameters containing 'auth' substring like 'author'", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?author=john&authenticate=false&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?author=john&authenticate=false&page=1", - }), - ); - }); - - it("should handle URL with fragment", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?token=secret#section", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?token=[REDACTED]#section", - }), - ); - }); - - it("should redact URL-encoded query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api%5Fkey=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api%5Fkey=[REDACTED]", - }), - ); - }); - - it("should handle URL without query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api", - }), - ); - }); - - it("should handle empty query string", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?", - }), - ); - }); - - it("should redact multiple sensitive parameters in URL", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?api_key=secret1&token=secret2&page=1", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?api_key=[REDACTED]&token=[REDACTED]&page=1", - }), - ); - }); - - it("should redact both credentials and query parameters", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:pass@example.com/api?token=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/api?token=[REDACTED]", - }), - ); - }); - - it("should use fast path for URLs without sensitive keywords", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", - }), - ); - }); - - it("should handle query parameter without value", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?flag&token=secret", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?flag&token=[REDACTED]", - }), - ); - }); - - it("should handle URL with multiple @ symbols in credentials", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user@example.com:pass@host.com/api", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@host.com/api", - }), - ); - }); - - it("should handle URL with @ in query parameter but not in credentials", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://example.com/api?email=user@example.com", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://example.com/api?email=user@example.com", - }), - ); - }); - - it("should handle URL with both credentials and @ in path", async () => { - const mockLogger = createMockLogger(); - mockSuccessResponse(); - - await fetcherImpl({ - url: "https://user:pass@example.com/users/@username", - method: "GET", - responseType: "json", - maxRetries: 0, - logging: { - level: "debug", - logger: mockLogger, - silent: false, - }, - }); - - expect(mockLogger.debug).toHaveBeenCalledWith( - "Making HTTP request", - expect.objectContaining({ - url: "https://[REDACTED]@example.com/users/@username", - }), - ); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/requestWithRetries.test.ts deleted file mode 100644 index d22661367f4e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/requestWithRetries.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import type { Mock, MockInstance } from "vitest"; -import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; - -describe("requestWithRetries", () => { - let mockFetch: Mock; - let originalMathRandom: typeof Math.random; - let setTimeoutSpy: MockInstance; - - beforeEach(() => { - mockFetch = vi.fn(); - originalMathRandom = Math.random; - - Math.random = vi.fn(() => 0.5); - - vi.useFakeTimers({ - toFake: [ - "setTimeout", - "clearTimeout", - "setInterval", - "clearInterval", - "setImmediate", - "clearImmediate", - "Date", - "performance", - "requestAnimationFrame", - "cancelAnimationFrame", - "requestIdleCallback", - "cancelIdleCallback", - ], - }); - }); - - afterEach(() => { - Math.random = originalMathRandom; - vi.clearAllMocks(); - vi.clearAllTimers(); - }); - - it("should retry on retryable status codes", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const retryableStatuses = [408, 429, 500, 502]; - let callCount = 0; - - mockFetch.mockImplementation(async () => { - if (callCount < retryableStatuses.length) { - return new Response("", { status: retryableStatuses[callCount++] }); - } - return new Response("", { status: 200 }); - }); - - const responsePromise = requestWithRetries(() => mockFetch(), retryableStatuses.length); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(retryableStatuses.length + 1); - expect(response.status).toBe(200); - }); - - it("should respect maxRetries limit", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const maxRetries = 2; - mockFetch.mockResolvedValue(new Response("", { status: 500 })); - - const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); - expect(response.status).toBe(500); - }); - - it("should not retry on success status codes", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const successStatuses = [200, 201, 202]; - - for (const status of successStatuses) { - mockFetch.mockReset(); - setTimeoutSpy.mockClear(); - mockFetch.mockResolvedValueOnce(new Response("", { status })); - - const responsePromise = requestWithRetries(() => mockFetch(), 3); - await vi.runAllTimersAsync(); - await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(1); - expect(setTimeoutSpy).not.toHaveBeenCalled(); - } - }); - - interface RetryHeaderTestCase { - description: string; - headerName: string; - headerValue: string | (() => string); - expectedDelayMin: number; - expectedDelayMax: number; - } - - const retryHeaderTests: RetryHeaderTestCase[] = [ - { - description: "should respect retry-after header with seconds value", - headerName: "retry-after", - headerValue: "5", - expectedDelayMin: 4000, - expectedDelayMax: 6000, - }, - { - description: "should respect retry-after header with HTTP date value", - headerName: "retry-after", - headerValue: () => new Date(Date.now() + 3000).toUTCString(), - expectedDelayMin: 2000, - expectedDelayMax: 4000, - }, - { - description: "should respect x-ratelimit-reset header", - headerName: "x-ratelimit-reset", - headerValue: () => Math.floor((Date.now() + 4000) / 1000).toString(), - expectedDelayMin: 3000, - expectedDelayMax: 6000, - }, - ]; - - retryHeaderTests.forEach(({ description, headerName, headerValue, expectedDelayMin, expectedDelayMax }) => { - it(description, async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const value = typeof headerValue === "function" ? headerValue() : headerValue; - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ [headerName]: value }), - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); - const actualDelay = setTimeoutSpy.mock.calls[0][1]; - expect(actualDelay).toBeGreaterThan(expectedDelayMin); - expect(actualDelay).toBeLessThan(expectedDelayMax); - expect(response.status).toBe(200); - }); - }); - - it("should apply correct exponential backoff with jitter", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch.mockResolvedValue(new Response("", { status: 500 })); - const maxRetries = 3; - const expectedDelays = [1000, 2000, 4000]; - - const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); - await vi.runAllTimersAsync(); - await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); - - expectedDelays.forEach((delay, index) => { - expect(setTimeoutSpy).toHaveBeenNthCalledWith(index + 1, expect.any(Function), delay); - }); - - expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); - }); - - it("should handle concurrent retries independently", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch - .mockResolvedValueOnce(new Response("", { status: 500 })) - .mockResolvedValueOnce(new Response("", { status: 500 })) - .mockResolvedValueOnce(new Response("", { status: 200 })) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const promise1 = requestWithRetries(() => mockFetch(), 1); - const promise2 = requestWithRetries(() => mockFetch(), 1); - - await vi.runAllTimersAsync(); - const [response1, response2] = await Promise.all([promise1, promise2]); - - expect(response1.status).toBe(200); - expect(response2.status).toBe(200); - }); - - it("should cap delay at MAX_RETRY_DELAY for large header values", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ "retry-after": "120" }), // 120 seconds = 120000ms > MAX_RETRY_DELAY (60000ms) - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); - expect(response.status).toBe(200); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/signals.test.ts deleted file mode 100644 index d7b6d1e63caa..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/signals.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; - -describe("Test getTimeoutSignal", () => { - beforeEach(() => { - vi.useFakeTimers(); - }); - - afterEach(() => { - vi.useRealTimers(); - }); - - it("should return an object with signal and abortId", () => { - const { signal, abortId } = getTimeoutSignal(1000); - - expect(signal).toBeDefined(); - expect(abortId).toBeDefined(); - expect(signal).toBeInstanceOf(AbortSignal); - expect(signal.aborted).toBe(false); - }); - - it("should create a signal that aborts after the specified timeout", () => { - const timeoutMs = 5000; - const { signal } = getTimeoutSignal(timeoutMs); - - expect(signal.aborted).toBe(false); - - vi.advanceTimersByTime(timeoutMs - 1); - expect(signal.aborted).toBe(false); - - vi.advanceTimersByTime(1); - expect(signal.aborted).toBe(true); - }); -}); - -describe("Test anySignal", () => { - it("should return an AbortSignal", () => { - const signal = anySignal(new AbortController().signal); - expect(signal).toBeInstanceOf(AbortSignal); - }); - - it("should abort when any of the input signals is aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal(controller1.signal, controller2.signal); - - expect(signal.aborted).toBe(false); - controller1.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should handle an array of signals", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal([controller1.signal, controller2.signal]); - - expect(signal.aborted).toBe(false); - controller2.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should abort immediately if one of the input signals is already aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - controller1.abort(); - - const signal = anySignal(controller1.signal, controller2.signal); - expect(signal.aborted).toBe(true); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/test-file.txt deleted file mode 100644 index c66d471e359c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/fetcher/test-file.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test file! diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/logging/logger.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/logging/logger.test.ts deleted file mode 100644 index 2e0b5fe5040c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/logging/logger.test.ts +++ /dev/null @@ -1,454 +0,0 @@ -import { ConsoleLogger, createLogger, Logger, LogLevel } from "../../../src/core/logging/logger"; - -function createMockLogger() { - return { - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }; -} - -describe("Logger", () => { - describe("LogLevel", () => { - it("should have correct log levels", () => { - expect(LogLevel.Debug).toBe("debug"); - expect(LogLevel.Info).toBe("info"); - expect(LogLevel.Warn).toBe("warn"); - expect(LogLevel.Error).toBe("error"); - }); - }); - - describe("ConsoleLogger", () => { - let consoleLogger: ConsoleLogger; - let consoleSpy: { - debug: ReturnType; - info: ReturnType; - warn: ReturnType; - error: ReturnType; - }; - - beforeEach(() => { - consoleLogger = new ConsoleLogger(); - consoleSpy = { - debug: vi.spyOn(console, "debug").mockImplementation(() => {}), - info: vi.spyOn(console, "info").mockImplementation(() => {}), - warn: vi.spyOn(console, "warn").mockImplementation(() => {}), - error: vi.spyOn(console, "error").mockImplementation(() => {}), - }; - }); - - afterEach(() => { - consoleSpy.debug.mockRestore(); - consoleSpy.info.mockRestore(); - consoleSpy.warn.mockRestore(); - consoleSpy.error.mockRestore(); - }); - - it("should log debug messages", () => { - consoleLogger.debug("debug message", { data: "test" }); - expect(consoleSpy.debug).toHaveBeenCalledWith("debug message", { data: "test" }); - }); - - it("should log info messages", () => { - consoleLogger.info("info message", { data: "test" }); - expect(consoleSpy.info).toHaveBeenCalledWith("info message", { data: "test" }); - }); - - it("should log warn messages", () => { - consoleLogger.warn("warn message", { data: "test" }); - expect(consoleSpy.warn).toHaveBeenCalledWith("warn message", { data: "test" }); - }); - - it("should log error messages", () => { - consoleLogger.error("error message", { data: "test" }); - expect(consoleSpy.error).toHaveBeenCalledWith("error message", { data: "test" }); - }); - - it("should handle multiple arguments", () => { - consoleLogger.debug("message", "arg1", "arg2", { key: "value" }); - expect(consoleSpy.debug).toHaveBeenCalledWith("message", "arg1", "arg2", { key: "value" }); - }); - }); - - describe("Logger with level filtering", () => { - let mockLogger: { - debug: ReturnType; - info: ReturnType; - warn: ReturnType; - error: ReturnType; - }; - - beforeEach(() => { - mockLogger = createMockLogger(); - }); - - describe("Debug level", () => { - it("should log all levels when set to debug", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).toHaveBeenCalledWith("debug"); - expect(mockLogger.info).toHaveBeenCalledWith("info"); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(true); - expect(logger.isInfo()).toBe(true); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Info level", () => { - it("should log info, warn, and error when set to info", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).toHaveBeenCalledWith("info"); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(true); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Warn level", () => { - it("should log warn and error when set to warn", () => { - const logger = new Logger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).toHaveBeenCalledWith("warn"); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(true); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Error level", () => { - it("should only log error when set to error", () => { - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).toHaveBeenCalledWith("error"); - }); - - it("should report correct level checks", () => { - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(false); - expect(logger.isError()).toBe(true); - }); - }); - - describe("Silent mode", () => { - it("should not log anything when silent is true", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - - expect(mockLogger.debug).not.toHaveBeenCalled(); - expect(mockLogger.info).not.toHaveBeenCalled(); - expect(mockLogger.warn).not.toHaveBeenCalled(); - expect(mockLogger.error).not.toHaveBeenCalled(); - }); - - it("should report all level checks as false when silent", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - expect(logger.isDebug()).toBe(false); - expect(logger.isInfo()).toBe(false); - expect(logger.isWarn()).toBe(false); - expect(logger.isError()).toBe(false); - }); - }); - - describe("shouldLog", () => { - it("should correctly determine if level should be logged", () => { - const logger = new Logger({ - level: LogLevel.Info, - logger: mockLogger, - silent: false, - }); - - expect(logger.shouldLog(LogLevel.Debug)).toBe(false); - expect(logger.shouldLog(LogLevel.Info)).toBe(true); - expect(logger.shouldLog(LogLevel.Warn)).toBe(true); - expect(logger.shouldLog(LogLevel.Error)).toBe(true); - }); - - it("should return false for all levels when silent", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: true, - }); - - expect(logger.shouldLog(LogLevel.Debug)).toBe(false); - expect(logger.shouldLog(LogLevel.Info)).toBe(false); - expect(logger.shouldLog(LogLevel.Warn)).toBe(false); - expect(logger.shouldLog(LogLevel.Error)).toBe(false); - }); - }); - - describe("Multiple arguments", () => { - it("should pass multiple arguments to logger", () => { - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("message", "arg1", { key: "value" }, 123); - expect(mockLogger.debug).toHaveBeenCalledWith("message", "arg1", { key: "value" }, 123); - }); - }); - }); - - describe("createLogger", () => { - it("should return default logger when no config provided", () => { - const logger = createLogger(); - expect(logger).toBeInstanceOf(Logger); - }); - - it("should return same logger instance when Logger is passed", () => { - const customLogger = new Logger({ - level: LogLevel.Debug, - logger: new ConsoleLogger(), - silent: false, - }); - - const result = createLogger(customLogger); - expect(result).toBe(customLogger); - }); - - it("should create logger with custom config", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - level: LogLevel.Warn, - logger: mockLogger, - silent: false, - }); - - expect(logger).toBeInstanceOf(Logger); - logger.warn("test"); - expect(mockLogger.warn).toHaveBeenCalledWith("test"); - }); - - it("should use default values for missing config", () => { - const logger = createLogger({}); - expect(logger).toBeInstanceOf(Logger); - }); - - it("should override default level", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("test"); - expect(mockLogger.debug).toHaveBeenCalledWith("test"); - }); - - it("should override default silent mode", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - logger: mockLogger, - silent: false, - }); - - logger.info("test"); - expect(mockLogger.info).toHaveBeenCalledWith("test"); - }); - - it("should use provided logger implementation", () => { - const customLogger = createMockLogger(); - - const logger = createLogger({ - logger: customLogger, - level: LogLevel.Debug, - silent: false, - }); - - logger.debug("test"); - expect(customLogger.debug).toHaveBeenCalledWith("test"); - }); - - it("should default to silent: true", () => { - const mockLogger = createMockLogger(); - - const logger = createLogger({ - logger: mockLogger, - level: LogLevel.Debug, - }); - - logger.debug("test"); - expect(mockLogger.debug).not.toHaveBeenCalled(); - }); - }); - - describe("Default logger", () => { - it("should have silent: true by default", () => { - const logger = createLogger(); - expect(logger.shouldLog(LogLevel.Info)).toBe(false); - }); - - it("should not log when using default logger", () => { - const logger = createLogger(); - - logger.info("test"); - expect(logger.isInfo()).toBe(false); - }); - }); - - describe("Edge cases", () => { - it("should handle empty message", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug(""); - expect(mockLogger.debug).toHaveBeenCalledWith(""); - }); - - it("should handle no arguments", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - logger.debug("message"); - expect(mockLogger.debug).toHaveBeenCalledWith("message"); - }); - - it("should handle complex objects", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Debug, - logger: mockLogger, - silent: false, - }); - - const complexObject = { - nested: { key: "value" }, - array: [1, 2, 3], - fn: () => "test", - }; - - logger.debug("message", complexObject); - expect(mockLogger.debug).toHaveBeenCalledWith("message", complexObject); - }); - - it("should handle errors as arguments", () => { - const mockLogger = createMockLogger(); - - const logger = new Logger({ - level: LogLevel.Error, - logger: mockLogger, - silent: false, - }); - - const error = new Error("Test error"); - logger.error("Error occurred", error); - expect(mockLogger.error).toHaveBeenCalledWith("Error occurred", error); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/join.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/join.test.ts deleted file mode 100644 index 123488f084ea..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/join.test.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { join } from "../../../src/core/url/index"; - -describe("join", () => { - interface TestCase { - description: string; - base: string; - segments: string[]; - expected: string; - } - - describe("basic functionality", () => { - const basicTests: TestCase[] = [ - { description: "should return empty string for empty base", base: "", segments: [], expected: "" }, - { - description: "should return empty string for empty base with path", - base: "", - segments: ["path"], - expected: "", - }, - { - description: "should handle single segment", - base: "base", - segments: ["segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with trailing slash on base", - base: "base/", - segments: ["segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with leading slash", - base: "base", - segments: ["/segment"], - expected: "base/segment", - }, - { - description: "should handle single segment with both slashes", - base: "base/", - segments: ["/segment"], - expected: "base/segment", - }, - { - description: "should handle multiple segments", - base: "base", - segments: ["path1", "path2", "path3"], - expected: "base/path1/path2/path3", - }, - { - description: "should handle multiple segments with slashes", - base: "base/", - segments: ["/path1/", "/path2/", "/path3/"], - expected: "base/path1/path2/path3/", - }, - ]; - - basicTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("URL handling", () => { - const urlTests: TestCase[] = [ - { - description: "should handle absolute URLs", - base: "https://example.com", - segments: ["api", "v1"], - expected: "https://example.com/api/v1", - }, - { - description: "should handle absolute URLs with slashes", - base: "https://example.com/", - segments: ["/api/", "/v1/"], - expected: "https://example.com/api/v1/", - }, - { - description: "should handle absolute URLs with base path", - base: "https://example.com/base", - segments: ["api", "v1"], - expected: "https://example.com/base/api/v1", - }, - { - description: "should preserve URL query parameters", - base: "https://example.com?query=1", - segments: ["api"], - expected: "https://example.com/api?query=1", - }, - { - description: "should preserve URL fragments", - base: "https://example.com#fragment", - segments: ["api"], - expected: "https://example.com/api#fragment", - }, - { - description: "should preserve URL query and fragments", - base: "https://example.com?query=1#fragment", - segments: ["api"], - expected: "https://example.com/api?query=1#fragment", - }, - { - description: "should handle http protocol", - base: "http://example.com", - segments: ["api"], - expected: "http://example.com/api", - }, - { - description: "should handle ftp protocol", - base: "ftp://example.com", - segments: ["files"], - expected: "ftp://example.com/files", - }, - { - description: "should handle ws protocol", - base: "ws://example.com", - segments: ["socket"], - expected: "ws://example.com/socket", - }, - { - description: "should fallback to path joining for malformed URLs", - base: "not-a-url://", - segments: ["path"], - expected: "not-a-url:///path", - }, - ]; - - urlTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("edge cases", () => { - const edgeCaseTests: TestCase[] = [ - { - description: "should handle empty segments", - base: "base", - segments: ["", "path"], - expected: "base/path", - }, - { - description: "should handle null segments", - base: "base", - segments: [null as any, "path"], - expected: "base/path", - }, - { - description: "should handle undefined segments", - base: "base", - segments: [undefined as any, "path"], - expected: "base/path", - }, - { - description: "should handle segments with only single slash", - base: "base", - segments: ["/", "path"], - expected: "base/path", - }, - { - description: "should handle segments with only double slash", - base: "base", - segments: ["//", "path"], - expected: "base/path", - }, - { - description: "should handle base paths with trailing slashes", - base: "base/", - segments: ["path"], - expected: "base/path", - }, - { - description: "should handle complex nested paths", - base: "api/v1/", - segments: ["/users/", "/123/", "/profile"], - expected: "api/v1/users/123/profile", - }, - ]; - - edgeCaseTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("real-world scenarios", () => { - const realWorldTests: TestCase[] = [ - { - description: "should handle API endpoint construction", - base: "https://api.example.com/v1", - segments: ["users", "123", "posts"], - expected: "https://api.example.com/v1/users/123/posts", - }, - { - description: "should handle file path construction", - base: "/var/www", - segments: ["html", "assets", "images"], - expected: "/var/www/html/assets/images", - }, - { - description: "should handle relative path construction", - base: "../parent", - segments: ["child", "grandchild"], - expected: "../parent/child/grandchild", - }, - { - description: "should handle Windows-style paths", - base: "C:\\Users", - segments: ["Documents", "file.txt"], - expected: "C:\\Users/Documents/file.txt", - }, - ]; - - realWorldTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); - - describe("performance scenarios", () => { - it("should handle many segments efficiently", () => { - const segments = Array(100).fill("segment"); - const result = join("base", ...segments); - expect(result).toBe(`base/${segments.join("/")}`); - }); - - it("should handle long URLs", () => { - const longPath = "a".repeat(1000); - expect(join("https://example.com", longPath)).toBe(`https://example.com/${longPath}`); - }); - }); - - describe("trailing slash preservation", () => { - const trailingSlashTests: TestCase[] = [ - { - description: - "should preserve trailing slash on final result when base has trailing slash and no segments", - base: "https://api.example.com/", - segments: [], - expected: "https://api.example.com/", - }, - { - description: "should preserve trailing slash on v1 path", - base: "https://api.example.com/v1/", - segments: [], - expected: "https://api.example.com/v1/", - }, - { - description: "should preserve trailing slash when last segment has trailing slash", - base: "https://api.example.com", - segments: ["users/"], - expected: "https://api.example.com/users/", - }, - { - description: "should preserve trailing slash with relative path", - base: "api/v1", - segments: ["users/"], - expected: "api/v1/users/", - }, - { - description: "should preserve trailing slash with multiple segments", - base: "https://api.example.com", - segments: ["v1", "collections/"], - expected: "https://api.example.com/v1/collections/", - }, - { - description: "should preserve trailing slash with base path", - base: "base", - segments: ["path1", "path2/"], - expected: "base/path1/path2/", - }, - ]; - - trailingSlashTests.forEach(({ description, base, segments, expected }) => { - it(description, () => { - expect(join(base, ...segments)).toBe(expected); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/qs.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/qs.test.ts deleted file mode 100644 index 42cdffb9e5ea..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/unit/url/qs.test.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { toQueryString } from "../../../src/core/url/index"; - -describe("Test qs toQueryString", () => { - interface BasicTestCase { - description: string; - input: any; - expected: string; - } - - describe("Basic functionality", () => { - const basicTests: BasicTestCase[] = [ - { description: "should return empty string for null", input: null, expected: "" }, - { description: "should return empty string for undefined", input: undefined, expected: "" }, - { description: "should return empty string for string primitive", input: "hello", expected: "" }, - { description: "should return empty string for number primitive", input: 42, expected: "" }, - { description: "should return empty string for true boolean", input: true, expected: "" }, - { description: "should return empty string for false boolean", input: false, expected: "" }, - { description: "should handle empty objects", input: {}, expected: "" }, - { - description: "should handle simple key-value pairs", - input: { name: "John", age: 30 }, - expected: "name=John&age=30", - }, - ]; - - basicTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Array handling", () => { - interface ArrayTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices" }; - expected: string; - } - - const arrayTests: ArrayTestCase[] = [ - { - description: "should handle arrays with indices format (default)", - input: { items: ["a", "b", "c"] }, - expected: "items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c", - }, - { - description: "should handle arrays with repeat format", - input: { items: ["a", "b", "c"] }, - options: { arrayFormat: "repeat" }, - expected: "items=a&items=b&items=c", - }, - { - description: "should handle empty arrays", - input: { items: [] }, - expected: "", - }, - { - description: "should handle arrays with mixed types", - input: { mixed: ["string", 42, true, false] }, - expected: "mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false", - }, - { - description: "should handle arrays with objects", - input: { users: [{ name: "John" }, { name: "Jane" }] }, - expected: "users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane", - }, - { - description: "should handle arrays with objects in repeat format", - input: { users: [{ name: "John" }, { name: "Jane" }] }, - options: { arrayFormat: "repeat" }, - expected: "users%5Bname%5D=John&users%5Bname%5D=Jane", - }, - ]; - - arrayTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Nested objects", () => { - const nestedTests: BasicTestCase[] = [ - { - description: "should handle nested objects", - input: { user: { name: "John", age: 30 } }, - expected: "user%5Bname%5D=John&user%5Bage%5D=30", - }, - { - description: "should handle deeply nested objects", - input: { user: { profile: { name: "John", settings: { theme: "dark" } } } }, - expected: "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - }, - { - description: "should handle empty nested objects", - input: { user: {} }, - expected: "", - }, - ]; - - nestedTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Encoding", () => { - interface EncodingTestCase { - description: string; - input: any; - options?: { encode?: boolean }; - expected: string; - } - - const encodingTests: EncodingTestCase[] = [ - { - description: "should encode by default", - input: { name: "John Doe", email: "john@example.com" }, - expected: "name=John%20Doe&email=john%40example.com", - }, - { - description: "should not encode when encode is false", - input: { name: "John Doe", email: "john@example.com" }, - options: { encode: false }, - expected: "name=John Doe&email=john@example.com", - }, - { - description: "should encode special characters in keys", - input: { "user name": "John", "email[primary]": "john@example.com" }, - expected: "user%20name=John&email%5Bprimary%5D=john%40example.com", - }, - { - description: "should not encode special characters in keys when encode is false", - input: { "user name": "John", "email[primary]": "john@example.com" }, - options: { encode: false }, - expected: "user name=John&email[primary]=john@example.com", - }, - ]; - - encodingTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Mixed scenarios", () => { - interface MixedTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices" }; - expected: string; - } - - const mixedTests: MixedTestCase[] = [ - { - description: "should handle complex nested structures", - input: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - expected: - "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - { - description: "should handle complex nested structures with repeat format", - input: { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], - }, - }, - sort: { field: "name", direction: "asc" }, - }, - options: { arrayFormat: "repeat" }, - expected: - "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - }, - { - description: "should handle arrays with null/undefined values", - input: { items: ["a", null, "c", undefined, "e"] }, - expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e", - }, - { - description: "should handle objects with null/undefined values", - input: { name: "John", age: null, email: undefined, active: true }, - expected: "name=John&age=&active=true", - }, - ]; - - mixedTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); - - describe("Edge cases", () => { - const edgeCaseTests: BasicTestCase[] = [ - { - description: "should handle numeric keys", - input: { "0": "zero", "1": "one" }, - expected: "0=zero&1=one", - }, - { - description: "should handle boolean values in objects", - input: { enabled: true, disabled: false }, - expected: "enabled=true&disabled=false", - }, - { - description: "should handle empty strings", - input: { name: "", description: "test" }, - expected: "name=&description=test", - }, - { - description: "should handle zero values", - input: { count: 0, price: 0.0 }, - expected: "count=0&price=0", - }, - { - description: "should handle arrays with empty strings", - input: { items: ["a", "", "c"] }, - expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c", - }, - ]; - - edgeCaseTests.forEach(({ description, input, expected }) => { - it(description, () => { - expect(toQueryString(input)).toBe(expected); - }); - }); - }); - - describe("Options combinations", () => { - interface OptionsTestCase { - description: string; - input: any; - options?: { arrayFormat?: "repeat" | "indices"; encode?: boolean }; - expected: string; - } - - const optionsTests: OptionsTestCase[] = [ - { - description: "should respect both arrayFormat and encode options", - input: { items: ["a & b", "c & d"] }, - options: { arrayFormat: "repeat", encode: false }, - expected: "items=a & b&items=c & d", - }, - { - description: "should use default options when none provided", - input: { items: ["a", "b"] }, - expected: "items%5B0%5D=a&items%5B1%5D=b", - }, - { - description: "should merge provided options with defaults", - input: { items: ["a", "b"], name: "John Doe" }, - options: { encode: false }, - expected: "items[0]=a&items[1]=b&name=John Doe", - }, - ]; - - optionsTests.forEach(({ description, input, options, expected }) => { - it(description, () => { - expect(toQueryString(input, options)).toBe(expected); - }); - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/.gitkeep b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/auth.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/auth.test.ts deleted file mode 100644 index b5d4a7c2b77d..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/auth.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsClient } from "../../src/Client"; -import { mockServerPool } from "../mock-server/MockServerPool"; -import { mockOAuthScheme } from "./mockAuth"; - -describe("AuthClient", () => { - test("getTokenWithClientCredentials", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsClient({ - maxRetries: 0, - clientId: "my_oauth_app_123", - clientSecret: "sk_live_abcdef123456789", - environment: server.baseUrl, - }); - const rawRequestBody = { - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - audience: "https://api.example.com", - grant_type: "client_credentials", - scope: "read:users", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); - - const response = await client.auth.getTokenWithClientCredentials({ - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - scope: "read:users", - }); - expect(response).toEqual({ - access_token: "access_token", - expires_in: 1, - refresh_token: "refresh_token", - }); - }); - - test("refreshToken", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsClient({ - maxRetries: 0, - clientId: "my_oauth_app_123", - clientSecret: "sk_live_abcdef123456789", - environment: server.baseUrl, - }); - const rawRequestBody = { - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - refresh_token: "refresh_token", - audience: "https://api.example.com", - grant_type: "refresh_token", - scope: "read:users", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); - - const response = await client.auth.refreshToken({ - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - refresh_token: "refresh_token", - scope: "read:users", - }); - expect(response).toEqual({ - access_token: "access_token", - expires_in: 1, - refresh_token: "refresh_token", - }); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/mockAuth.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/mockAuth.ts deleted file mode 100644 index d9c3a85858d4..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/mockAuth.ts +++ /dev/null @@ -1,21 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { MockServer } from "../mock-server/MockServer"; - -export function mockOAuthScheme(server: MockServer): void { - const rawRequestBody = { - client_id: "my_oauth_app_123", - client_secret: "sk_live_abcdef123456789", - audience: "https://api.example.com", - grant_type: "client_credentials", - }; - const rawResponseBody = { access_token: "access_token", expires_in: 1, refresh_token: "refresh_token" }; - server - .mockEndpoint() - .post("/token") - .jsonBody(rawRequestBody) - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested-no-auth/api.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested-no-auth/api.test.ts deleted file mode 100644 index 963f5317b4e3..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested-no-auth/api.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsClient } from "../../../src/Client"; -import { mockServerPool } from "../../mock-server/MockServerPool"; -import { mockOAuthScheme } from "../mockAuth"; - -describe("ApiClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsClient({ - maxRetries: 0, - clientId: "my_oauth_app_123", - clientSecret: "sk_live_abcdef123456789", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/nested-no-auth/get-something").respondWith().statusCode(200).build(); - - const response = await client.nestedNoAuth.api.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested/api.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested/api.test.ts deleted file mode 100644 index 0ced05d71069..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/nested/api.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsClient } from "../../../src/Client"; -import { mockServerPool } from "../../mock-server/MockServerPool"; -import { mockOAuthScheme } from "../mockAuth"; - -describe("ApiClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsClient({ - maxRetries: 0, - clientId: "my_oauth_app_123", - clientSecret: "sk_live_abcdef123456789", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/nested/get-something").respondWith().statusCode(200).build(); - - const response = await client.nested.api.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/simple.test.ts b/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/simple.test.ts deleted file mode 100644 index 64567e7e4c7e..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tests/wire/simple.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import { SeedOauthClientCredentialsClient } from "../../src/Client"; -import { mockServerPool } from "../mock-server/MockServerPool"; -import { mockOAuthScheme } from "./mockAuth"; - -describe("SimpleClient", () => { - test("getSomething", async () => { - const server = mockServerPool.createServer(); - mockOAuthScheme(server); - - const client = new SeedOauthClientCredentialsClient({ - maxRetries: 0, - clientId: "my_oauth_app_123", - clientSecret: "sk_live_abcdef123456789", - environment: server.baseUrl, - }); - - server.mockEndpoint().get("/get-something").respondWith().statusCode(200).build(); - - const response = await client.simple.getSomething(); - expect(response).toEqual(undefined); - }); -}); diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.base.json b/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.base.json deleted file mode 100644 index d7627675de20..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.base.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "extendedDiagnostics": true, - "strict": true, - "target": "ES6", - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": true, - "outDir": "dist", - "rootDir": "src", - "baseUrl": "src", - "isolatedModules": true, - "isolatedDeclarations": true - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.cjs.json b/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.cjs.json deleted file mode 100644 index 5c11446f5984..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.cjs.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist/cjs" - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.esm.json b/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.esm.json deleted file mode 100644 index 6ce909748b2c..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.esm.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "module": "esnext", - "outDir": "dist/esm", - "verbatimModuleSyntax": true - }, - "include": ["src"], - "exclude": [] -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.json b/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.json deleted file mode 100644 index d77fdf00d259..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./tsconfig.cjs.json" -} diff --git a/seed/ts-sdk/oauth-client-credentials/token-override/vitest.config.mts b/seed/ts-sdk/oauth-client-credentials/token-override/vitest.config.mts deleted file mode 100644 index ba2ec4f9d45a..000000000000 --- a/seed/ts-sdk/oauth-client-credentials/token-override/vitest.config.mts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineConfig } from "vitest/config"; -export default defineConfig({ - test: { - projects: [ - { - test: { - globals: true, - name: "unit", - environment: "node", - root: "./tests", - include: ["**/*.test.{js,ts,jsx,tsx}"], - exclude: ["wire/**"], - setupFiles: ["./setup.ts"], - }, - }, - { - test: { - globals: true, - name: "wire", - environment: "node", - root: "./tests/wire", - setupFiles: ["../setup.ts", "../mock-server/setup.ts"], - }, - }, - ], - passWithNoTests: true, - }, -}); diff --git a/seed/ts-sdk/seed.yml b/seed/ts-sdk/seed.yml index ba7138553d15..60f7a73a8fc7 100644 --- a/seed/ts-sdk/seed.yml +++ b/seed/ts-sdk/seed.yml @@ -266,18 +266,12 @@ fixtures: - outputFolder: serde customConfig: noSerdeLayer: false - - outputFolder: token-override - customConfig: - oauthTokenOverride: true oauth-client-credentials-mandatory-auth: - outputFolder: no-custom-config customConfig: null oauth-client-credentials-environment-variables: - outputFolder: no-custom-config customConfig: null - - outputFolder: token-override - customConfig: - oauthTokenOverride: true oauth-client-credentials-nested-root: - outputFolder: no-custom-config customConfig: null