diff --git a/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts b/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts index d4c3cd0fa394..9a9d9921b8ee 100644 --- a/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts +++ b/generators/typescript-v2/ast/src/custom-config/TypescriptCustomConfigSchema.ts @@ -51,6 +51,7 @@ export const TypescriptCustomConfigSchema = z.strictObject({ inlinePathParameters: z.optional(z.boolean()), namespaceExport: z.optional(z.string()), noSerdeLayer: z.optional(z.boolean()), + serializationFormat: z.optional(z.enum(["zurg", "zod", "none"])), private: z.optional(z.boolean()), requireDefaultEnvironment: z.optional(z.boolean()), retainOriginalCasing: z.optional(z.boolean()), diff --git a/generators/typescript/express/cli/src/ExpressGeneratorCli.ts b/generators/typescript/express/cli/src/ExpressGeneratorCli.ts index 7c86d8fa0c47..672d9d87eea2 100644 --- a/generators/typescript/express/cli/src/ExpressGeneratorCli.ts +++ b/generators/typescript/express/cli/src/ExpressGeneratorCli.ts @@ -2,7 +2,7 @@ import { Logger } from "@fern-api/logger"; import { FernGeneratorExec } from "@fern-fern/generator-exec-sdk"; import { IntermediateRepresentation } from "@fern-fern/ir-sdk/api"; import { AbstractGeneratorCli } from "@fern-typescript/abstract-generator-cli"; -import { NpmPackage, PersistedTypescriptProject } from "@fern-typescript/commons"; +import { NpmPackage, PersistedTypescriptProject, SerializationPipeline } from "@fern-typescript/commons"; import { GeneratorContext } from "@fern-typescript/contexts"; import { ExpressGenerator } from "@fern-typescript/express-generator"; import { camelCase, upperFirst } from "lodash-es"; @@ -12,7 +12,14 @@ import { ExpressCustomConfigSchema } from "./custom-config/schema/ExpressCustomC export class ExpressGeneratorCli extends AbstractGeneratorCli { protected parseCustomConfig(customConfig: unknown, logger: Logger): ExpressCustomConfig { const parsed = customConfig != null ? ExpressCustomConfigSchema.parse(customConfig) : undefined; - const noSerdeLayer = parsed?.noSerdeLayer ?? false; + + // Resolve serialization format from new option or legacy noSerdeLayer + const serializationFormat = SerializationPipeline.resolveFormatType({ + serializationFormat: parsed?.serializationFormat, + noSerdeLayer: parsed?.noSerdeLayer + }); + const noSerdeLayer = serializationFormat === "none"; + const enableInlineTypes = false; // hardcode, not supported in Express const config = { useBrandedStringAliases: parsed?.useBrandedStringAliases ?? false, @@ -22,6 +29,7 @@ export class ExpressGeneratorCli extends AbstractGeneratorCli { protected parseCustomConfig(customConfig: unknown, logger: Logger): SdkCustomConfig { const parsed = customConfig != null ? SdkCustomConfigSchema.parse(customConfig) : undefined; - const noSerdeLayer = parsed?.noSerdeLayer ?? true; + + // Resolve serialization format from new option or legacy noSerdeLayer + // Note: SDK defaults to noSerdeLayer: true (no serialization) for backward compatibility + const serializationFormat = SerializationPipeline.resolveFormatType({ + serializationFormat: parsed?.serializationFormat, + noSerdeLayer: parsed?.noSerdeLayer ?? true + }); + const noSerdeLayer = serializationFormat === "none"; + const config = { useBrandedStringAliases: parsed?.useBrandedStringAliases ?? false, outputSourceFiles: parsed?.outputSourceFiles ?? true, @@ -58,6 +67,7 @@ export class SdkGeneratorCli extends AbstractGeneratorCli { treatUnknownAsAny: parsed?.treatUnknownAsAny ?? false, includeContentHeadersOnFileDownloadResponse: parsed?.includeContentHeadersOnFileDownloadResponse ?? false, noSerdeLayer, + serializationFormat, extraPeerDependencies: parsed?.extraPeerDependencies ?? {}, extraPeerDependenciesMeta: parsed?.extraPeerDependenciesMeta ?? {}, noOptionalProperties: parsed?.noOptionalProperties ?? false, @@ -207,6 +217,7 @@ export class SdkGeneratorCli extends AbstractGeneratorCli { treatUnknownAsAny: customConfig.treatUnknownAsAny, includeContentHeadersOnFileDownloadResponse: customConfig.includeContentHeadersOnFileDownloadResponse, includeSerdeLayer: !customConfig.noSerdeLayer, + serializationFormat: customConfig.serializationFormat, retainOriginalCasing: customConfig.retainOriginalCasing ?? false, parameterNaming: customConfig.parameterNaming ?? "default", noOptionalProperties: customConfig.noOptionalProperties, diff --git a/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts b/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts index 2e6803490b99..189924311395 100644 --- a/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts +++ b/generators/typescript/sdk/cli/src/custom-config/SdkCustomConfig.ts @@ -1,3 +1,5 @@ +import { SerializationFormatType } from "@fern-typescript/commons"; + // this is the parsed config shape. to view the allowed options for generators.yml, // see SdkCustomConfigSchema.ts export interface SdkCustomConfig { @@ -24,6 +26,7 @@ export interface SdkCustomConfig { treatUnknownAsAny: boolean; includeContentHeadersOnFileDownloadResponse: boolean; noSerdeLayer: boolean; + serializationFormat: SerializationFormatType; noOptionalProperties: boolean; includeApiReference: boolean | undefined; tolerateRepublish: boolean; diff --git a/generators/typescript/sdk/generator/src/SdkGenerator.ts b/generators/typescript/sdk/generator/src/SdkGenerator.ts index bac7c240f804..6798ed2635cc 100644 --- a/generators/typescript/sdk/generator/src/SdkGenerator.ts +++ b/generators/typescript/sdk/generator/src/SdkGenerator.ts @@ -27,6 +27,7 @@ import { NpmPackage, PackageId, PublicExportsManager, + SerializationFormatType, SimpleTypescriptProject, TypescriptProject } from "@fern-typescript/commons"; @@ -124,6 +125,7 @@ export declare namespace SdkGenerator { treatUnknownAsAny: boolean; includeContentHeadersOnFileDownloadResponse: boolean; includeSerdeLayer: boolean; + serializationFormat: SerializationFormatType; noOptionalProperties: boolean; tolerateRepublish: boolean; retainOriginalCasing: boolean; @@ -289,7 +291,8 @@ export class SdkGenerator { fetchSupport: this.config.fetchSupport, relativePackagePath: this.relativePackagePath, relativeTestPath: this.relativeTestPath, - generateEndpointMetadata: this.config.generateEndpointMetadata + generateEndpointMetadata: this.config.generateEndpointMetadata, + serializationFormat: this.config.serializationFormat }); const apiDirectory: ExportedDirectory[] = [ diff --git a/generators/typescript/utils/abstract-schema-generator/src/AbstractGeneratedSchema.ts b/generators/typescript/utils/abstract-schema-generator/src/AbstractGeneratedSchema.ts index 651247c1d1f0..9c816f6bd45a 100644 --- a/generators/typescript/utils/abstract-schema-generator/src/AbstractGeneratedSchema.ts +++ b/generators/typescript/utils/abstract-schema-generator/src/AbstractGeneratedSchema.ts @@ -18,23 +18,136 @@ export abstract class AbstractGeneratedSchema { } public writeSchemaToFile(context: Context): void { - context.sourceFile.addVariableStatement({ - isExported: true, - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: this.typeName, - type: getTextOfTsNode( - this.getReferenceToSchemaType({ - context, - rawShape: this.getReferenceToRawShape(context), - parsedShape: this.getReferenceToParsedShape(context) - }) + const schema = this.buildSchema(context); + const schemaExpression = schema.toExpression(); + const toJsonExpression = schema.toJsonExpression; + + // For Zod format, always generate a wrapper object with parse and json methods + // This is needed because Zod schemas don't have built-in bidirectional serialization + if (context.coreUtilities.zurg.name === "zod") { + // Generate: + // const __Schema = ; + // export const = { _schema: __Schema, parse: ..., json: ... }; + const internalSchemaName = `_${this.typeName}_Schema`; + const parsedParam = ts.factory.createIdentifier("parsed"); + const rawParam = ts.factory.createIdentifier("raw"); + const schemaRef = ts.factory.createIdentifier(internalSchemaName); + + // First, add the internal schema variable + context.sourceFile.addVariableStatement({ + isExported: false, + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: internalSchemaName, + initializer: getTextOfTsNode(schemaExpression) + } + ] + }); + + // If no toJsonExpression, use identity function: (parsed) => parsed + const jsonBody = toJsonExpression ? toJsonExpression(parsedParam) : parsedParam; + + // Get the type nodes for parameter annotations + const parsedTypeNode = this.getReferenceToParsedShape(context); + + // Get the raw type node for json return type cast + const rawTypeNode = this.getReferenceToRawShape(context); + + const wrapperObject = ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment("_schema", schemaRef), + ts.factory.createPropertyAssignment( + "parse", + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + rawParam, + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + // Cast the parse result to the expected Parsed type to fix Zod's loose inference + ts.factory.createAsExpression( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(schemaRef, "parse"), + undefined, + [rawParam] + ), + parsedTypeNode + ) + ) ), - initializer: getTextOfTsNode(this.buildSchema(context).toExpression()) - } - ] - }); + ts.factory.createPropertyAssignment( + "json", + ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + parsedParam, + undefined, + parsedTypeNode + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + // Cast the json result to the expected Raw type + ts.factory.createAsExpression(jsonBody, rawTypeNode) + ) + ) + ], + true + ); + + // Then add the exported wrapper + context.sourceFile.addVariableStatement({ + isExported: true, + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: this.typeName, + type: getTextOfTsNode( + this.getReferenceToSchemaType({ + context, + rawShape: this.getReferenceToRawShape(context), + parsedShape: this.getReferenceToParsedShape(context) + }) + ), + initializer: getTextOfTsNode(wrapperObject) + } + ] + }); + } else { + // Original behavior for Zurg format + context.sourceFile.addVariableStatement({ + isExported: true, + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: this.typeName, + type: getTextOfTsNode( + this.getReferenceToSchemaType({ + context, + rawShape: this.getReferenceToRawShape(context), + parsedShape: this.getReferenceToParsedShape(context) + }) + ), + initializer: getTextOfTsNode(schemaExpression) + } + ] + }); + } this.generateModule(context); } diff --git a/generators/typescript/utils/commons/src/core-utilities/CoreUtilitiesManager.ts b/generators/typescript/utils/commons/src/core-utilities/CoreUtilitiesManager.ts index 2803fb084280..46de8eae286c 100644 --- a/generators/typescript/utils/commons/src/core-utilities/CoreUtilitiesManager.ts +++ b/generators/typescript/utils/commons/src/core-utilities/CoreUtilitiesManager.ts @@ -8,6 +8,14 @@ import { DependencyManager } from "../dependency-manager/DependencyManager"; import { ExportsManager } from "../exports-manager"; import { ImportsManager } from "../imports-manager"; import { getReferenceToExportViaNamespaceImport } from "../referencing"; +import { + PassthroughFormat, + SerializationFormatType, + ZOD_VERSION, + ZodFormat, + ZURG_MANIFEST, + ZurgFormat +} from "../serialization-pipeline"; import { AuthImpl } from "./Auth"; import { CallbackQueueImpl } from "./CallbackQueue"; import { CoreUtilities } from "./CoreUtilities"; @@ -22,7 +30,6 @@ import { StreamImpl } from "./Stream"; import { UrlUtilsImpl } from "./UrlUtils"; import { UtilsImpl } from "./Utils"; import { WebsocketImpl } from "./Websocket"; -import { ZurgImpl } from "./Zurg"; export declare namespace CoreUtilitiesManager { namespace getCoreUtilities { @@ -51,6 +58,7 @@ export class CoreUtilitiesManager { private readonly relativePackagePath: string; private readonly relativeTestPath: string; private readonly generateEndpointMetadata: boolean; + private readonly serializationFormat: SerializationFormatType; constructor({ streamType, @@ -58,7 +66,8 @@ export class CoreUtilitiesManager { fetchSupport, relativePackagePath = DEFAULT_PACKAGE_PATH, relativeTestPath = DEFAULT_TEST_PATH, - generateEndpointMetadata + generateEndpointMetadata, + serializationFormat = "zurg" }: { streamType: "wrapper" | "web"; formDataSupport: "Node16" | "Node18"; @@ -66,6 +75,7 @@ export class CoreUtilitiesManager { relativePackagePath?: string; relativeTestPath?: string; generateEndpointMetadata: boolean; + serializationFormat?: SerializationFormatType; }) { this.streamType = streamType; this.formDataSupport = formDataSupport; @@ -73,6 +83,7 @@ export class CoreUtilitiesManager { this.relativePackagePath = relativePackagePath; this.relativeTestPath = relativeTestPath; this.generateEndpointMetadata = generateEndpointMetadata; + this.serializationFormat = serializationFormat; } public getCoreUtilities({ @@ -90,8 +101,11 @@ export class CoreUtilitiesManager { relativeTestPath }); + // Create the serialization format based on configuration + const serializationFormat = this.createSerializationFormat(getReferenceToExport, importsManager); + return { - zurg: new ZurgImpl({ getReferenceToExport, generateEndpointMetadata: this.generateEndpointMetadata }), + zurg: serializationFormat, fetcher: new FetcherImpl({ getReferenceToExport, generateEndpointMetadata: this.generateEndpointMetadata }), stream: new StreamImpl({ getReferenceToExport, generateEndpointMetadata: this.generateEndpointMetadata }), auth: new AuthImpl({ getReferenceToExport, generateEndpointMetadata: this.generateEndpointMetadata }), @@ -128,6 +142,35 @@ export class CoreUtilitiesManager { }; } + private createSerializationFormat( + getReferenceToExport: (args: { + manifest: CoreUtility.Manifest; + exportedName: string; + }) => ReturnType, + importsManager: ImportsManager + ) { + const config = { + getReferenceToExport, + generateEndpointMetadata: this.generateEndpointMetadata + }; + + switch (this.serializationFormat) { + case "zurg": + // Add Zurg manifest to referenced utilities so it gets copied + this.addManifestAndDependencies(ZURG_MANIFEST); + return new ZurgFormat(config, importsManager); + + case "zod": + return new ZodFormat(config, importsManager); + + case "none": + return new PassthroughFormat(config); + + default: + throw new Error(`Unknown serialization format: ${this.serializationFormat}`); + } + } + public finalize(exportsManager: ExportsManager, dependencyManager: DependencyManager): void { for (const utility of Object.values(this.referencedCoreUtilities)) { exportsManager.addExportsForDirectories( @@ -145,6 +188,20 @@ export class CoreUtilitiesManager { fetchSupport: this.fetchSupport }); } + + // Add runtime dependencies for serialization format + this.addSerializationDependencies(dependencyManager); + } + + /** + * Add npm dependencies required by the active serialization format + */ + private addSerializationDependencies(dependencyManager: DependencyManager): void { + if (this.serializationFormat === "zod") { + // Zod uses an npm dependency instead of bundled runtime files + dependencyManager.addDependency("zod", ZOD_VERSION); + } + // Zurg and Passthrough formats don't require external npm dependencies } public async copyCoreUtilities({ diff --git a/generators/typescript/utils/commons/src/core-utilities/Zurg.ts b/generators/typescript/utils/commons/src/core-utilities/Zurg.ts index b24d71afa05e..aa5bf754bae4 100644 --- a/generators/typescript/utils/commons/src/core-utilities/Zurg.ts +++ b/generators/typescript/utils/commons/src/core-utilities/Zurg.ts @@ -1,953 +1,27 @@ -import { ts } from "ts-morph"; +import { SerializationFormat } from "../serialization-pipeline/SerializationFormat"; -import { Reference } from "../referencing"; -import { CoreUtility } from "./CoreUtility"; +// Re-export types from SerializationFormat for backward compatibility +export type SchemaOptions = SerializationFormat.SchemaOptions; -export interface SchemaOptions { - unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; - allowUnrecognizedUnionMembers?: boolean; - allowUnrecognizedEnumValues?: boolean; - skipValidation?: boolean; - omitUndefined?: boolean; - breadcrumbsPrefix?: string[]; -} - -export interface Zurg { - object: (properties: Zurg.Property[]) => Zurg.ObjectSchema; - objectWithoutOptionalProperties: (properties: Zurg.Property[]) => Zurg.ObjectSchema; - union: (args: Zurg.union.Args) => Zurg.ObjectLikeSchema; - undiscriminatedUnion: (schemas: Zurg.Schema[]) => Zurg.Schema; - list: (itemSchema: Zurg.Schema) => Zurg.Schema; - set: (itemSchema: Zurg.Schema) => Zurg.Schema; - record: (args: { keySchema: Zurg.Schema; valueSchema: Zurg.Schema }) => Zurg.Schema; - enum: (values: string[]) => Zurg.Schema; - string: () => Zurg.Schema; - stringLiteral: (literal: string) => Zurg.Schema; - booleanLiteral: (literal: boolean) => Zurg.Schema; - date: () => Zurg.Schema; - number: () => Zurg.Schema; - bigint: () => Zurg.Schema; - boolean: () => Zurg.Schema; - any: () => Zurg.Schema; - unknown: () => Zurg.Schema; - never: () => Zurg.Schema; - lazy: (schema: Zurg.Schema) => Zurg.Schema; - lazyObject: (schema: Zurg.Schema) => Zurg.ObjectSchema; - - Schema: { - _getReferenceToType: (args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => ts.TypeNode; - _fromExpression: (expression: ts.Expression, opts?: { isObject: boolean }) => Zurg.Schema; - _visitMaybeValid: ( - referenceToMaybeValid: ts.Expression, - visitor: { - valid: (referenceToValue: ts.Expression) => ts.Statement[]; - invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; - } - ) => ts.Statement[]; - }; - - ObjectSchema: { - _getReferenceToType: (args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => ts.TypeNode; - }; - - MaybeValid: { - ok: "ok"; - - Valid: { - value: "value"; - }; - - Invalid: { - errors: "errors"; - }; - }; - - ValidationError: { - path: "path"; - message: "message"; - }; -} +/** + * Zurg is now a type alias for SerializationFormat. + * This maintains backward compatibility while allowing different serialization formats to be used. + */ +export type Zurg = SerializationFormat; +/** + * Zurg namespace for backward compatibility with existing code. + * All types are aliased to their SerializationFormat equivalents. + */ export declare namespace Zurg { - interface Schema extends BaseSchema, SchemaUtils {} - - interface BaseSchema { - toExpression: () => ts.Expression; - isOptional: boolean; - isNullable: boolean; - } - - interface SchemaUtils { - parse: (raw: ts.Expression, opts: Required) => ts.Expression; - json: (parsed: ts.Expression, opts: Required) => ts.Expression; - parseOrThrow: (raw: ts.Expression, opts: Required) => ts.Expression; - jsonOrThrow: (parsed: ts.Expression, opts: Required) => ts.Expression; - nullable: () => Zurg.Schema; - optional: () => Zurg.Schema; - optionalNullable: () => Zurg.Schema; - transform: (args: { - newShape: ts.TypeNode | undefined; - transform: ts.Expression; - untransform: ts.Expression; - }) => Zurg.Schema; - } - - interface ObjectLikeSchema extends Schema, ObjectLikeUtils {} - - interface ObjectLikeUtils { - withParsedProperties: (properties: Zurg.AdditionalProperty[]) => Zurg.ObjectLikeSchema; - } - - interface AdditionalProperty { - key: string; - getValue: (args: { getReferenceToParsed: () => ts.Expression }) => ts.Expression; - } - - interface ObjectSchema extends Schema, ObjectLikeUtils, ObjectUtils {} - - interface ObjectUtils { - extend: (extension: Zurg.Schema) => ObjectSchema; - passthrough: () => ObjectSchema; - } - - interface Property { - key: { - parsed: string; - raw: string; - }; - value: Schema; - } - - namespace union { - interface Args { - parsedDiscriminant: string; - rawDiscriminant: string; - singleUnionTypes: Zurg.union.SingleUnionType[]; - } - - interface SingleUnionType { - discriminantValue: string; - nonDiscriminantProperties: Zurg.ObjectSchema; - } - } -} - -export const MANIFEST: CoreUtility.Manifest = { - name: "schemas", - pathInCoreUtilities: { nameOnDisk: "schemas", exportDeclaration: { namespaceExport: "serialization" } }, - getFilesPatterns: () => { - return { patterns: ["src/core/schemas/**", "tests/unit/schemas/**"] }; - } -}; -export class ZurgImpl extends CoreUtility implements Zurg { - public readonly MANIFEST = MANIFEST; - - public object = this.withExportedName("object", (object) => (properties: Zurg.Property[]): Zurg.ObjectSchema => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(object.getExpression(), undefined, [ - this.constructObjectLiteralForProperties(properties) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - }); - - public objectWithoutOptionalProperties = this.withExportedName( - "objectWithoutOptionalProperties", - (objectWithoutOptionalProperties) => - (properties: Zurg.Property[]): Zurg.ObjectSchema => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(objectWithoutOptionalProperties.getExpression(), undefined, [ - this.constructObjectLiteralForProperties(properties) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - } - ); - - private constructObjectLiteralForProperties(properties: Zurg.Property[]): ts.ObjectLiteralExpression { - return ts.factory.createObjectLiteralExpression( - properties.map((property) => { - let value = property.value.toExpression(); - if (property.key.raw !== property.key.parsed) { - value = this.property(property.key.raw, value); - } - return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(property.key.parsed), value); - }), - true - ); - } - - private getObjectUtils(objectSchema: Zurg.BaseSchema): Zurg.ObjectUtils { - return { - extend: (extension) => this.extend(objectSchema, extension), - passthrough: () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - objectSchema.toExpression(), - ts.factory.createIdentifier("passthrough") - ), - undefined, - [] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - } - }; - } - - private extend(objectSchema: Zurg.BaseSchema, extension: Zurg.BaseSchema): Zurg.ObjectSchema { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - objectSchema.toExpression(), - ts.factory.createIdentifier("extend") - ), - undefined, - [extension.toExpression()] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - } - - private property = this.withExportedName( - "property", - (property) => - (rawValue: string, value: ts.Expression): ts.Expression => { - return ts.factory.createCallExpression(property.getExpression(), undefined, [ - ts.factory.createStringLiteral(rawValue), - value - ]); - } - ); - - private getObjectLikeUtils(objectLike: Zurg.BaseSchema): Zurg.ObjectLikeUtils { - return { - withParsedProperties: (additionalProperties: Zurg.AdditionalProperty[]) => - this.withParsedProperties(objectLike, additionalProperties) - }; - } - - private withParsedProperties( - objectLike: Zurg.BaseSchema, - additionalProperties: Zurg.AdditionalProperty[] - ): Zurg.ObjectLikeSchema { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - objectLike.toExpression(), - ts.factory.createIdentifier("withParsedProperties") - ), - undefined, - [ - ts.factory.createObjectLiteralExpression( - additionalProperties.map((property) => { - const parsedIdentifier = ts.factory.createIdentifier("parsed"); - const context = { didAccessParsed: false }; - const getReferenceToParsed = () => { - context.didAccessParsed = true; - return parsedIdentifier; - }; - - const value = property.getValue({ getReferenceToParsed }); - - return ts.factory.createPropertyAssignment( - property.key, - context.didAccessParsed - ? ts.factory.createArrowFunction( - undefined, - undefined, - [ - ts.factory.createParameterDeclaration( - undefined, - undefined, - undefined, - parsedIdentifier - ) - ], - undefined, - undefined, - value - ) - : ts.isCallExpression(value) - ? ts.factory.createArrowFunction( - undefined, - undefined, - [], - undefined, - undefined, - value - ) - : value - ); - }), - true - ) - ] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema) - }; - } - - public union = this.withExportedName( - "union", - (union: Reference) => - ({ parsedDiscriminant, rawDiscriminant, singleUnionTypes }: Zurg.union.Args): Zurg.ObjectLikeSchema => { - const discriminantArgument = - parsedDiscriminant === rawDiscriminant - ? ts.factory.createStringLiteral(parsedDiscriminant) - : this.discriminant({ parsedDiscriminant, rawDiscriminant }); - - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(union.getExpression(), undefined, [ - discriminantArgument, - ts.factory.createObjectLiteralExpression( - singleUnionTypes.map((singleUnionType) => - ts.factory.createPropertyAssignment( - ts.factory.createStringLiteral(singleUnionType.discriminantValue), - singleUnionType.nonDiscriminantProperties.toExpression() - ) - ), - true - ) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema) - }; - } - ); - - private discriminant = this.withExportedName( - "discriminant", - (discriminant) => - ({ - parsedDiscriminant, - rawDiscriminant - }: { - parsedDiscriminant: string; - rawDiscriminant: string; - }): ts.Expression => { - return ts.factory.createCallExpression(discriminant.getExpression(), undefined, [ - ts.factory.createStringLiteral(parsedDiscriminant), - ts.factory.createStringLiteral(rawDiscriminant) - ]); - } - ); - - public undiscriminatedUnion = this.withExportedName( - "undiscriminatedUnion", - (undiscriminatedUnion: Reference) => (schemas: Zurg.Schema[]) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(undiscriminatedUnion.getExpression(), undefined, [ - ts.factory.createArrayLiteralExpression(schemas.map((schema) => schema.toExpression())) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - ); + export type Schema = SerializationFormat.SchemaWithUtils; + export type ObjectSchema = SerializationFormat.ObjectSchema; + export type ObjectLikeSchema = SerializationFormat.ObjectLikeSchema; + export type AdditionalProperty = SerializationFormat.AdditionalProperty; + export type Property = SerializationFormat.Property; - public list = this.withExportedName("list", (list: Reference) => (itemSchema: Zurg.Schema) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(list.getExpression(), undefined, [itemSchema.toExpression()]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public set = this.withExportedName("set", (set: Reference) => (itemSchema: Zurg.Schema) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(set.getExpression(), undefined, [itemSchema.toExpression()]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public record = this.withExportedName( - "record", - (record: Reference) => - ({ keySchema, valueSchema }: { keySchema: Zurg.Schema; valueSchema: Zurg.Schema }) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(record.getExpression(), undefined, [ - keySchema.toExpression(), - valueSchema.toExpression() - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - ); - - public enum = this.withExportedName("enum_", (enum_: Reference) => (values: string[]) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(enum_.getExpression(), undefined, [ - ts.factory.createArrayLiteralExpression( - values.map((value) => ts.factory.createStringLiteral(value)) - ) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public string = this.withExportedName("string", (string: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(string.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public stringLiteral = this.withExportedName("stringLiteral", (stringLiteral: Reference) => (literal: string) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(stringLiteral.getExpression(), undefined, [ - ts.factory.createStringLiteral(literal) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public booleanLiteral = this.withExportedName( - "booleanLiteral", - (booleanLiteral: Reference) => (literal: boolean) => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression(booleanLiteral.getExpression(), undefined, [ - literal ? ts.factory.createTrue() : ts.factory.createFalse() - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - ); - - public number = this.withExportedName("number", (number: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(number.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public bigint = this.withExportedName("bigint", (bigint: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(bigint.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public boolean = this.withExportedName("boolean", (boolean: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(boolean.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public date = this.withExportedName("date", (date: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(date.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public any = this.withExportedName("any", (any: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: true, - toExpression: () => ts.factory.createCallExpression(any.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public unknown = this.withExportedName("unknown", (unknown: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: true, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(unknown.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public never = this.withExportedName("never", (never: Reference) => () => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => ts.factory.createCallExpression(never.getExpression(), undefined, undefined) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - private getSchemaUtils(baseSchema: Zurg.BaseSchema): Zurg.SchemaUtils { - return { - nullable: () => this.nullable(baseSchema), - optional: () => this.optional(baseSchema), - optionalNullable: () => this.optionalNullable(baseSchema), - parse: (raw, opts) => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "parse"), - undefined, - [raw, ...this.constructSchemaOptionsArgs(opts)] - ), - json: (parsed, opts) => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "json"), - undefined, - [parsed, ...this.constructSchemaOptionsArgs(opts)] - ), - parseOrThrow: (raw, opts) => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "parseOrThrow"), - undefined, - [raw, ...this.constructSchemaOptionsArgs(opts)] - ), - jsonOrThrow: (parsed, opts) => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "jsonOrThrow"), - undefined, - [parsed, ...this.constructSchemaOptionsArgs(opts)] - ), - transform: ({ - newShape, - transform, - untransform - }: { - newShape: ts.TypeNode | undefined; - transform: ts.Expression; - untransform: ts.Expression; - }) => - this.transform(baseSchema, { - newShape, - transformer: this.Schema._fromTransformers({ transform, untransform }) - }) - }; + export namespace union { + export type Args = SerializationFormat.UnionArgs; + export type SingleUnionType = SerializationFormat.SingleUnionType; } - - private constructSchemaOptionsArgs(schemaOptions: Required): ts.Expression[] { - const properties: ts.ObjectLiteralElementLike[] = []; - - if (schemaOptions.unrecognizedObjectKeys !== "fail") { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("unrecognizedObjectKeys"), - ts.factory.createStringLiteral(schemaOptions.unrecognizedObjectKeys) - ) - ); - } - if (schemaOptions.allowUnrecognizedUnionMembers) { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("allowUnrecognizedUnionMembers"), - ts.factory.createTrue() - ) - ); - } - if (schemaOptions.allowUnrecognizedEnumValues) { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("allowUnrecognizedEnumValues"), - ts.factory.createTrue() - ) - ); - } - if (schemaOptions.skipValidation) { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("skipValidation"), - ts.factory.createTrue() - ) - ); - } - if (schemaOptions.omitUndefined) { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("omitUndefined"), - ts.factory.createTrue() - ) - ); - } - if (schemaOptions.breadcrumbsPrefix.length > 0) { - properties.push( - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier("breadcrumbsPrefix"), - ts.factory.createArrayLiteralExpression( - schemaOptions.breadcrumbsPrefix.map((breadcrumb) => ts.factory.createStringLiteral(breadcrumb)) - ) - ) - ); - } - - if (properties.length > 0) { - return [ts.factory.createObjectLiteralExpression(properties)]; - } else { - return []; - } - } - - private nullable(schema: Zurg.BaseSchema): Zurg.Schema { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: true, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - schema.toExpression(), - ts.factory.createIdentifier("nullable") - ), - undefined, - [] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - - private optional(schema: Zurg.BaseSchema): Zurg.Schema { - const baseSchema: Zurg.BaseSchema = { - isOptional: true, - isNullable: false, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - schema.toExpression(), - ts.factory.createIdentifier("optional") - ), - undefined, - [] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - - private optionalNullable(schema: Zurg.BaseSchema): Zurg.Schema { - const baseSchema: Zurg.BaseSchema = { - isOptional: true, - isNullable: true, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - schema.toExpression(), - ts.factory.createIdentifier("optionalNullable") - ), - undefined, - [] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - - private transform( - schema: Zurg.BaseSchema, - { newShape, transformer }: { newShape: ts.TypeNode | undefined; transformer: Zurg.BaseSchema } - ): Zurg.Schema { - const baseSchema: Zurg.BaseSchema = { - isOptional: transformer.isOptional, - isNullable: transformer.isNullable, - toExpression: () => - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - schema.toExpression(), - ts.factory.createIdentifier("transform") - ), - newShape != null ? [newShape] : undefined, - [transformer.toExpression()] - ) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - } - - public lazy = this.withExportedName("lazy", (lazy) => (schema: Zurg.Schema): Zurg.Schema => { - const baseSchema: Zurg.BaseSchema = { - isOptional: schema.isOptional, - isNullable: schema.isNullable, - toExpression: () => - ts.factory.createCallExpression(lazy.getExpression(), undefined, [ - ts.factory.createArrowFunction([], undefined, [], undefined, undefined, schema.toExpression()) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }); - - public lazyObject = this.withExportedName( - "lazyObject", - (lazyObject) => - (schema: Zurg.Schema): Zurg.ObjectSchema => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: schema.isNullable, - toExpression: () => - ts.factory.createCallExpression(lazyObject.getExpression(), undefined, [ - ts.factory.createArrowFunction( - [], - undefined, - [], - undefined, - undefined, - schema.toExpression() - ) - ]) - }; - - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - } - ); - - public Schema = { - _getReferenceToType: this.withExportedName( - "Schema", - (Schema) => - ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => - ts.factory.createTypeReferenceNode(Schema.getEntityName(), [rawShape, parsedShape]) - ), - - _fromExpression: (expression: ts.Expression, opts?: { isObject: boolean }): Zurg.Schema => { - const baseSchema: Zurg.BaseSchema = { - isOptional: false, - isNullable: false, - toExpression: () => expression - }; - if (opts?.isObject) { - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema), - ...this.getObjectLikeUtils(baseSchema), - ...this.getObjectUtils(baseSchema) - }; - } - return { - ...baseSchema, - ...this.getSchemaUtils(baseSchema) - }; - }, - - _fromTransformers: ({ - transform, - untransform - }: { - transform: ts.Expression; - untransform: ts.Expression; - }): Zurg.Schema => { - return this.Schema._fromExpression( - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment("transform", transform), - ts.factory.createPropertyAssignment("untransform", untransform) - ], - true - ) - ); - }, - - _visitMaybeValid: ( - referenceToMaybeValid: ts.Expression, - visitor: { - valid: (referenceToValue: ts.Expression) => ts.Statement[]; - invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; - } - ): ts.Statement[] => { - return [ - ts.factory.createIfStatement( - ts.factory.createPropertyAccessExpression(referenceToMaybeValid, this.MaybeValid.ok), - ts.factory.createBlock( - visitor.valid( - ts.factory.createPropertyAccessExpression( - referenceToMaybeValid, - this.MaybeValid.Valid.value - ) - ), - true - ), - ts.factory.createBlock( - visitor.invalid( - ts.factory.createPropertyAccessExpression( - referenceToMaybeValid, - this.MaybeValid.Invalid.errors - ) - ), - true - ) - ) - ]; - } - }; - - public ObjectSchema = { - _getReferenceToType: this.withExportedName( - "ObjectSchema", - (ObjectSchema) => - ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => - ts.factory.createTypeReferenceNode(ObjectSchema.getEntityName(), [rawShape, parsedShape]) - ) - }; - - public MaybeValid = { - ok: "ok" as const, - Valid: { - value: "value" as const - }, - Invalid: { - errors: "errors" as const - } - }; - - public ValidationError = { - path: "path" as const, - message: "message" as const - }; } diff --git a/generators/typescript/utils/commons/src/index.ts b/generators/typescript/utils/commons/src/index.ts index e0bd50858eb2..62ba92fa214f 100644 --- a/generators/typescript/utils/commons/src/index.ts +++ b/generators/typescript/utils/commons/src/index.ts @@ -33,6 +33,7 @@ export { type PackageId } from "./PackageId"; export * from "./public-exports-manager"; export * from "./referencing"; export { removeUndefinedAndNullFromTypeNode } from "./removeUndefinedAndNullFromTypeNode"; +export * from "./serialization-pipeline"; export { type TypeReferenceNode } from "./TypeReferenceNode"; export * from "./typescript-project"; export { convertJestImportsToVitest } from "./typescript-project/convertJestImportsToVitest"; diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/SerializationFormat.ts b/generators/typescript/utils/commons/src/serialization-pipeline/SerializationFormat.ts new file mode 100644 index 000000000000..b24c231b1cdf --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/SerializationFormat.ts @@ -0,0 +1,386 @@ +import { ts } from "ts-morph"; + +import { Reference } from "../referencing"; + +/** + * The main serialization format protocol. Describes how an object goes from the internal Fern format to generated ASTs. + * To add new supported serialization options, conform to this protocol. + */ +export interface SerializationFormat { + /** + * Unique identifier for this format + */ + readonly name: "zurg" | "zod" | "none"; + + // ==================== Schema Builders ==================== + + /** + * Create an object schema with the given properties + */ + object: (properties: SerializationFormat.Property[]) => SerializationFormat.ObjectSchema; + + /** + * Create an object schema where all properties are required (no optionals) + */ + objectWithoutOptionalProperties: (properties: SerializationFormat.Property[]) => SerializationFormat.ObjectSchema; + + /** + * Create a discriminated union schema + */ + union: (args: SerializationFormat.UnionArgs) => SerializationFormat.ObjectLikeSchema; + + /** + * Create an undiscriminated union schema + */ + undiscriminatedUnion: (schemas: SerializationFormat.Schema[]) => SerializationFormat.SchemaWithUtils; + + /** + * Create an array/list schema + */ + list: (itemSchema: SerializationFormat.Schema) => SerializationFormat.SchemaWithUtils; + + /** + * Create a Set schema + */ + set: (itemSchema: SerializationFormat.Schema) => SerializationFormat.SchemaWithUtils; + + /** + * Create a Record/Map schema + */ + record: (args: { + keySchema: SerializationFormat.Schema; + valueSchema: SerializationFormat.Schema; + }) => SerializationFormat.SchemaWithUtils; + + /** + * Create an enum schema + */ + enum: (values: string[]) => SerializationFormat.SchemaWithUtils; + + // ==================== Primitive Schemas ==================== + + /** + * Create a string schema + */ + string: () => SerializationFormat.SchemaWithUtils; + + /** + * Create a string literal schema + */ + stringLiteral: (literal: string) => SerializationFormat.SchemaWithUtils; + + /** + * Create a boolean literal schema + */ + booleanLiteral: (literal: boolean) => SerializationFormat.SchemaWithUtils; + + /** + * Create a date schema (parses ISO strings to Date objects) + */ + date: () => SerializationFormat.SchemaWithUtils; + + /** + * Create a number schema + */ + number: () => SerializationFormat.SchemaWithUtils; + + /** + * Create a bigint schema + */ + bigint: () => SerializationFormat.SchemaWithUtils; + + /** + * Create a boolean schema + */ + boolean: () => SerializationFormat.SchemaWithUtils; + + /** + * Create an any schema (allows any value) + */ + any: () => SerializationFormat.SchemaWithUtils; + + /** + * Create an unknown schema + */ + unknown: () => SerializationFormat.SchemaWithUtils; + + /** + * Create a never schema (always fails validation) + */ + never: () => SerializationFormat.SchemaWithUtils; + + // ==================== Schema Wrappers ==================== + + /** + * Create a lazy schema for recursive types + */ + lazy: (schema: SerializationFormat.Schema) => SerializationFormat.SchemaWithUtils; + + /** + * Create a lazy object schema for recursive object types + */ + lazyObject: (schema: SerializationFormat.Schema) => SerializationFormat.ObjectSchema; + + // ==================== Type Utilities ==================== + + /** + * Schema type utilities + */ + Schema: { + /** + * Get the TypeScript type reference for a schema type + */ + _getReferenceToType: (args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => ts.TypeNode; + + /** + * Create a schema from a raw expression + */ + _fromExpression: ( + expression: ts.Expression, + opts?: { isObject: boolean } + ) => SerializationFormat.SchemaWithUtils; + + /** + * Generate if/else statements for handling MaybeValid results + */ + _visitMaybeValid: ( + referenceToMaybeValid: ts.Expression, + visitor: { + valid: (referenceToValue: ts.Expression) => ts.Statement[]; + invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; + } + ) => ts.Statement[]; + }; + + /** + * ObjectSchema type utilities + */ + ObjectSchema: { + /** + * Get the TypeScript type reference for an object schema type + */ + _getReferenceToType: (args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => ts.TypeNode; + }; + + /** + * MaybeValid result type field names + */ + MaybeValid: { + ok: string; + Valid: { + value: string; + }; + Invalid: { + errors: string; + }; + }; + + /** + * ValidationError field names + */ + ValidationError: { + path: string; + message: string; + }; + + // ==================== Runtime Configuration ==================== + + /** + * Get npm dependencies required by this format's runtime + * Returns empty object if no npm dependencies needed + */ + getRuntimeDependencies: () => Record; + + /** + * Get file patterns for runtime files to copy into generated SDK + * Returns null if using npm dependency instead of bundled files + */ + getRuntimeFilePatterns: () => { patterns: string[]; ignore?: string[] } | null; +} + +/** + * Namespace containing all types related to SerializationFormat. + * Use these types like: SerializationFormat.Schema, SerializationFormat.Property, etc. + */ +export namespace SerializationFormat { + /** + * Options passed to schema parse/json operations + */ + export interface SchemaOptions { + unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; + allowUnrecognizedUnionMembers?: boolean; + allowUnrecognizedEnumValues?: boolean; + skipValidation?: boolean; + omitUndefined?: boolean; + breadcrumbsPrefix?: string[]; + } + + /** + * Base schema interface that all format-specific schemas must implement. + * This represents a schema that can generate TypeScript AST expressions. + */ + export interface Schema { + /** + * Generates the TypeScript AST expression for this schema definition + */ + toExpression: () => ts.Expression; + + /** + * Whether this schema represents an optional value + */ + isOptional: boolean; + + /** + * Whether this schema represents a nullable value + */ + isNullable: boolean; + + /** + * Optional: Generate expression to serialize parsed value to JSON. + * If not provided, value passes through unchanged. + * Used for types that need transformation during serialization (Set → Array, etc.) + */ + toJsonExpression?: (parsed: ts.Expression) => ts.Expression; + } + + /** + * Extended schema interface with utility methods for transformations + */ + export interface SchemaWithUtils extends Schema { + /** + * Generate parse expression: raw JSON -> parsed type + */ + parse: (raw: ts.Expression, opts: Required) => ts.Expression; + + /** + * Generate json expression: parsed type -> raw JSON + */ + json: (parsed: ts.Expression, opts: Required) => ts.Expression; + + /** + * Generate parseOrThrow expression + */ + parseOrThrow: (raw: ts.Expression, opts: Required) => ts.Expression; + + /** + * Generate jsonOrThrow expression + */ + jsonOrThrow: (parsed: ts.Expression, opts: Required) => ts.Expression; + + /** + * Wrap schema to allow null values + */ + nullable: () => SchemaWithUtils; + + /** + * Wrap schema to allow undefined values + */ + optional: () => SchemaWithUtils; + + /** + * Wrap schema to allow both null and undefined values + */ + optionalNullable: () => SchemaWithUtils; + + /** + * Apply a transform to the schema + */ + transform: (args: { + newShape: ts.TypeNode | undefined; + transform: ts.Expression; + untransform: ts.Expression; + }) => SchemaWithUtils; + } + + /** + * Additional property to be added during parsing + */ + export interface AdditionalProperty { + key: string; + getValue: (args: { getReferenceToParsed: () => ts.Expression }) => ts.Expression; + } + + /** + * Schema interface for object-like types (objects, unions) + */ + export interface ObjectLikeSchema extends SchemaWithUtils { + /** + * Add computed properties to the parsed output + */ + withParsedProperties: (properties: AdditionalProperty[]) => ObjectLikeSchema; + } + + /** + * Schema interface for object types with extend/passthrough capabilities + */ + export interface ObjectSchema extends ObjectLikeSchema { + /** + * Extend this object schema with another schema's properties + */ + extend: (extension: Schema) => ObjectSchema; + + /** + * Allow unknown properties to pass through + */ + passthrough: () => ObjectSchema; + } + + /** + * Property definition for object schemas + */ + export interface Property { + key: { + /** The property name in the parsed TypeScript type */ + parsed: string; + /** The property name in the raw JSON */ + raw: string; + }; + value: Schema; + } + + /** + * A single variant of a discriminated union + */ + export interface SingleUnionType { + /** The discriminant value that identifies this variant */ + discriminantValue: string; + /** Schema for the non-discriminant properties */ + nonDiscriminantProperties: ObjectSchema; + } + + /** + * Arguments for creating a discriminated union schema + */ + export interface UnionArgs { + /** The discriminant property name in the parsed type */ + parsedDiscriminant: string; + /** The discriminant property name in the raw JSON */ + rawDiscriminant: string; + /** The union member types */ + singleUnionTypes: SingleUnionType[]; + } + + /** + * Runtime dependencies required by the serialization format. + * Note this is required for the legacy zurg integration until it can be fully refactored. + */ + export interface RuntimeDependency { + name: string; + version: string; + } + + /** + * Configuration for creating a serialization format + */ + export interface Config { + /** + * Function to get a reference to an exported name from the format's module + */ + getReferenceToExport: (args: { manifest: any; exportedName: string }) => Reference; + + /** + * Whether to generate endpoint metadata + */ + generateEndpointMetadata: boolean; + } +} diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/SerializationPipeline.ts b/generators/typescript/utils/commons/src/serialization-pipeline/SerializationPipeline.ts new file mode 100644 index 000000000000..665add231eb6 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/SerializationPipeline.ts @@ -0,0 +1,131 @@ +import { PassthroughFormat } from "./formats/PassthroughFormat"; +import { ZodFormat } from "./formats/ZodFormat"; +import { ZurgFormat } from "./formats/ZurgFormat"; +import { SerializationFormat } from "./SerializationFormat"; + +/** + * Supported serialization format types + */ +export type SerializationFormatType = "zurg" | "zod" | "none"; + +/** + * Configuration for creating a SerializationPipeline + */ +export interface SerializationPipelineConfig extends SerializationFormat.Config { + /** + * The serialization format to use. + * - "zurg": Use Zurg (bundled runtime) + * - "zod": Use Zod (npm dependency) + * - "none": No serialization, data passes through unchanged + */ + format: SerializationFormatType; +} + +/** + * SerializationPipeline is an abstraction around various serialization formats. It provides a unified interface + * for generating serialization schema AST code regardless of the underlying format being used. + */ +export class SerializationPipeline { + /** + * The concrete format implementation. + */ + private readonly format: SerializationFormat; + + /** + * The type of the selected serialization format. + */ + private readonly formatType: SerializationFormatType; + + /** + * Create a new SerializationPipeline instance. + * @param config - The configuration for the pipeline. + */ + constructor(config: SerializationPipelineConfig) { + this.formatType = config.format; + this.format = this.createFormat(config); + } + + /** + * Given a pipeline configuration, generate the appropriate serialization format. + */ + private createFormat(config: SerializationPipelineConfig): SerializationFormat { + switch (config.format) { + case "zurg": + return new ZurgFormat(config); + + case "zod": + return new ZodFormat(config); + + case "none": + return new PassthroughFormat(config); + + default: + throw new Error(`Unknown serialization format: ${config.format}`); + } + } + + /** + * Get the active serialization format + */ + public getFormat(): SerializationFormat { + return this.format; + } + + /** + * Get the format type string + */ + public getFormatType(): SerializationFormatType { + return this.formatType; + } + + /** + * Check if serialization is enabled i.e. not passthrough. + */ + public isEnabled(): boolean { + return this.formatType !== "none"; + } + + /** + * Check if the serialization format is passthrough. + */ + public isPassthrough(): boolean { + return this.formatType === "none"; + } + + /** + * Get runtime dependencies required by the active format. + * Note: only used for legacy zurg format until it can be refactored into a npm dependency. + */ + public getRuntimeDependencies(): Record { + return this.format.getRuntimeDependencies(); + } + + /** + * Get runtime file patterns for the active format. + * Used only for legacy zurg format until it can be refactored into a npm dependency. + */ + public getRuntimeFilePatterns(): { patterns: string[]; ignore?: string[] } | null { + return this.format.getRuntimeFilePatterns(); + } + + /** + * Helper to determine the format type from legacy noSerdeLayer config. Helps with backwards compatibility. + */ + public static resolveFormatType(options: { + serializationFormat?: SerializationFormatType; + noSerdeLayer?: boolean; + }): SerializationFormatType { + // If explicit format is provided, use it + if (options.serializationFormat != null) { + return options.serializationFormat; + } + + // Fall back to noSerdeLayer logic for backward compatibility + if (options.noSerdeLayer === true) { + return "none"; + } + + // Default to Zurg + return "zurg"; + } +} diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/SerializationPipeline.test.ts b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/SerializationPipeline.test.ts new file mode 100644 index 000000000000..7281a23bd309 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/SerializationPipeline.test.ts @@ -0,0 +1,216 @@ +import { ts } from "ts-morph"; +import { describe, expect, it } from "vitest"; + +import { Reference } from "../../referencing"; +import { PassthroughFormat } from "../formats/PassthroughFormat"; +import { ZodFormat } from "../formats/ZodFormat"; +import { ZurgFormat } from "../formats/ZurgFormat"; +import { SerializationPipeline } from "../SerializationPipeline"; + +/** + * Create a mock reference for testing + */ +function createMockReference(exportedName: string): Reference { + return { + getExpression: () => { + return ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + }, + getTypeNode: () => { + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ), + undefined + ); + }, + getEntityName: () => { + return ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + } + }; +} + +describe("SerializationPipeline", () => { + const baseConfig = { + getReferenceToExport: ({ exportedName }: { manifest: any; exportedName: string }) => + createMockReference(exportedName), + generateEndpointMetadata: false + }; + + describe("format creation", () => { + it("creates ZurgFormat for 'default'", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zurg" + }); + + expect(pipeline.getFormat()).toBeInstanceOf(ZurgFormat); + expect(pipeline.getFormatType()).toBe("zurg"); + expect(pipeline.isEnabled()).toBe(true); + }); + + it("creates PassthroughFormat for 'none'", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "none" + }); + + expect(pipeline.getFormat()).toBeInstanceOf(PassthroughFormat); + expect(pipeline.getFormatType()).toBe("none"); + expect(pipeline.isEnabled()).toBe(false); + }); + + it("creates ZodFormat for 'zod'", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zod" + }); + + expect(pipeline.getFormat()).toBeInstanceOf(ZodFormat); + expect(pipeline.getFormatType()).toBe("zod"); + expect(pipeline.isEnabled()).toBe(true); + }); + }); + + describe("resolveFormatType", () => { + it("returns explicit serializationFormat when provided", () => { + expect( + SerializationPipeline.resolveFormatType({ + serializationFormat: "zurg" + }) + ).toBe("zurg"); + + expect( + SerializationPipeline.resolveFormatType({ + serializationFormat: "none" + }) + ).toBe("none"); + + expect( + SerializationPipeline.resolveFormatType({ + serializationFormat: "zod" + }) + ).toBe("zod"); + }); + + it("returns 'none' when noSerdeLayer is true", () => { + expect( + SerializationPipeline.resolveFormatType({ + noSerdeLayer: true + }) + ).toBe("none"); + }); + + it("returns 'default' when noSerdeLayer is false", () => { + expect( + SerializationPipeline.resolveFormatType({ + noSerdeLayer: false + }) + ).toBe("zurg"); + }); + + it("returns 'default' when nothing is specified", () => { + expect(SerializationPipeline.resolveFormatType({})).toBe("zurg"); + }); + + it("serializationFormat takes precedence over noSerdeLayer", () => { + // Even with noSerdeLayer: true, explicit format wins + expect( + SerializationPipeline.resolveFormatType({ + serializationFormat: "zurg", + noSerdeLayer: true + }) + ).toBe("zurg"); + + // And vice versa + expect( + SerializationPipeline.resolveFormatType({ + serializationFormat: "none", + noSerdeLayer: false + }) + ).toBe("none"); + }); + }); + + describe("runtime configuration", () => { + it("ZurgFormat returns no npm dependencies", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zurg" + }); + + expect(pipeline.getRuntimeDependencies()).toEqual({}); + }); + + it("ZurgFormat returns file patterns for bundled runtime", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zurg" + }); + + const patterns = pipeline.getRuntimeFilePatterns(); + expect(patterns).not.toBeNull(); + expect(patterns?.patterns).toContain("src/core/schemas/**"); + }); + + it("PassthroughFormat returns no dependencies and no files", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "none" + }); + + expect(pipeline.getRuntimeDependencies()).toEqual({}); + expect(pipeline.getRuntimeFilePatterns()).toBeNull(); + }); + + it("ZodFormat returns zod npm dependency and no files", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zod" + }); + + const deps = pipeline.getRuntimeDependencies(); + expect(deps).toHaveProperty("zod"); + expect(pipeline.getRuntimeFilePatterns()).toBeNull(); + }); + }); + + describe("schema generation through pipeline", () => { + it("ZurgFormat generates valid schema expressions", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "zurg" + }); + + const format = pipeline.getFormat(); + const schema = format.object([{ key: { parsed: "name", raw: "name" }, value: format.string() }]); + + const expr = schema.toExpression(); + expect(expr).toBeDefined(); + + // Verify it's a valid call expression + expect(ts.isCallExpression(expr)).toBe(true); + }); + + it("PassthroughFormat generates no-op expressions", () => { + const pipeline = new SerializationPipeline({ + ...baseConfig, + format: "none" + }); + + const format = pipeline.getFormat(); + const schema = format.object([{ key: { parsed: "name", raw: "name" }, value: format.string() }]); + + const expr = schema.toExpression(); + expect(expr).toBeDefined(); + // PassthroughFormat returns identifier "undefined" + expect(ts.isIdentifier(expr)).toBe(true); + }); + }); +}); diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZodFormat.test.ts b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZodFormat.test.ts new file mode 100644 index 000000000000..4fcc84f424e1 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZodFormat.test.ts @@ -0,0 +1,437 @@ +import { ts } from "ts-morph"; +import { describe, expect, it } from "vitest"; + +import { Reference } from "../../referencing"; +import { ZodFormat } from "../formats/ZodFormat"; + +/** + * Helper to print TypeScript AST to string for comparison + */ +function printNode(node: ts.Node): string { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const sourceFile = ts.createSourceFile("test.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); + return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); +} + +/** + * Create a mock reference for testing + */ +function createMockReference(exportedName: string): Reference { + return { + getExpression: () => { + return ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("z"), + ts.factory.createIdentifier(exportedName) + ); + }, + getTypeNode: () => { + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier("z"), + ts.factory.createIdentifier(exportedName) + ), + undefined + ); + }, + getEntityName: () => { + return ts.factory.createQualifiedName( + ts.factory.createIdentifier("z"), + ts.factory.createIdentifier(exportedName) + ); + } + }; +} + +/** + * Create a ZodFormat instance for testing + */ +function createZod(): ZodFormat { + return new ZodFormat({ + getReferenceToExport: ({ exportedName }) => createMockReference(exportedName), + generateEndpointMetadata: false + }); +} + +describe("ZodFormat AST Generation", () => { + const zod = createZod(); + + describe("Primitive Schemas", () => { + it("string() generates z.string()", () => { + const schema = zod.string(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("number() generates z.number()", () => { + const schema = zod.number(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("boolean() generates z.boolean()", () => { + const schema = zod.boolean(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("bigint() generates z.bigint()", () => { + const schema = zod.bigint(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("date() generates z.string().transform()", () => { + const schema = zod.date(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("any() generates z.any()", () => { + const schema = zod.any(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("unknown() generates z.unknown()", () => { + const schema = zod.unknown(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("never() generates z.never()", () => { + const schema = zod.never(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Literal Schemas", () => { + it("stringLiteral() generates z.literal()", () => { + const schema = zod.stringLiteral("hello"); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("booleanLiteral(true) generates z.literal(true)", () => { + const schema = zod.booleanLiteral(true); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("booleanLiteral(false) generates z.literal(false)", () => { + const schema = zod.booleanLiteral(false); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Object Schemas", () => { + it("empty object generates z.object({})", () => { + const schema = zod.object([]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with same raw/parsed keys generates z.object()", () => { + const schema = zod.object([ + { key: { parsed: "name", raw: "name" }, value: zod.string() }, + { key: { parsed: "age", raw: "age" }, value: zod.number() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with different raw/parsed keys generates transform", () => { + const schema = zod.object([ + { key: { parsed: "firstName", raw: "first_name" }, value: zod.string() }, + { key: { parsed: "lastName", raw: "last_name" }, value: zod.string() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with optional property", () => { + const schema = zod.object([ + { key: { parsed: "name", raw: "name" }, value: zod.string() }, + { key: { parsed: "nickname", raw: "nickname" }, value: zod.string().optional() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with nullable property", () => { + const schema = zod.object([{ key: { parsed: "name", raw: "name" }, value: zod.string().nullable() }]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object.passthrough()", () => { + const schema = zod.object([{ key: { parsed: "id", raw: "id" }, value: zod.string() }]).passthrough(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Enum Schemas", () => { + it("enum generates z.enum()", () => { + const schema = zod.enum(["ACTIVE", "INACTIVE", "PENDING"]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Collection Schemas", () => { + it("list() generates z.array()", () => { + const schema = zod.list(zod.string()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("set() generates z.array().transform()", () => { + const schema = zod.set(zod.number()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("record() generates z.record()", () => { + const schema = zod.record({ + keySchema: zod.string(), + valueSchema: zod.number() + }); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("nested list generates z.array(z.array())", () => { + const schema = zod.list(zod.list(zod.string())); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Union Schemas", () => { + it("discriminated union generates z.discriminatedUnion()", () => { + const schema = zod.union({ + parsedDiscriminant: "type", + rawDiscriminant: "type", + singleUnionTypes: [ + { + discriminantValue: "dog", + nonDiscriminantProperties: zod.object([ + { key: { parsed: "breed", raw: "breed" }, value: zod.string() } + ]) + }, + { + discriminantValue: "cat", + nonDiscriminantProperties: zod.object([ + { key: { parsed: "indoor", raw: "indoor" }, value: zod.boolean() } + ]) + } + ] + }); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("undiscriminatedUnion generates z.union()", () => { + const schema = zod.undiscriminatedUnion([zod.string(), zod.number(), zod.boolean()]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Lazy Schemas", () => { + it("lazy() generates z.lazy()", () => { + const schema = zod.lazy(zod.string()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("lazyObject() generates z.lazy()", () => { + const schema = zod.lazyObject(zod.object([{ key: { parsed: "id", raw: "id" }, value: zod.string() }])); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Schema Modifiers", () => { + it("optional() generates .optional()", () => { + const schema = zod.string().optional(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("nullable() generates .nullable()", () => { + const schema = zod.string().nullable(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("optionalNullable() generates .optional().nullable()", () => { + const schema = zod.string().optionalNullable(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Schema Operations", () => { + it("parse() generates .parse()", () => { + const schema = zod.string(); + const rawExpr = ts.factory.createIdentifier("rawValue"); + const parseExpr = schema.parse(rawExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(parseExpr); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Type References", () => { + it("Schema._getReferenceToType generates z.ZodTypeAny", () => { + const rawShape = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); + const parsedShape = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); + const typeNode = zod.Schema._getReferenceToType({ rawShape, parsedShape }); + const ast = printNode(typeNode); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("MaybeValid Visitor", () => { + it("_visitMaybeValid generates if/else with success/data/error", () => { + const maybeValidRef = ts.factory.createIdentifier("result"); + const statements = zod.Schema._visitMaybeValid(maybeValidRef, { + valid: (valueRef) => [ + ts.factory.createExpressionStatement( + ts.factory.createCallExpression(ts.factory.createIdentifier("console.log"), undefined, [ + valueRef + ]) + ) + ], + invalid: (errorsRef) => [ + ts.factory.createThrowStatement( + ts.factory.createNewExpression(ts.factory.createIdentifier("Error"), undefined, [errorsRef]) + ) + ] + }); + const block = ts.factory.createBlock(statements, true); + const ast = printNode(block); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Field names", () => { + it("MaybeValid field names use Zod conventions", () => { + expect(zod.MaybeValid.ok).toBe("success"); + expect(zod.MaybeValid.Valid.value).toBe("data"); + expect(zod.MaybeValid.Invalid.errors).toBe("error"); + }); + }); + + describe("Runtime Configuration", () => { + it("returns zod as npm dependency", () => { + const deps = zod.getRuntimeDependencies(); + expect(deps).toHaveProperty("zod"); + expect(deps.zod).toMatch(/^\^3\./); + }); + + it("returns null for runtime file patterns (uses npm)", () => { + const patterns = zod.getRuntimeFilePatterns(); + expect(patterns).toBeNull(); + }); + }); + + describe("JSON Serialization Compatibility", () => { + it("set() generates z.array().transform() for parsing and toJsonExpression for serialization", () => { + // Parsing: array → Set via transform + // Serialization: Set → Array via toJsonExpression + const schema = zod.set(zod.string()); + const ast = printNode(schema.toExpression()); + // Should contain transform to create Set from array + expect(ast).toContain("transform"); + expect(ast).toContain("new Set"); + }); + + it("set().json() generates Array.from() for Set serialization", () => { + // When serializing, we need to convert Set → Array + const schema = zod.set(zod.string()); + const valueExpr = ts.factory.createIdentifier("value"); + const jsonExpr = schema.json(valueExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(jsonExpr); + // Should generate Array.from(value) + expect(ast).toBe("Array.from(value)"); + }); + + it("set().optional().json() handles undefined correctly", () => { + // Optional set should check for null/undefined before converting + const schema = zod.set(zod.string()).optional(); + const valueExpr = ts.factory.createIdentifier("value"); + const jsonExpr = schema.json(valueExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(jsonExpr); + // Should generate: value != null ? Array.from(value) : value + expect(ast).toContain("Array.from(value)"); + expect(ast).toContain("!="); + expect(ast).toContain("null"); + }); + + it("record() always uses string keys for JSON compatibility", () => { + // JSON object keys are always strings, even if Fern declares map + const schema = zod.record({ + keySchema: zod.number(), // Even with number key schema + valueSchema: zod.string() + }); + const ast = printNode(schema.toExpression()); + // Key should be z.string(), not z.number() + expect(ast).toBe("z.record(z.string(), z.string())"); + }); + + it("record() ignores key schema and uses string", () => { + // Verify various key types all become string + const withBoolKey = zod.record({ + keySchema: zod.boolean(), + valueSchema: zod.number() + }); + const withAnyKey = zod.record({ + keySchema: zod.any(), + valueSchema: zod.string() + }); + + expect(printNode(withBoolKey.toExpression())).toContain("z.string()"); + expect(printNode(withAnyKey.toExpression())).toContain("z.string()"); + }); + + it("primitive types have no json transformation", () => { + // Primitives should just pass through + const stringSchema = zod.string(); + const valueExpr = ts.factory.createIdentifier("value"); + const jsonExpr = stringSchema.json(valueExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + // Should just return the value as-is + expect(printNode(jsonExpr)).toBe("value"); + }); + }); +}); diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgBaseline.test.ts b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgBaseline.test.ts new file mode 100644 index 000000000000..451babd4c335 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgBaseline.test.ts @@ -0,0 +1,448 @@ +import { ts } from "ts-morph"; +import { describe, expect, it } from "vitest"; +import { ZURG_MANIFEST, ZurgFormat } from "../formats/ZurgFormat"; + +/** + * Helper to print TypeScript AST to string for snapshot comparison + */ +function printNode(node: ts.Node): string { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const sourceFile = ts.createSourceFile("test.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); + return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); +} + +/** + * Create a ZurgFormat instance for testing. + * We don't pass ImportsManager for unit tests - the import tracking + * is only needed for actual code generation. + */ +function createZurg(): ZurgFormat { + return new ZurgFormat({ + getReferenceToExport: () => { + throw new Error("getReferenceToExport should not be called in new ZurgFormat"); + }, + generateEndpointMetadata: false + }); +} + +describe("ZurgFormat AST Generation Baseline", () => { + const zurg = createZurg(); + + describe("Primitive Schemas", () => { + it("string() generates correct AST", () => { + const schema = zurg.string(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("number() generates correct AST", () => { + const schema = zurg.number(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("boolean() generates correct AST", () => { + const schema = zurg.boolean(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("bigint() generates correct AST", () => { + const schema = zurg.bigint(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("date() generates correct AST", () => { + const schema = zurg.date(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("any() generates correct AST", () => { + const schema = zurg.any(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("unknown() generates correct AST", () => { + const schema = zurg.unknown(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("never() generates correct AST", () => { + const schema = zurg.never(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Literal Schemas", () => { + it("stringLiteral() generates correct AST", () => { + const schema = zurg.stringLiteral("hello"); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("booleanLiteral(true) generates correct AST", () => { + const schema = zurg.booleanLiteral(true); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("booleanLiteral(false) generates correct AST", () => { + const schema = zurg.booleanLiteral(false); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Object Schemas", () => { + it("empty object generates correct AST", () => { + const schema = zurg.object([]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with same raw/parsed keys generates correct AST", () => { + const schema = zurg.object([ + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with different raw/parsed keys generates correct AST", () => { + const schema = zurg.object([ + { key: { parsed: "firstName", raw: "first_name" }, value: zurg.string() }, + { key: { parsed: "lastName", raw: "last_name" }, value: zurg.string() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with optional property generates correct AST", () => { + const schema = zurg.object([ + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "nickname", raw: "nickname" }, value: zurg.string().optional() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with nullable property generates correct AST", () => { + const schema = zurg.object([{ key: { parsed: "name", raw: "name" }, value: zurg.string().nullable() }]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("objectWithoutOptionalProperties generates correct AST", () => { + const schema = zurg.objectWithoutOptionalProperties([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object.extend() generates correct AST", () => { + const baseSchema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]); + const extendedSchema = baseSchema.extend( + zurg.object([{ key: { parsed: "name", raw: "name" }, value: zurg.string() }]) + ); + const ast = printNode(extendedSchema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object.passthrough() generates correct AST", () => { + const schema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]).passthrough(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Enum Schemas", () => { + it("enum with single value generates correct AST", () => { + const schema = zurg.enum(["ACTIVE"]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("enum with multiple values generates correct AST", () => { + const schema = zurg.enum(["ACTIVE", "INACTIVE", "PENDING"]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Collection Schemas", () => { + it("list() generates correct AST", () => { + const schema = zurg.list(zurg.string()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("set() generates correct AST", () => { + const schema = zurg.set(zurg.number()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("record() generates correct AST", () => { + const schema = zurg.record({ + keySchema: zurg.string(), + valueSchema: zurg.number() + }); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("nested list generates correct AST", () => { + const schema = zurg.list(zurg.list(zurg.string())); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Union Schemas", () => { + it("discriminated union generates correct AST", () => { + const schema = zurg.union({ + parsedDiscriminant: "type", + rawDiscriminant: "type", + singleUnionTypes: [ + { + discriminantValue: "dog", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "breed", raw: "breed" }, value: zurg.string() } + ]) + }, + { + discriminantValue: "cat", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "indoor", raw: "indoor" }, value: zurg.boolean() } + ]) + } + ] + }); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("union with different raw/parsed discriminant generates correct AST", () => { + const schema = zurg.union({ + parsedDiscriminant: "animalType", + rawDiscriminant: "animal_type", + singleUnionTypes: [ + { + discriminantValue: "dog", + nonDiscriminantProperties: zurg.object([]) + } + ] + }); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("undiscriminatedUnion generates correct AST", () => { + const schema = zurg.undiscriminatedUnion([zurg.string(), zurg.number(), zurg.boolean()]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Lazy Schemas (for recursion)", () => { + it("lazy() generates correct AST", () => { + const schema = zurg.lazy(zurg.string()); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("lazyObject() generates correct AST", () => { + const schema = zurg.lazyObject(zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }])); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Schema Modifiers", () => { + it("optional() generates correct AST", () => { + const schema = zurg.string().optional(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("nullable() generates correct AST", () => { + const schema = zurg.string().nullable(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("optionalNullable() generates correct AST", () => { + const schema = zurg.string().optionalNullable(); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Schema Operations", () => { + it("parse() generates correct AST", () => { + const schema = zurg.string(); + const rawExpr = ts.factory.createIdentifier("rawValue"); + const parseExpr = schema.parse(rawExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(parseExpr); + expect(ast).toMatchSnapshot(); + }); + + it("json() generates correct AST", () => { + const schema = zurg.string(); + const parsedExpr = ts.factory.createIdentifier("parsedValue"); + const jsonExpr = schema.json(parsedExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(jsonExpr); + expect(ast).toMatchSnapshot(); + }); + + it("parseOrThrow() generates correct AST", () => { + const schema = zurg.string(); + const rawExpr = ts.factory.createIdentifier("rawValue"); + const parseExpr = schema.parseOrThrow(rawExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(parseExpr); + expect(ast).toMatchSnapshot(); + }); + + it("jsonOrThrow() generates correct AST", () => { + const schema = zurg.string(); + const parsedExpr = ts.factory.createIdentifier("parsedValue"); + const jsonExpr = schema.jsonOrThrow(parsedExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + const ast = printNode(jsonExpr); + expect(ast).toMatchSnapshot(); + }); + + it("parse() with skipValidation generates correct AST", () => { + const schema = zurg.object([{ key: { parsed: "name", raw: "name" }, value: zurg.string() }]); + const rawExpr = ts.factory.createIdentifier("rawValue"); + const parseExpr = schema.parse(rawExpr, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + omitUndefined: true, + breadcrumbsPrefix: ["request"] + }); + const ast = printNode(parseExpr); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Type References", () => { + it("Schema._getReferenceToType generates correct AST", () => { + const rawShape = ts.factory.createTypeLiteralNode([ + ts.factory.createPropertySignature( + undefined, + "name", + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ) + ]); + const parsedShape = ts.factory.createTypeLiteralNode([ + ts.factory.createPropertySignature( + undefined, + "name", + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ) + ]); + const typeNode = zurg.Schema._getReferenceToType({ rawShape, parsedShape }); + const ast = printNode(typeNode); + expect(ast).toMatchSnapshot(); + }); + + it("ObjectSchema._getReferenceToType generates correct AST", () => { + const rawShape = ts.factory.createTypeLiteralNode([]); + const parsedShape = ts.factory.createTypeLiteralNode([]); + const typeNode = zurg.ObjectSchema._getReferenceToType({ rawShape, parsedShape }); + const ast = printNode(typeNode); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("MaybeValid Visitor", () => { + it("_visitMaybeValid generates correct AST", () => { + const maybeValidRef = ts.factory.createIdentifier("result"); + const statements = zurg.Schema._visitMaybeValid(maybeValidRef, { + valid: (valueRef: ts.Expression) => [ + ts.factory.createExpressionStatement( + ts.factory.createCallExpression(ts.factory.createIdentifier("console.log"), undefined, [ + valueRef + ]) + ) + ], + invalid: (errorsRef: ts.Expression) => [ + ts.factory.createThrowStatement( + ts.factory.createNewExpression(ts.factory.createIdentifier("Error"), undefined, [errorsRef]) + ) + ] + }); + const block = ts.factory.createBlock(statements, true); + const ast = printNode(block); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Complex Nested Schemas", () => { + it("deeply nested object generates correct AST", () => { + const schema = zurg.object([ + { + key: { parsed: "user", raw: "user" }, + value: zurg.object([ + { + key: { parsed: "profile", raw: "profile" }, + value: zurg.object([ + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number().optional() } + ]) + } + ]) + }, + { + key: { parsed: "tags", raw: "tags" }, + value: zurg.list(zurg.string()) + }, + { + key: { parsed: "metadata", raw: "metadata" }, + value: zurg.record({ keySchema: zurg.string(), valueSchema: zurg.any() }) + } + ]); + const ast = printNode(schema.toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); +}); diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgFormat.test.ts b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgFormat.test.ts new file mode 100644 index 000000000000..01fbdb0dd8dd --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/ZurgFormat.test.ts @@ -0,0 +1,255 @@ +import { ts } from "ts-morph"; +import { describe, expect, it } from "vitest"; +import { CoreUtility } from "../../core-utilities/CoreUtility"; +import { Reference } from "../../referencing"; +import { ZurgFormat } from "../formats/ZurgFormat"; + +/** + * Helper to print TypeScript AST to string for comparison + */ +function printNode(node: ts.Node): string { + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + const sourceFile = ts.createSourceFile("test.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); + return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile); +} + +/** + * Create a mock reference that simulates what the real system does. + */ +function createMockReference(exportedName: string): Reference { + return { + getExpression: () => { + return ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + }, + getTypeNode: () => { + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ), + undefined + ); + }, + getEntityName: () => { + return ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + } + }; +} + +/** + * Create a ZurgFormat instance for testing + */ +function createZurgFormat(): ZurgFormat { + return new ZurgFormat({ + getReferenceToExport: ({ exportedName }: { manifest: CoreUtility.Manifest; exportedName: string }) => + createMockReference(exportedName), + generateEndpointMetadata: false + }); +} + +describe("ZurgFormat AST Generation", () => { + const zurg = createZurgFormat(); + + describe("Primitive Schemas", () => { + it("string() generates correct AST", () => { + const ast = printNode(zurg.string().toExpression()); + expect(ast).toBe("core.serialization.string()"); + }); + + it("number() generates correct AST", () => { + const ast = printNode(zurg.number().toExpression()); + expect(ast).toBe("core.serialization.number()"); + }); + + it("boolean() generates correct AST", () => { + const ast = printNode(zurg.boolean().toExpression()); + expect(ast).toBe("core.serialization.boolean()"); + }); + + it("bigint() generates correct AST", () => { + const ast = printNode(zurg.bigint().toExpression()); + expect(ast).toBe("core.serialization.bigint()"); + }); + + it("date() generates correct AST", () => { + const ast = printNode(zurg.date().toExpression()); + expect(ast).toBe("core.serialization.date()"); + }); + + it("any() generates correct AST", () => { + const ast = printNode(zurg.any().toExpression()); + expect(ast).toBe("core.serialization.any()"); + }); + + it("unknown() generates correct AST", () => { + const ast = printNode(zurg.unknown().toExpression()); + expect(ast).toBe("core.serialization.unknown()"); + }); + + it("never() generates correct AST", () => { + const ast = printNode(zurg.never().toExpression()); + expect(ast).toBe("core.serialization.never()"); + }); + }); + + describe("Literal Schemas", () => { + it("stringLiteral() generates correct AST", () => { + const ast = printNode(zurg.stringLiteral("hello").toExpression()); + expect(ast).toBe('core.serialization.stringLiteral("hello")'); + }); + + it("booleanLiteral(true) generates correct AST", () => { + const ast = printNode(zurg.booleanLiteral(true).toExpression()); + expect(ast).toBe("core.serialization.booleanLiteral(true)"); + }); + + it("booleanLiteral(false) generates correct AST", () => { + const ast = printNode(zurg.booleanLiteral(false).toExpression()); + expect(ast).toBe("core.serialization.booleanLiteral(false)"); + }); + }); + + describe("Object Schemas", () => { + it("empty object generates correct AST", () => { + const ast = printNode(zurg.object([]).toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("object with same raw/parsed keys generates correct AST", () => { + const ast = printNode( + zurg + .object([ + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number() } + ]) + .toExpression() + ); + expect(ast).toMatchSnapshot(); + }); + + it("object with different raw/parsed keys generates correct AST", () => { + const ast = printNode( + zurg + .object([ + { key: { parsed: "firstName", raw: "first_name" }, value: zurg.string() }, + { key: { parsed: "lastName", raw: "last_name" }, value: zurg.string() } + ]) + .toExpression() + ); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Enum Schema", () => { + it("enum with values generates correct AST", () => { + const ast = printNode(zurg.enum(["A", "B", "C"]).toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Collection Schemas", () => { + it("list(string()) generates correct AST", () => { + const ast = printNode(zurg.list(zurg.string()).toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("set(number()) generates correct AST", () => { + const ast = printNode(zurg.set(zurg.number()).toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("record(string, any) generates correct AST", () => { + const ast = printNode(zurg.record({ keySchema: zurg.string(), valueSchema: zurg.any() }).toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Union Schemas", () => { + it("discriminated union generates correct AST", () => { + const ast = printNode( + zurg + .union({ + parsedDiscriminant: "type", + rawDiscriminant: "type", + singleUnionTypes: [ + { + discriminantValue: "dog", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "breed", raw: "breed" }, value: zurg.string() } + ]) + }, + { + discriminantValue: "cat", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "indoor", raw: "indoor" }, value: zurg.boolean() } + ]) + } + ] + }) + .toExpression() + ); + expect(ast).toMatchSnapshot(); + }); + + it("undiscriminated union generates correct AST", () => { + const ast = printNode( + zurg.undiscriminatedUnion([zurg.string(), zurg.number(), zurg.boolean()]).toExpression() + ); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Schema Modifiers", () => { + it("optional() generates correct AST", () => { + const ast = printNode(zurg.string().optional().toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("nullable() generates correct AST", () => { + const ast = printNode(zurg.string().nullable().toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("optionalNullable() generates correct AST", () => { + const ast = printNode(zurg.string().optionalNullable().toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Lazy Schemas", () => { + it("lazy() generates correct AST", () => { + const ast = printNode(zurg.lazy(zurg.string()).toExpression()); + expect(ast).toMatchSnapshot(); + }); + + it("lazyObject() generates correct AST", () => { + const innerSchema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]); + const ast = printNode(zurg.lazyObject(innerSchema).toExpression()); + expect(ast).toMatchSnapshot(); + }); + }); + + describe("Runtime Configuration", () => { + it("returns empty dependencies for Zurg", () => { + expect(zurg.getRuntimeDependencies()).toEqual({}); + }); + + it("returns runtime file patterns for Zurg", () => { + const patterns = zurg.getRuntimeFilePatterns(); + expect(patterns).not.toBeNull(); + expect(patterns?.patterns).toContain("src/core/schemas/**"); + }); + }); + + describe("Format Identity", () => { + it("has name 'default'", () => { + expect(zurg.name).toBe("zurg"); + }); + }); +}); diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZodFormat.test.ts.snap b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZodFormat.test.ts.snap new file mode 100644 index 000000000000..a14211a1d75c --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZodFormat.test.ts.snap @@ -0,0 +1,119 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ZodFormat AST Generation > Collection Schemas > list() generates z.array() 1`] = `"z.array(z.string())"`; + +exports[`ZodFormat AST Generation > Collection Schemas > nested list generates z.array(z.array()) 1`] = `"z.array(z.array(z.string()))"`; + +exports[`ZodFormat AST Generation > Collection Schemas > record() generates z.record() 1`] = `"z.record(z.string(), z.number())"`; + +exports[`ZodFormat AST Generation > Collection Schemas > set() generates z.array().transform() 1`] = `"z.array(z.number()).transform(arr => new Set(arr))"`; + +exports[`ZodFormat AST Generation > Enum Schemas > enum generates z.enum() 1`] = `"z.enum(["ACTIVE", "INACTIVE", "PENDING"])"`; + +exports[`ZodFormat AST Generation > Lazy Schemas > lazy() generates z.lazy() 1`] = `"z.lazy(() => z.string())"`; + +exports[`ZodFormat AST Generation > Lazy Schemas > lazyObject() generates z.lazy() 1`] = ` +"z.lazy(() => z.object({ + "id": z.string() +}))" +`; + +exports[`ZodFormat AST Generation > Literal Schemas > booleanLiteral(false) generates z.literal(false) 1`] = `"z.literal(false)"`; + +exports[`ZodFormat AST Generation > Literal Schemas > booleanLiteral(true) generates z.literal(true) 1`] = `"z.literal(true)"`; + +exports[`ZodFormat AST Generation > Literal Schemas > stringLiteral() generates z.literal() 1`] = `"z.literal("hello")"`; + +exports[`ZodFormat AST Generation > MaybeValid Visitor > _visitMaybeValid generates if/else with success/data/error 1`] = ` +"{ + if (result.success) { + console.log(result.data); + } + else { + throw new Error(result.error); + } +}" +`; + +exports[`ZodFormat AST Generation > Object Schemas > empty object generates z.object({}) 1`] = `"z.object({})"`; + +exports[`ZodFormat AST Generation > Object Schemas > object with different raw/parsed keys generates transform 1`] = ` +"z.object({ + "first_name": z.string(), + "last_name": z.string() +}).transform(data => ({ + firstName: data.first_name, + lastName: data.last_name +}))" +`; + +exports[`ZodFormat AST Generation > Object Schemas > object with nullable property 1`] = ` +"z.object({ + "name": z.string().nullable() +})" +`; + +exports[`ZodFormat AST Generation > Object Schemas > object with optional property 1`] = ` +"z.object({ + "name": z.string(), + "nickname": z.string().optional() +})" +`; + +exports[`ZodFormat AST Generation > Object Schemas > object with same raw/parsed keys generates z.object() 1`] = ` +"z.object({ + "name": z.string(), + "age": z.number() +})" +`; + +exports[`ZodFormat AST Generation > Object Schemas > object.passthrough() 1`] = ` +"z.object({ + "id": z.string() +}).passthrough()" +`; + +exports[`ZodFormat AST Generation > Primitive Schemas > any() generates z.any() 1`] = `"z.any()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > bigint() generates z.bigint() 1`] = `"z.bigint()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > boolean() generates z.boolean() 1`] = `"z.boolean()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > date() generates z.string().transform() 1`] = `"z.string().transform(s => new Date(s))"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > never() generates z.never() 1`] = `"z.never()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > number() generates z.number() 1`] = `"z.number()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > string() generates z.string() 1`] = `"z.string()"`; + +exports[`ZodFormat AST Generation > Primitive Schemas > unknown() generates z.unknown() 1`] = `"z.unknown()"`; + +exports[`ZodFormat AST Generation > Schema Modifiers > nullable() generates .nullable() 1`] = `"z.string().nullable()"`; + +exports[`ZodFormat AST Generation > Schema Modifiers > optional() generates .optional() 1`] = `"z.string().optional()"`; + +exports[`ZodFormat AST Generation > Schema Modifiers > optionalNullable() generates .optional().nullable() 1`] = `"z.string().optional().nullable()"`; + +exports[`ZodFormat AST Generation > Schema Operations > parse() generates .parse() 1`] = `"z.string().parse(rawValue)"`; + +exports[`ZodFormat AST Generation > Type References > Schema._getReferenceToType generates z.ZodTypeAny 1`] = ` +"{ + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => string; +}" +`; + +exports[`ZodFormat AST Generation > Union Schemas > discriminated union generates z.discriminatedUnion() 1`] = ` +"z.discriminatedUnion("type", [ + z.object({ "type": z.literal("dog") }).merge(z.object({ + "breed": z.string() + }) as z.AnyZodObject), + z.object({ "type": z.literal("cat") }).merge(z.object({ + "indoor": z.boolean() + }) as z.AnyZodObject) +])" +`; + +exports[`ZodFormat AST Generation > Union Schemas > undiscriminatedUnion generates z.union() 1`] = `"z.union([z.string(), z.number(), z.boolean()])"`; diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgBaseline.test.ts.snap b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgBaseline.test.ts.snap new file mode 100644 index 000000000000..0022d9053d5f --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgBaseline.test.ts.snap @@ -0,0 +1,165 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ZurgFormat AST Generation Baseline > Collection Schemas > list() generates correct AST 1`] = `"core.serialization.list(core.serialization.string())"`; + +exports[`ZurgFormat AST Generation Baseline > Collection Schemas > nested list generates correct AST 1`] = `"core.serialization.list(core.serialization.list(core.serialization.string()))"`; + +exports[`ZurgFormat AST Generation Baseline > Collection Schemas > record() generates correct AST 1`] = `"core.serialization.record(core.serialization.string(), core.serialization.number())"`; + +exports[`ZurgFormat AST Generation Baseline > Collection Schemas > set() generates correct AST 1`] = `"core.serialization.set(core.serialization.number())"`; + +exports[`ZurgFormat AST Generation Baseline > Complex Nested Schemas > deeply nested object generates correct AST 1`] = ` +"core.serialization.object({ + "user": core.serialization.object({ + "profile": core.serialization.object({ + "name": core.serialization.string(), + "age": core.serialization.number().optional() + }) + }), + "tags": core.serialization.list(core.serialization.string()), + "metadata": core.serialization.record(core.serialization.string(), core.serialization.any()) +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Enum Schemas > enum with multiple values generates correct AST 1`] = `"core.serialization.enum_(["ACTIVE", "INACTIVE", "PENDING"])"`; + +exports[`ZurgFormat AST Generation Baseline > Enum Schemas > enum with single value generates correct AST 1`] = `"core.serialization.enum_(["ACTIVE"])"`; + +exports[`ZurgFormat AST Generation Baseline > Lazy Schemas (for recursion) > lazy() generates correct AST 1`] = `"core.serialization.lazy(() => core.serialization.string())"`; + +exports[`ZurgFormat AST Generation Baseline > Lazy Schemas (for recursion) > lazyObject() generates correct AST 1`] = ` +"core.serialization.lazyObject(() => core.serialization.object({ + "id": core.serialization.string() +}))" +`; + +exports[`ZurgFormat AST Generation Baseline > Literal Schemas > booleanLiteral(false) generates correct AST 1`] = `"core.serialization.booleanLiteral(false)"`; + +exports[`ZurgFormat AST Generation Baseline > Literal Schemas > booleanLiteral(true) generates correct AST 1`] = `"core.serialization.booleanLiteral(true)"`; + +exports[`ZurgFormat AST Generation Baseline > Literal Schemas > stringLiteral() generates correct AST 1`] = `"core.serialization.stringLiteral("hello")"`; + +exports[`ZurgFormat AST Generation Baseline > MaybeValid Visitor > _visitMaybeValid generates correct AST 1`] = ` +"{ + if (result.ok) { + console.log(result.value); + } + else { + throw new Error(result.errors); + } +}" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > empty object generates correct AST 1`] = `"core.serialization.object({})"`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object with different raw/parsed keys generates correct AST 1`] = ` +"core.serialization.object({ + "firstName": core.serialization.property("first_name", core.serialization.string()), + "lastName": core.serialization.property("last_name", core.serialization.string()) +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object with nullable property generates correct AST 1`] = ` +"core.serialization.object({ + "name": core.serialization.string().nullable() +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object with optional property generates correct AST 1`] = ` +"core.serialization.object({ + "name": core.serialization.string(), + "nickname": core.serialization.string().optional() +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object with same raw/parsed keys generates correct AST 1`] = ` +"core.serialization.object({ + "name": core.serialization.string(), + "age": core.serialization.number() +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object.extend() generates correct AST 1`] = ` +"core.serialization.object({ + "id": core.serialization.string() +}).extend(core.serialization.object({ + "name": core.serialization.string() +}))" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > object.passthrough() generates correct AST 1`] = ` +"core.serialization.object({ + "id": core.serialization.string() +}).passthrough()" +`; + +exports[`ZurgFormat AST Generation Baseline > Object Schemas > objectWithoutOptionalProperties generates correct AST 1`] = ` +"core.serialization.objectWithoutOptionalProperties({ + "id": core.serialization.string() +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > any() generates correct AST 1`] = `"core.serialization.any()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > bigint() generates correct AST 1`] = `"core.serialization.bigint()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > boolean() generates correct AST 1`] = `"core.serialization.boolean()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > date() generates correct AST 1`] = `"core.serialization.date()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > never() generates correct AST 1`] = `"core.serialization.never()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > number() generates correct AST 1`] = `"core.serialization.number()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > string() generates correct AST 1`] = `"core.serialization.string()"`; + +exports[`ZurgFormat AST Generation Baseline > Primitive Schemas > unknown() generates correct AST 1`] = `"core.serialization.unknown()"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Modifiers > nullable() generates correct AST 1`] = `"core.serialization.string().nullable()"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Modifiers > optional() generates correct AST 1`] = `"core.serialization.string().optional()"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Modifiers > optionalNullable() generates correct AST 1`] = `"core.serialization.string().optionalNullable()"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Operations > json() generates correct AST 1`] = `"core.serialization.string().json(parsedValue)"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Operations > jsonOrThrow() generates correct AST 1`] = `"core.serialization.string().jsonOrThrow(parsedValue)"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Operations > parse() generates correct AST 1`] = `"core.serialization.string().parse(rawValue)"`; + +exports[`ZurgFormat AST Generation Baseline > Schema Operations > parse() with skipValidation generates correct AST 1`] = ` +"core.serialization.object({ + "name": core.serialization.string() +}).parse(rawValue, { unrecognizedObjectKeys: "passthrough", allowUnrecognizedUnionMembers: true, allowUnrecognizedEnumValues: true, skipValidation: true, omitUndefined: true, breadcrumbsPrefix: ["request"] })" +`; + +exports[`ZurgFormat AST Generation Baseline > Schema Operations > parseOrThrow() generates correct AST 1`] = `"core.serialization.string().parseOrThrow(rawValue)"`; + +exports[`ZurgFormat AST Generation Baseline > Type References > ObjectSchema._getReferenceToType generates correct AST 1`] = `"core.serialization.ObjectSchema<{}, {}>"`; + +exports[`ZurgFormat AST Generation Baseline > Type References > Schema._getReferenceToType generates correct AST 1`] = ` +"core.serialization.Schema<{ + name: string; +}, { + name: string; +}>" +`; + +exports[`ZurgFormat AST Generation Baseline > Union Schemas > discriminated union generates correct AST 1`] = ` +"core.serialization.union("type", { + "dog": core.serialization.object({ + "breed": core.serialization.string() + }), + "cat": core.serialization.object({ + "indoor": core.serialization.boolean() + }) +})" +`; + +exports[`ZurgFormat AST Generation Baseline > Union Schemas > undiscriminatedUnion generates correct AST 1`] = `"core.serialization.undiscriminatedUnion([core.serialization.string(), core.serialization.number(), core.serialization.boolean()])"`; + +exports[`ZurgFormat AST Generation Baseline > Union Schemas > union with different raw/parsed discriminant generates correct AST 1`] = ` +"core.serialization.union(core.serialization.discriminant("animalType", "animal_type"), { + "dog": core.serialization.object({}) +})" +`; diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgFormat.test.ts.snap b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgFormat.test.ts.snap new file mode 100644 index 000000000000..35431a5fcd42 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/__snapshots__/ZurgFormat.test.ts.snap @@ -0,0 +1,52 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ZurgFormat AST Generation > Collection Schemas > list(string()) generates correct AST 1`] = `"core.serialization.list(core.serialization.string())"`; + +exports[`ZurgFormat AST Generation > Collection Schemas > record(string, any) generates correct AST 1`] = `"core.serialization.record(core.serialization.string(), core.serialization.any())"`; + +exports[`ZurgFormat AST Generation > Collection Schemas > set(number()) generates correct AST 1`] = `"core.serialization.set(core.serialization.number())"`; + +exports[`ZurgFormat AST Generation > Enum Schema > enum with values generates correct AST 1`] = `"core.serialization.enum_(["A", "B", "C"])"`; + +exports[`ZurgFormat AST Generation > Lazy Schemas > lazy() generates correct AST 1`] = `"core.serialization.lazy(() => core.serialization.string())"`; + +exports[`ZurgFormat AST Generation > Lazy Schemas > lazyObject() generates correct AST 1`] = ` +"core.serialization.lazyObject(() => core.serialization.object({ + "id": core.serialization.string() +}))" +`; + +exports[`ZurgFormat AST Generation > Object Schemas > empty object generates correct AST 1`] = `"core.serialization.object({})"`; + +exports[`ZurgFormat AST Generation > Object Schemas > object with different raw/parsed keys generates correct AST 1`] = ` +"core.serialization.object({ + "firstName": core.serialization.property("first_name", core.serialization.string()), + "lastName": core.serialization.property("last_name", core.serialization.string()) +})" +`; + +exports[`ZurgFormat AST Generation > Object Schemas > object with same raw/parsed keys generates correct AST 1`] = ` +"core.serialization.object({ + "name": core.serialization.string(), + "age": core.serialization.number() +})" +`; + +exports[`ZurgFormat AST Generation > Schema Modifiers > nullable() generates correct AST 1`] = `"core.serialization.string().nullable()"`; + +exports[`ZurgFormat AST Generation > Schema Modifiers > optional() generates correct AST 1`] = `"core.serialization.string().optional()"`; + +exports[`ZurgFormat AST Generation > Schema Modifiers > optionalNullable() generates correct AST 1`] = `"core.serialization.string().optionalNullable()"`; + +exports[`ZurgFormat AST Generation > Union Schemas > discriminated union generates correct AST 1`] = ` +"core.serialization.union("type", { + "dog": core.serialization.object({ + "breed": core.serialization.string() + }), + "cat": core.serialization.object({ + "indoor": core.serialization.boolean() + }) +})" +`; + +exports[`ZurgFormat AST Generation > Union Schemas > undiscriminated union generates correct AST 1`] = `"core.serialization.undiscriminatedUnion([core.serialization.string(), core.serialization.number(), core.serialization.boolean()])"`; diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/benchmark.test.ts b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/benchmark.test.ts new file mode 100644 index 000000000000..5ca3a6685924 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/__tests__/benchmark.test.ts @@ -0,0 +1,562 @@ +import { performance } from "perf_hooks"; +import { ts } from "ts-morph"; +import { beforeAll, describe, expect, it } from "vitest"; +import { CoreUtility } from "../../core-utilities/CoreUtility"; +import { Reference } from "../../referencing"; +import { ZurgFormat } from "../formats/ZurgFormat"; + +/** + * Create a mock reference for testing + */ +function createMockReference(exportedName: string): Reference { + return { + getExpression: () => { + return ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + }, + getTypeNode: () => { + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ), + undefined + ); + }, + getEntityName: () => { + return ts.factory.createQualifiedName( + ts.factory.createIdentifier("serialization"), + ts.factory.createIdentifier(exportedName) + ); + } + }; +} + +/** + * Create a ZurgFormat instance for testing + */ +function createZurg(): ZurgFormat { + return new ZurgFormat({ + getReferenceToExport: ({ exportedName }: { manifest: CoreUtility.Manifest; exportedName: string }) => + createMockReference(exportedName), + generateEndpointMetadata: false + }); +} + +/** + * Benchmark result structure + */ +interface BenchmarkResult { + name: string; + iterations: number; + totalTimeMs: number; + avgTimeMs: number; + opsPerSecond: number; + memoryUsedMB: number; +} + +/** + * Run a benchmark for a given operation + */ +function benchmark(name: string, fn: () => void, iterations: number = 1000): BenchmarkResult { + // Force GC if available (run with --expose-gc flag) + if (global.gc) { + global.gc(); + } + + const memBefore = process.memoryUsage().heapUsed; + + const start = performance.now(); + for (let i = 0; i < iterations; i++) { + fn(); + } + const end = performance.now(); + + const memAfter = process.memoryUsage().heapUsed; + + const totalTimeMs = end - start; + const avgTimeMs = totalTimeMs / iterations; + const opsPerSecond = 1000 / avgTimeMs; + const memoryUsedMB = (memAfter - memBefore) / 1024 / 1024; + + return { + name, + iterations, + totalTimeMs, + avgTimeMs, + opsPerSecond, + memoryUsedMB + }; +} + +/** + * Format benchmark results for logging + */ +function formatResult(result: BenchmarkResult): string { + return `${result.name}: + - Iterations: ${result.iterations} + - Total time: ${result.totalTimeMs.toFixed(2)}ms + - Avg time: ${result.avgTimeMs.toFixed(4)}ms + - Ops/sec: ${result.opsPerSecond.toFixed(2)} + - Memory delta: ${result.memoryUsedMB.toFixed(2)}MB`; +} + +describe("Zurg Performance Benchmarks", () => { + let zurg: ZurgFormat; + + beforeAll(() => { + zurg = createZurg(); + }); + + describe("Primitive Schema Creation", () => { + it("string() creation performance", () => { + const result = benchmark( + "string()", + () => { + zurg.string(); + }, + 10000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(1); // Should be sub-millisecond + }); + + it("number() creation performance", () => { + const result = benchmark( + "number()", + () => { + zurg.number(); + }, + 10000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(1); + }); + + it("boolean() creation performance", () => { + const result = benchmark( + "boolean()", + () => { + zurg.boolean(); + }, + 10000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(1); + }); + }); + + describe("Object Schema Creation", () => { + it("simple object (3 properties) creation performance", () => { + const result = benchmark( + "object (3 props)", + () => { + zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number() } + ]); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(5); + }); + + it("medium object (10 properties) creation performance", () => { + const result = benchmark( + "object (10 props)", + () => { + zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "email", raw: "email" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number() }, + { key: { parsed: "active", raw: "active" }, value: zurg.boolean() }, + { key: { parsed: "createdAt", raw: "created_at" }, value: zurg.date() }, + { key: { parsed: "updatedAt", raw: "updated_at" }, value: zurg.date() }, + { key: { parsed: "role", raw: "role" }, value: zurg.enum(["admin", "user", "guest"]) }, + { key: { parsed: "tags", raw: "tags" }, value: zurg.list(zurg.string()) }, + { + key: { parsed: "metadata", raw: "metadata" }, + value: zurg.record({ keySchema: zurg.string(), valueSchema: zurg.any() }) + } + ]); + }, + 2000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(10); + }); + + it("object with property renaming performance", () => { + const result = benchmark( + "object (renamed props)", + () => { + zurg.object([ + { key: { parsed: "firstName", raw: "first_name" }, value: zurg.string() }, + { key: { parsed: "lastName", raw: "last_name" }, value: zurg.string() }, + { key: { parsed: "emailAddress", raw: "email_address" }, value: zurg.string() }, + { key: { parsed: "phoneNumber", raw: "phone_number" }, value: zurg.string() }, + { key: { parsed: "dateOfBirth", raw: "date_of_birth" }, value: zurg.date() } + ]); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(5); + }); + }); + + describe("Union Schema Creation", () => { + it("discriminated union (3 variants) creation performance", () => { + const result = benchmark( + "union (3 variants)", + () => { + zurg.union({ + parsedDiscriminant: "type", + rawDiscriminant: "type", + singleUnionTypes: [ + { + discriminantValue: "dog", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "breed", raw: "breed" }, value: zurg.string() } + ]) + }, + { + discriminantValue: "cat", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "indoor", raw: "indoor" }, value: zurg.boolean() } + ]) + }, + { + discriminantValue: "bird", + nonDiscriminantProperties: zurg.object([ + { key: { parsed: "canFly", raw: "can_fly" }, value: zurg.boolean() } + ]) + } + ] + }); + }, + 2000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(10); + }); + + it("undiscriminated union (5 types) creation performance", () => { + const result = benchmark( + "undiscriminatedUnion (5 types)", + () => { + zurg.undiscriminatedUnion([ + zurg.string(), + zurg.number(), + zurg.boolean(), + zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]), + zurg.list(zurg.string()) + ]); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(5); + }); + }); + + describe("Collection Schema Creation", () => { + it("list schema creation performance", () => { + const result = benchmark( + "list(string())", + () => { + zurg.list(zurg.string()); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + + it("nested list schema creation performance", () => { + const result = benchmark( + "list(list(string()))", + () => { + zurg.list(zurg.list(zurg.string())); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(3); + }); + + it("record schema creation performance", () => { + const result = benchmark( + "record(string, object)", + () => { + zurg.record({ + keySchema: zurg.string(), + valueSchema: zurg.object([{ key: { parsed: "name", raw: "name" }, value: zurg.string() }]) + }); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(5); + }); + }); + + describe("AST Generation (toExpression)", () => { + it("simple object toExpression() performance", () => { + const schema = zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "name", raw: "name" }, value: zurg.string() } + ]); + + const result = benchmark( + "object.toExpression()", + () => { + schema.toExpression(); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(1); + }); + + it("complex nested schema toExpression() performance", () => { + const schema = zurg.object([ + { + key: { parsed: "user", raw: "user" }, + value: zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { + key: { parsed: "profile", raw: "profile" }, + value: zurg.object([ + { key: { parsed: "name", raw: "name" }, value: zurg.string() }, + { key: { parsed: "age", raw: "age" }, value: zurg.number().optional() }, + { key: { parsed: "tags", raw: "tags" }, value: zurg.list(zurg.string()) } + ]) + } + ]) + }, + { + key: { parsed: "permissions", raw: "permissions" }, + value: zurg.list(zurg.enum(["read", "write", "admin"])) + } + ]); + + const result = benchmark( + "complex.toExpression()", + () => { + schema.toExpression(); + }, + 2000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + }); + + describe("Schema Operations", () => { + it("optional() chain performance", () => { + const result = benchmark( + "string().optional()", + () => { + zurg.string().optional(); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + + it("nullable() chain performance", () => { + const result = benchmark( + "string().nullable()", + () => { + zurg.string().nullable(); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + + it("object.extend() performance", () => { + const baseSchema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]); + + const result = benchmark( + "object.extend()", + () => { + baseSchema.extend(zurg.object([{ key: { parsed: "name", raw: "name" }, value: zurg.string() }])); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(5); + }); + + it("lazy() wrapper performance", () => { + const innerSchema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]); + + const result = benchmark( + "lazy()", + () => { + zurg.lazy(innerSchema); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + + it("lazyObject() wrapper performance", () => { + const innerSchema = zurg.object([{ key: { parsed: "id", raw: "id" }, value: zurg.string() }]); + + const result = benchmark( + "lazyObject()", + () => { + zurg.lazyObject(innerSchema); + }, + 5000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + }); + + describe("Parse/JSON Expression Generation", () => { + it("parse() expression generation performance", () => { + const schema = zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "name", raw: "name" }, value: zurg.string() } + ]); + const rawExpr = ts.factory.createIdentifier("raw"); + + const result = benchmark( + "schema.parse()", + () => { + schema.parse(rawExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + + it("json() expression generation performance", () => { + const schema = zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "name", raw: "name" }, value: zurg.string() } + ]); + const parsedExpr = ts.factory.createIdentifier("parsed"); + + const result = benchmark( + "schema.json()", + () => { + schema.json(parsedExpr, { + unrecognizedObjectKeys: "fail", + allowUnrecognizedUnionMembers: false, + allowUnrecognizedEnumValues: false, + skipValidation: false, + omitUndefined: false, + breadcrumbsPrefix: [] + }); + }, + 3000 + ); + + console.log(formatResult(result)); + expect(result.avgTimeMs).toBeLessThan(2); + }); + }); + + describe("Realistic Workload Simulation", () => { + it("simulate generating schemas for 100 types", () => { + const result = benchmark( + "100 type schemas", + () => { + // Simulate a mix of types that might be in a real API + for (let i = 0; i < 100; i++) { + const typeIndex = i % 5; + switch (typeIndex) { + case 0: + // Simple object + zurg.object([ + { key: { parsed: "id", raw: "id" }, value: zurg.string() }, + { key: { parsed: "value", raw: "value" }, value: zurg.number() } + ]); + break; + case 1: + // Enum + zurg.enum(["A", "B", "C"]); + break; + case 2: + // List + zurg.list(zurg.string()); + break; + case 3: + // Union + zurg.union({ + parsedDiscriminant: "type", + rawDiscriminant: "type", + singleUnionTypes: [ + { + discriminantValue: "a", + nonDiscriminantProperties: zurg.object([]) + }, + { + discriminantValue: "b", + nonDiscriminantProperties: zurg.object([]) + } + ] + }); + break; + case 4: + // Complex object + zurg.object([ + { key: { parsed: "firstName", raw: "first_name" }, value: zurg.string() }, + { key: { parsed: "lastName", raw: "last_name" }, value: zurg.string() }, + { key: { parsed: "email", raw: "email" }, value: zurg.string().optional() }, + { key: { parsed: "tags", raw: "tags" }, value: zurg.list(zurg.string()) } + ]); + break; + } + } + }, + 100 + ); + + console.log(formatResult(result)); + // 100 types should complete in reasonable time + expect(result.avgTimeMs).toBeLessThan(500); + }); + }); +}); diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/formats/PassthroughFormat.ts b/generators/typescript/utils/commons/src/serialization-pipeline/formats/PassthroughFormat.ts new file mode 100644 index 000000000000..2378121d0915 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/formats/PassthroughFormat.ts @@ -0,0 +1,123 @@ +import { ts } from "ts-morph"; + +import { SerializationFormat } from "../SerializationFormat"; + +/** + * A no-op schema that does nothing - used when serialization is disabled. + */ +const NO_OP_SCHEMA: SerializationFormat.SchemaWithUtils = { + toExpression: () => ts.factory.createIdentifier("undefined"), + isOptional: false, + isNullable: false, + parse: (raw) => raw, + json: (parsed) => parsed, + parseOrThrow: (raw) => raw, + jsonOrThrow: (parsed) => parsed, + nullable: () => NO_OP_SCHEMA, + optional: () => NO_OP_SCHEMA, + optionalNullable: () => NO_OP_SCHEMA, + transform: () => NO_OP_SCHEMA +}; + +/** + * A no-op object schema + */ +const NO_OP_OBJECT_SCHEMA: SerializationFormat.ObjectSchema = { + ...NO_OP_SCHEMA, + withParsedProperties: () => NO_OP_OBJECT_SCHEMA as SerializationFormat.ObjectLikeSchema, + extend: () => NO_OP_OBJECT_SCHEMA, + passthrough: () => NO_OP_OBJECT_SCHEMA +}; + +/** + * PassthroughFormat - used when serialization is completely disabled. + * All schema builders return no-op schemas that don't generate any code. + * Data passes through without transformation. + */ +export class PassthroughFormat implements SerializationFormat { + public readonly name = "none" as const; + + constructor(_config: SerializationFormat.Config) { + // No configuration needed for passthrough format + } + + // All schema builders return no-op schemas + public object = (_properties: SerializationFormat.Property[]): SerializationFormat.ObjectSchema => + NO_OP_OBJECT_SCHEMA; + public objectWithoutOptionalProperties = ( + _properties: SerializationFormat.Property[] + ): SerializationFormat.ObjectSchema => NO_OP_OBJECT_SCHEMA; + public union = (_args: SerializationFormat.UnionArgs): SerializationFormat.ObjectLikeSchema => + NO_OP_OBJECT_SCHEMA as SerializationFormat.ObjectLikeSchema; + public undiscriminatedUnion = (_schemas: SerializationFormat.Schema[]): SerializationFormat.SchemaWithUtils => + NO_OP_SCHEMA; + public list = (_itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public set = (_itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public record = (_args: { + keySchema: SerializationFormat.Schema; + valueSchema: SerializationFormat.Schema; + }): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public enum = (_values: string[]): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public string = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public stringLiteral = (_literal: string): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public booleanLiteral = (_literal: boolean): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public date = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public number = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public bigint = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public boolean = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public any = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public unknown = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public never = (): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public lazy = (_schema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => NO_OP_SCHEMA; + public lazyObject = (_schema: SerializationFormat.Schema): SerializationFormat.ObjectSchema => NO_OP_OBJECT_SCHEMA; + + public Schema = { + _getReferenceToType: (_args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }): ts.TypeNode => { + return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword); + }, + _fromExpression: ( + _expression: ts.Expression, + _opts?: { isObject: boolean } + ): SerializationFormat.SchemaWithUtils => { + return NO_OP_SCHEMA; + }, + _visitMaybeValid: ( + _referenceToMaybeValid: ts.Expression, + _visitor: { + valid: (referenceToValue: ts.Expression) => ts.Statement[]; + invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; + } + ): ts.Statement[] => { + return []; + } + }; + + public ObjectSchema = { + _getReferenceToType: (_args: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }): ts.TypeNode => { + return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword); + } + }; + + public MaybeValid = { + ok: "ok" as const, + Valid: { + value: "value" as const + }, + Invalid: { + errors: "errors" as const + } + }; + + public ValidationError = { + path: "path" as const, + message: "message" as const + }; + + public getRuntimeDependencies(): Record { + return {}; + } + + public getRuntimeFilePatterns(): { patterns: string[]; ignore?: string[] } | null { + return null; // No runtime files needed + } +} diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZodFormat.ts b/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZodFormat.ts new file mode 100644 index 000000000000..f7171c387086 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZodFormat.ts @@ -0,0 +1,1339 @@ +import { ts } from "ts-morph"; + +import { ImportsManager } from "../../imports-manager"; +import { SerializationFormat } from "../SerializationFormat"; + +/** + * Zod version to use as dependency. + * @todo Make this configurable from a client standpoint. This would be complex to implement because it would likely + * involve scanning Zod's api at the given version and code-generating based on the results of the scan. + */ +export const ZOD_VERSION = "^3.23.0"; + +/** + * Base schema implementation for Zod format. + */ +interface ZodBaseSchema extends SerializationFormat.Schema { + toExpression: () => ts.Expression; + isOptional: boolean; + isNullable: boolean; + + /** + * Optional: generate expression to serialize value to JSON. + * If not provided, value passes through unchanged. + * Used for types that need transformation (Set → Array, etc.) + */ + toJsonExpression?: (parsed: ts.Expression) => ts.Expression; +} + +/** + * Helper to create a property access expression like `z.string()`. + * + * @example + * // createZodCall("string") generates: + * z.string() + * + * // createZodCall("literal", [ts.factory.createStringLiteral("foo")]) generates: + * z.literal("foo") + */ +function createZodCall(methodName: string, args: ts.Expression[] = []): ts.Expression { + // AST structure: CallExpression(PropertyAccessExpression(Identifier("z"), Identifier(methodName)), args) + return ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("z"), + ts.factory.createIdentifier(methodName) + ), + undefined, // type arguments (generics) - not needed for Zod calls + args + ); +} + +/** + * Helper to chain a method call on an expression. + * + * @example + * // chainMethod(z.string(), "optional") generates: + * z.string().optional() + * + * // chainMethod(z.array(z.string()), "transform", [transformFn]) generates: + * z.array(z.string()).transform(transformFn) + */ +function chainMethod(expr: ts.Expression, methodName: string, args: ts.Expression[] = []): ts.Expression { + // AST structure: CallExpression(PropertyAccessExpression(expr, Identifier(methodName)), args) + return ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(expr, ts.factory.createIdentifier(methodName)), + undefined, // type arguments + args + ); +} + +/** + * ZodFormat - generates Zod schema code. + * Uses Zod as an npm dependency instead of bundled runtime. + */ +export class ZodFormat implements SerializationFormat { + public readonly name = "zod" as const; + + private importsManager?: ImportsManager; + private hasAddedZodImport = false; + + constructor(_config: SerializationFormat.Config, importsManager?: ImportsManager) { + this.importsManager = importsManager; + } + + /** + * Ensure the zod import is added to the current file + */ + private ensureZodImport(): void { + if (!this.hasAddedZodImport && this.importsManager) { + this.importsManager.addImport("zod", { namedImports: ["z"] }); + this.hasAddedZodImport = true; + } + } + + /** + * Create a zod call expression and ensure the import is added + */ + private zodCall(methodName: string, args: ts.Expression[] = []): ts.Expression { + this.ensureZodImport(); + return createZodCall(methodName, args); + } + + // ==================== Schema Utilities ==================== + + private getSchemaUtils( + baseSchema: ZodBaseSchema + ): Omit { + return { + nullable: () => this.nullable(baseSchema), + optional: () => this.optional(baseSchema), + optionalNullable: () => this.optionalNullable(baseSchema), + parse: (raw, _opts) => { + // Zod uses .parse() directly + return chainMethod(baseSchema.toExpression(), "parse", [raw]); + }, + json: (parsed, _opts) => { + // Use schema-specific serialization if defined, otherwise pass through + if (baseSchema.toJsonExpression) { + return baseSchema.toJsonExpression(parsed); + } + return parsed; + }, + parseOrThrow: (raw, _opts) => { + // Same as parse() for Zod - it throws by default + return chainMethod(baseSchema.toExpression(), "parse", [raw]); + }, + jsonOrThrow: (parsed, _opts) => { + // Use schema-specific serialization if defined, otherwise pass through + if (baseSchema.toJsonExpression) { + return baseSchema.toJsonExpression(parsed); + } + return parsed; + }, + transform: ({ transform, untransform }) => { + // Zod's transform only handles parse direction (JSON → TypeScript) + // We use .transform() for parse and store untransform separately + // + // Generated code example: + // z.string().transform((val) => new Date(val)) + // + // Note: `untransform` is used for the reverse direction (TypeScript → JSON) + // and is handled via toJsonExpression elsewhere + const transformedExpr = chainMethod(baseSchema.toExpression(), "transform", [transform]); + const newBase: ZodBaseSchema = { + isOptional: baseSchema.isOptional, + isNullable: baseSchema.isNullable, + toExpression: () => transformedExpr + }; + return { + ...newBase, + ...this.getSchemaUtils(newBase) + }; + } + }; + } + + /** + * Wrap schema to allow null values. + * + * @example + * // For z.string().nullable(), generates: + * z.string().nullable() + * + * // If inner schema has toJsonExpression (e.g., Date → ISO string), generates serialization: + * value != null ? value.toISOString() : null + */ + private nullable(schema: ZodBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZodBaseSchema = { + isOptional: schema.isOptional, + isNullable: true, + toExpression: () => chainMethod(schema.toExpression(), "nullable"), + // Preserve inner serialization, handling null: value != null ? transform(value) : null + // Use != (loose inequality) to catch both null and undefined + toJsonExpression: schema.toJsonExpression + ? (parsed) => + // AST for: parsed != null ? innerTransform(parsed) : null + ts.factory.createConditionalExpression( + ts.factory.createBinaryExpression( + parsed, + ts.SyntaxKind.ExclamationEqualsToken, // != (loose equality catches null & undefined) + ts.factory.createNull() + ), + undefined, // question token position + schema.toJsonExpression!(parsed), // whenTrue: apply inner transform + undefined, // colon token position + ts.factory.createNull() // whenFalse: return null + ) + : undefined + }; + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + /** + * Wrap schema to allow undefined values. + */ + private optional(schema: ZodBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZodBaseSchema = { + isOptional: true, + isNullable: schema.isNullable, + toExpression: () => chainMethod(schema.toExpression(), "optional"), + // Preserve inner serialization, handling undefined: value != null ? transform(value) : value + // Use != (loose inequality) to check for both null and undefined + toJsonExpression: schema.toJsonExpression + ? (parsed) => + ts.factory.createConditionalExpression( + ts.factory.createBinaryExpression( + parsed, + ts.SyntaxKind.ExclamationEqualsToken, // != (loose) to catch both null and undefined + ts.factory.createNull() + ), + undefined, + schema.toJsonExpression!(parsed), + undefined, + parsed + ) + : undefined + }; + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + /** + * Wrap schema to allow both null and undefined values. + */ + private optionalNullable(schema: ZodBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZodBaseSchema = { + isOptional: true, + isNullable: true, + toExpression: () => chainMethod(chainMethod(schema.toExpression(), "optional"), "nullable"), + // Preserve inner serialization, handling null/undefined + // Use != (loose inequality) to catch both null and undefined + toJsonExpression: schema.toJsonExpression + ? (parsed) => + ts.factory.createConditionalExpression( + ts.factory.createBinaryExpression( + parsed, + ts.SyntaxKind.ExclamationEqualsToken, // != (loose) + ts.factory.createNull() + ), + undefined, + schema.toJsonExpression!(parsed), + undefined, + parsed + ) + : undefined + }; + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + // ==================== Object-like Utilities ==================== + + /** + * Creates utilities for object-like schemas (objects and unions). + * + * withParsedProperties adds computed properties to the parsed result using Zod's transform. + */ + private getObjectLikeUtils( + _objectLike: ZodBaseSchema + ): Pick { + return { + withParsedProperties: (additionalProperties: SerializationFormat.AdditionalProperty[]) => { + // Zod doesn't have direct equivalent of withParsedProperties + // We use .transform() to add computed properties after parsing + if (additionalProperties.length === 0) { + return _objectLike as unknown as SerializationFormat.ObjectLikeSchema; + } + + // Generate AST for: + // schema.transform((parsed) => ({ + // ...parsed, + // computedProp1: computeValue1(parsed), + // computedProp2: computeValue2(parsed), + // })) + const transformExpr = ts.factory.createArrowFunction( + undefined, // modifiers + undefined, // type parameters + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "parsed")], + undefined, // return type + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createObjectLiteralExpression( + [ + // Spread original parsed properties: ...parsed + ts.factory.createSpreadAssignment(ts.factory.createIdentifier("parsed")), + // Add computed properties: key: getValue(parsed) + ...additionalProperties.map((prop) => { + const value = prop.getValue({ + getReferenceToParsed: () => ts.factory.createIdentifier("parsed") + }); + return ts.factory.createPropertyAssignment(prop.key, value); + }) + ], + true // multiLine + ) + ); + + const transformedExpr = chainMethod(_objectLike.toExpression(), "transform", [transformExpr]); + const newBase: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => transformedExpr + }; + + return { + ...newBase, + ...this.getSchemaUtils(newBase), + ...this.getObjectLikeUtils(newBase) + }; + } + }; + } + + // ==================== Object Utilities ==================== + + private getObjectUtils( + objectSchema: ZodBaseSchema + ): Pick { + return { + extend: (extension: SerializationFormat.Schema) => { + // Zod uses .merge() for extending objects + const extendedExpr = chainMethod(objectSchema.toExpression(), "merge", [extension.toExpression()]); + const newBase: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => extendedExpr + }; + return { + ...newBase, + ...this.getSchemaUtils(newBase), + ...this.getObjectLikeUtils(newBase), + ...this.getObjectUtils(newBase) + }; + }, + passthrough: () => { + // Zod has .passthrough() for allowing extra properties + const passthroughExpr = chainMethod(objectSchema.toExpression(), "passthrough"); + const newBase: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => passthroughExpr + }; + return { + ...newBase, + ...this.getSchemaUtils(newBase), + ...this.getObjectLikeUtils(newBase), + ...this.getObjectUtils(newBase) + }; + } + }; + } + + // ==================== Object Schema Builders ==================== + + /** + * Creates a Zod object schema from property definitions. + * + * Handles two complexities: + * 1. Key renaming: Fern allows different names for wire format (raw) vs TypeScript (parsed) + * 2. Nested serialization: Properties may need custom JSON serialization (e.g., Date → ISO string) + * + * @example + * // For properties [{key: {raw: "user_name", parsed: "userName"}, value: z.string()}] + * // Generates parsing schema: + * z.object({ user_name: z.string() }).transform((data) => ({ userName: data.user_name })) + * + * // And serialization (toJsonExpression): + * { user_name: parsed.userName } + */ + public object = (properties: SerializationFormat.Property[]): SerializationFormat.ObjectSchema => { + // Check if any property has toJsonExpression (needs serialization transform) + const propsWithJsonTransform = properties.filter((p) => (p.value as ZodBaseSchema).toJsonExpression != null); + + /** + * Creates a toJsonExpression for objects that recursively transforms properties needing serialization. + * + * When property keys differ between raw/parsed (e.g., NestedObject vs nestedObject), + * we must explicitly map ALL properties to avoid having both keys in the output. + */ + const hasKeyRenaming = properties.some((p) => p.key.raw !== p.key.parsed); + const createObjectJsonExpression = (parsed: ts.Expression): ts.Expression => { + if (propsWithJsonTransform.length === 0 && !hasKeyRenaming) { + return parsed; // No transform needed - O(1) + } + + // If there's key renaming OR transforms, we must explicitly map all properties + // to ensure correct key names and proper serialization + const propAssignments: ts.ObjectLiteralElementLike[] = properties.map((prop) => { + const propSchema = prop.value as ZodBaseSchema; + const propAccess = ts.factory.createPropertyAccessExpression(parsed, prop.key.parsed); + + // If this property has a toJsonExpression, use it + if (propSchema.toJsonExpression) { + // If property is optional or nullable, wrap with null check + const transformExpr = + propSchema.isOptional || propSchema.isNullable + ? ts.factory.createConditionalExpression( + ts.factory.createBinaryExpression( + propAccess, + ts.SyntaxKind.ExclamationEqualsToken, // != (loose) + ts.factory.createNull() + ), + undefined, + propSchema.toJsonExpression(propAccess), + undefined, + propAccess + ) + : propSchema.toJsonExpression(propAccess); + return ts.factory.createPropertyAssignment(prop.key.raw, transformExpr); + } + + // No transform needed - just map the key + return ts.factory.createPropertyAssignment(prop.key.raw, propAccess); + }); + + return ts.factory.createObjectLiteralExpression(propAssignments, true); + }; + + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + const propAssignments = properties.map((prop) => { + // Handle property key renaming + // Zod doesn't have built-in support for raw/parsed key mapping + // We'll use the parsed key and handle renaming in transform if needed + return ts.factory.createPropertyAssignment( + ts.factory.createStringLiteral(prop.key.raw), // Use raw key for parsing + prop.value.toExpression() + ); + }); + + return this.zodCall("object", [ts.factory.createObjectLiteralExpression(propAssignments, true)]); + }, + // Need toJsonExpression if there are transforms OR key renamings + toJsonExpression: + propsWithJsonTransform.length > 0 || hasKeyRenaming ? createObjectJsonExpression : undefined + }; + + // If any properties have different raw/parsed keys, wrap with transform for parsing + // This transforms from wire format keys to TypeScript keys after Zod parses + if (hasKeyRenaming) { + const transformedBase: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + const inner = baseSchema.toExpression(); + // Generate AST for key renaming transform: + // z.object({ raw_key: z.string() }).transform((data) => ({ + // parsedKey: data.raw_key, + // anotherParsedKey: data.another_raw_key, + // })) + const transformExpr = ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "data")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createObjectLiteralExpression( + // Map each property: parsedKey: data.rawKey + properties.map((prop) => + ts.factory.createPropertyAssignment( + prop.key.parsed, // TypeScript property name + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("data"), + prop.key.raw // Wire format property name + ) + ) + ), + true // multiLine + ) + ); + return chainMethod(inner, "transform", [transformExpr]); + }, + // Need toJsonExpression if there are transforms OR key renamings + toJsonExpression: + propsWithJsonTransform.length > 0 || hasKeyRenaming ? createObjectJsonExpression : undefined + }; + return { + ...transformedBase, + ...this.getSchemaUtils(transformedBase), + ...this.getObjectLikeUtils(transformedBase), + ...this.getObjectUtils(transformedBase) + }; + } + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + }; + + public objectWithoutOptionalProperties = ( + properties: SerializationFormat.Property[] + ): SerializationFormat.ObjectSchema => { + // In Zod, we use .strict() to disallow extra properties + // For "without optional properties", we just create a regular object + // The optionality is handled at the property level + return this.object(properties); + }; + + // ==================== Union Schema Builders ==================== + + /** + * Creates a discriminated union schema. + * + * @example + * // For a union with discriminant "type" and variants "dog" | "cat": + * z.discriminatedUnion("type", [ + * z.object({ type: z.literal("dog") }).merge(DogSchema as z.AnyZodObject), + * z.object({ type: z.literal("cat") }).merge(CatSchema as z.AnyZodObject), + * ]) + */ + public union = ({ + parsedDiscriminant, + rawDiscriminant, + singleUnionTypes + }: SerializationFormat.UnionArgs): SerializationFormat.ObjectLikeSchema => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + this.ensureZodImport(); + // Use z.discriminatedUnion for better performance than z.union + // Each variant is: z.object({ discriminant: z.literal("value") }).merge(propsSchema) + const variants = singleUnionTypes.map((variant) => { + // Create discriminant property: { type: z.literal("dog") } + const discriminantProp = ts.factory.createPropertyAssignment( + ts.factory.createStringLiteral(rawDiscriminant), + this.zodCall("literal", [ts.factory.createStringLiteral(variant.discriminantValue)]) + ); + + // Get the properties from the non-discriminant schema + // We need to merge them with the discriminant + // Cast to z.AnyZodObject because the imported schema is typed as z.ZodType + // but .merge() requires ZodObject - this is a TypeScript type assertion, not runtime + const schemaWithCast = ts.factory.createAsExpression( + variant.nonDiscriminantProperties.toExpression(), + ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName(ts.factory.createIdentifier("z"), "AnyZodObject"), + undefined + ) + ); + // Combine: z.object({ type: z.literal("dog") }).merge(DogPropsSchema) + return chainMethod( + this.zodCall("object", [ts.factory.createObjectLiteralExpression([discriminantProp], false)]), + "merge", + [schemaWithCast] + ); + }); + + // Final: z.discriminatedUnion("type", [variant1, variant2, ...]) + return this.zodCall("discriminatedUnion", [ + ts.factory.createStringLiteral(rawDiscriminant), + ts.factory.createArrayLiteralExpression(variants, true) + ]); + } + }; + + // Handle discriminant renaming if needed + if (parsedDiscriminant !== rawDiscriminant) { + const transformedBase: ZodBaseSchema = { + ...baseSchema, + toExpression: () => { + const inner = baseSchema.toExpression(); + const transformExpr = ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "data")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createSpreadAssignment(ts.factory.createIdentifier("data")), + ts.factory.createPropertyAssignment( + parsedDiscriminant, + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("data"), + rawDiscriminant + ) + ) + ], + true + ) + ); + return chainMethod(inner, "transform", [transformExpr]); + } + }; + return { + ...transformedBase, + ...this.getSchemaUtils(transformedBase), + ...this.getObjectLikeUtils(transformedBase) + }; + } + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema) + }; + }; + + public undiscriminatedUnion = (schemas: SerializationFormat.Schema[]): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + return this.zodCall("union", [ + ts.factory.createArrayLiteralExpression( + schemas.map((s) => s.toExpression()), + false + ) + ]); + } + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Collection Schema Builders ==================== + + /** + * Creates an array/list schema. + * + * @example + * // Parsing schema: + * z.array(z.string()) + * + * // Serialization (toJsonExpression) for Date[] items: + * parsed.map((item) => item.toISOString()) + */ + public list = (itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("array", [itemSchema.toExpression()]), + // If items need serialization, map over array and serialize each item + // Generate: parsed.map((item) => itemTransform(item)) + toJsonExpression: itemSchema.toJsonExpression + ? (parsed) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(parsed, "map"), + undefined, + [ + // Arrow function: (item) => itemTransform(item) + ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "item")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + itemSchema.toJsonExpression!(ts.factory.createIdentifier("item")) + ) + ] + ) + : undefined + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + /** + * Creates a Set schema. + * + * JSON wire format uses arrays, so we transform: + * - Parsing: JSON array → JavaScript Set via z.array().transform(arr => new Set(arr)) + * - Serialization: JavaScript Set → JSON array via Array.from(set) + * + * @example + * // Parsing schema: + * z.array(z.string()).transform((arr) => new Set(arr)) + * + * // Serialization (toJsonExpression) for Set: + * Array.from(mySet).map((item) => item.toISOString()) + */ + public set = (itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + // Generate: z.array(itemSchema).transform((arr) => new Set(arr)) + const arraySchema = this.zodCall("array", [itemSchema.toExpression()]); + // Arrow function: (arr) => new Set(arr) + const transformFn = ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "arr")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + // new Set(arr) + ts.factory.createNewExpression(ts.factory.createIdentifier("Set"), undefined, [ + ts.factory.createIdentifier("arr") + ]) + ); + return chainMethod(arraySchema, "transform", [transformFn]); + }, + // For JSON serialization, convert Set to Array + // If items need serialization: Array.from(set).map(item => itemTransform(item)) + // Otherwise just: Array.from(set) + toJsonExpression: itemSchema.toJsonExpression + ? (parsed) => + // Generate: Array.from(parsed).map((item) => itemTransform(item)) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + // Array.from(parsed) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("Array"), + "from" + ), + undefined, + [parsed] + ), + "map" + ), + undefined, + [ + // Arrow function: (item) => itemTransform(item) + ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "item")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + itemSchema.toJsonExpression!(ts.factory.createIdentifier("item")) + ) + ] + ) + : (parsed) => + // Generate: Array.from(parsed) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier("Array"), "from"), + undefined, + [parsed] + ) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + /** + * Creates a Record/Map schema (key-value dictionary). + * + * @example + * // Parsing schema: + * z.record(z.string(), z.date()) + * + * // Serialization (toJsonExpression) for Record: + * Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, v.toISOString()])) + */ + public record = ({ + keySchema: _keySchema, + valueSchema + }: { + keySchema: SerializationFormat.Schema; + valueSchema: SerializationFormat.Schema; + }): SerializationFormat.SchemaWithUtils => { + // JSON object keys are always strings, so we use z.string() for the key + // regardless of the declared key type (e.g., even if Fern declares map) + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("record", [this.zodCall("string"), valueSchema.toExpression()]), + // If values need serialization, transform each value in the record + // Pattern: Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, transform(v)])) + toJsonExpression: valueSchema.toJsonExpression + ? (parsed) => + // Generate: Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, valueTransform(v)])) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("Object"), + "fromEntries" + ), + undefined, + [ + // Object.entries(parsed).map(...) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + // Object.entries(parsed) + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("Object"), + "entries" + ), + undefined, + [parsed] + ), + "map" + ), + undefined, + [ + // Arrow function with destructuring: ([k, v]) => [k, valueTransform(v)] + ts.factory.createArrowFunction( + undefined, + undefined, + [ + // Destructuring parameter: [k, v] + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + ts.factory.createArrayBindingPattern([ + ts.factory.createBindingElement(undefined, undefined, "k"), + ts.factory.createBindingElement(undefined, undefined, "v") + ]) + ) + ], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + // Return [k, valueTransform(v)] + ts.factory.createArrayLiteralExpression([ + ts.factory.createIdentifier("k"), + valueSchema.toJsonExpression!(ts.factory.createIdentifier("v")) + ]) + ) + ] + ) + ] + ) + : undefined + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Enum Schema Builder ==================== + + public enum = (values: string[]): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.zodCall("enum", [ + ts.factory.createArrayLiteralExpression( + values.map((v) => ts.factory.createStringLiteral(v)), + false + ) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Primitive Schema Builders ==================== + + public string = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("string") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public stringLiteral = (literal: string): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("literal", [ts.factory.createStringLiteral(literal)]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public booleanLiteral = (literal: boolean): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("literal", [literal ? ts.factory.createTrue() : ts.factory.createFalse()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public number = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("number") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public bigint = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("bigint") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public boolean = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("boolean") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + /** + * Creates a Date schema. + * + * JSON wire format uses ISO 8601 strings, so we transform: + * - Parsing: ISO string → JavaScript Date via z.string().transform(s => new Date(s)) + * - Serialization: JavaScript Date → ISO string via date.toISOString() + * + * @example + * // Parsing schema: + * z.string().transform((s) => new Date(s)) + * + * // Serialization (toJsonExpression): + * myDate.toISOString() + */ + public date = (): SerializationFormat.SchemaWithUtils => { + // Zod's z.date() parses Date objects, but we need to parse ISO strings from JSON + // Use z.string().transform() for ISO string → Date parsing + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => { + // Generate: z.string().transform((s) => new Date(s)) + return chainMethod(this.zodCall("string"), "transform", [ + // Arrow function: (s) => new Date(s) + ts.factory.createArrowFunction( + undefined, + undefined, + [ts.factory.createParameterDeclaration(undefined, undefined, undefined, "s")], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + // new Date(s) + ts.factory.createNewExpression(ts.factory.createIdentifier("Date"), undefined, [ + ts.factory.createIdentifier("s") + ]) + ) + ]); + }, + // For JSON serialization, convert Date back to ISO string + // Generate: parsed.toISOString() + toJsonExpression: (parsed) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(parsed, "toISOString"), + undefined, + [] + ) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public any = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: true, // any includes null + toExpression: () => this.zodCall("any") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public unknown = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: true, // unknown can be undefined + isNullable: false, + toExpression: () => this.zodCall("unknown") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public never = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.zodCall("never") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Lazy Schema Builders ==================== + + public lazy = (schema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: schema.isOptional, + isNullable: schema.isNullable, + toExpression: () => + this.zodCall("lazy", [ + ts.factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + schema.toExpression() + ) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public lazyObject = (schema: SerializationFormat.Schema): SerializationFormat.ObjectSchema => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: schema.isNullable, + toExpression: () => + this.zodCall("lazy", [ + ts.factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + schema.toExpression() + ) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + }; + + // ==================== Type Utilities ==================== + + /** + * Type utilities for generating schema type references. + * + * In Zod format, each schema file exports a wrapper object: + * ```typescript + * export const MyTypeSchema = { + * _schema: z.object({ ... }), + * parse: (raw: unknown): MyType => _schema.parse(raw), + * json: (parsed: MyType): MyTypeRaw => ({ ... }) + * } + * ``` + * + * These utilities generate the TypeScript types for such wrapper objects. + */ + public Schema = { + /** + * Generates type: { _schema: z.ZodTypeAny; parse: (raw: unknown) => Parsed; json: (parsed: Parsed) => Raw } + */ + _getReferenceToType: ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => { + this.ensureZodImport(); + // Generate a TypeLiteralNode with three properties: _schema, parse, json + return ts.factory.createTypeLiteralNode([ + // _schema: z.ZodTypeAny + ts.factory.createPropertySignature( + undefined, + "_schema", + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName(ts.factory.createIdentifier("z"), "ZodTypeAny"), + undefined + ) + ), + // parse: (raw: unknown) => Parsed + ts.factory.createPropertySignature( + undefined, + "parse", + undefined, + ts.factory.createFunctionTypeNode( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + "raw", + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) + ) + ], + parsedShape + ) + ), + // json: (parsed: Parsed) => Raw + ts.factory.createPropertySignature( + undefined, + "json", + undefined, + ts.factory.createFunctionTypeNode( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + "parsed", + undefined, + parsedShape + ) + ], + rawShape + ) + ) + ]); + }, + + /** + * Creates a schema from an imported schema reference expression. + * + * In Zod format, schemas are wrapped in objects with { _schema, parse, json } structure: + * - _schema: The actual Zod schema (z.object, z.string, etc.) + * - parse: Function to parse JSON → TypeScript type + * - json: Function to serialize TypeScript type → JSON + * + * @example + * // For an imported schema reference `UserSchema`: + * // toExpression() returns: UserSchema._schema (for use in z.array(UserSchema._schema)) + * // toJsonExpression(parsed) returns: UserSchema.json(parsed) + */ + _fromExpression: ( + expression: ts.Expression, + opts?: { isObject: boolean } + ): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZodBaseSchema = { + isOptional: false, + isNullable: false, + // Return expression._schema for use in schema composition (z.array(), z.record(), etc.) + // This accesses the raw Zod schema from the wrapper object + toExpression: () => ts.factory.createPropertyAccessExpression(expression, "_schema"), + // Generate: schemaRef.json(parsed) for serialization + // This calls the json method on the wrapper object generated in writeSchemaToFile + toJsonExpression: (parsed) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(expression, "json"), + undefined, + [parsed] + ) + }; + if (opts?.isObject) { + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + } as SerializationFormat.SchemaWithUtils; + } + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }, + + /** + * Generates if/else statements for handling Zod's safeParse result. + * + * Zod's safeParse returns: { success: boolean; data?: T; error?: ZodError } + * + * @example + * // Generated code: + * if (result.success) { + * // valid branch: use result.data + * } else { + * // invalid branch: use result.error + * } + */ + _visitMaybeValid: ( + referenceToMaybeValid: ts.Expression, + visitor: { + valid: (referenceToValue: ts.Expression) => ts.Statement[]; + invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; + } + ): ts.Statement[] => { + // Generate: if (result.success) { ...validStatements } else { ...invalidStatements } + return [ + ts.factory.createIfStatement( + // Condition: result.success + ts.factory.createPropertyAccessExpression(referenceToMaybeValid, "success"), + // Then block: visitor.valid(result.data) + ts.factory.createBlock( + visitor.valid(ts.factory.createPropertyAccessExpression(referenceToMaybeValid, "data")), + true // multiLine + ), + // Else block: visitor.invalid(result.error) + ts.factory.createBlock( + visitor.invalid(ts.factory.createPropertyAccessExpression(referenceToMaybeValid, "error")), + true // multiLine + ) + ) + ]; + } + }; + + public ObjectSchema = { + _getReferenceToType: ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => { + // Generate same wrapper type as Schema - objects use the same structure + this.ensureZodImport(); + return ts.factory.createTypeLiteralNode([ + // _schema: z.ZodTypeAny + ts.factory.createPropertySignature( + undefined, + "_schema", + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName(ts.factory.createIdentifier("z"), "ZodTypeAny"), + undefined + ) + ), + // parse: (raw: unknown) => Parsed + ts.factory.createPropertySignature( + undefined, + "parse", + undefined, + ts.factory.createFunctionTypeNode( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + "raw", + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) + ) + ], + parsedShape + ) + ), + // json: (parsed: Parsed) => Raw + ts.factory.createPropertySignature( + undefined, + "json", + undefined, + ts.factory.createFunctionTypeNode( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + "parsed", + undefined, + parsedShape + ) + ], + rawShape + ) + ) + ]); + } + }; + + // Zod's safeParse result field names + public MaybeValid = { + ok: "success" as const, + Valid: { + value: "data" as const + }, + Invalid: { + errors: "error" as const + } + }; + + // Zod's error field names + public ValidationError = { + path: "path" as const, + message: "message" as const + }; + + // ==================== Runtime Configuration ==================== + + public getRuntimeDependencies(): Record { + return { + zod: ZOD_VERSION + }; + } + + public getRuntimeFilePatterns(): { patterns: string[]; ignore?: string[] } | null { + // Zod uses npm dependency, no bundled files needed + return null; + } +} diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZurgFormat.ts b/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZurgFormat.ts new file mode 100644 index 000000000000..2d869742d7b2 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/formats/ZurgFormat.ts @@ -0,0 +1,874 @@ +import { ts } from "ts-morph"; + +import { CoreUtility } from "../../core-utilities/CoreUtility"; +import { ImportsManager } from "../../imports-manager"; +import { SerializationFormat } from "../SerializationFormat"; + +/** + * Manifest for the Zurg runtime files. + * Used by CoreUtilitiesManager to copy runtime files to generated SDK. + * + * @abstract @Zurg validation schema files. Note that while they are still currently bundled in the source, the + * next phase will to be move them into a separate public npm package that can be imported in the same manner as Zod and other libs. + * A possible alternative to that is to generate them using ASTs but that would end up being more complex to maintain. + */ +export const ZURG_MANIFEST: CoreUtility.Manifest = { + name: "schemas", + pathInCoreUtilities: { nameOnDisk: "schemas", exportDeclaration: { namespaceExport: "serialization" } }, + getFilesPatterns: () => { + return { patterns: ["src/core/schemas/**", "tests/unit/schemas/**"] }; + } +}; + +/** + * Base schema implementation for Zurg format + */ +interface ZurgBaseSchema extends SerializationFormat.Schema { + toExpression: () => ts.Expression; + isOptional: boolean; + isNullable: boolean; +} + +/** + * Helper to create a property access expression like `core.serialization.string()`. + */ +function createSerializationCall(methodName: string, args: ts.Expression[] = []): ts.Expression { + return ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("core"), + ts.factory.createIdentifier("serialization") + ), + ts.factory.createIdentifier(methodName) + ), + undefined, + args + ); +} + +/** + * Helper to chain a method call on an expression + */ +function chainMethod(expr: ts.Expression, methodName: string, args: ts.Expression[] = []): ts.Expression { + return ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(expr, ts.factory.createIdentifier(methodName)), + undefined, + args + ); +} + +/** + * ZurgFormat - generates Zurg schema code. + * Uses locally bundled runtime files with simple import pattern. + */ +export class ZurgFormat implements SerializationFormat { + public readonly name = "zurg" as const; + + private importsManager?: ImportsManager; + private hasAddedSerializationImport = false; + private generateEndpointMetadata: boolean; + + constructor(config: SerializationFormat.Config, importsManager?: ImportsManager) { + this.importsManager = importsManager; + this.generateEndpointMetadata = config.generateEndpointMetadata; + } + + /** + * Ensure the core import is added to the current file + */ + private ensureSerializationImport(): void { + if (!this.hasAddedSerializationImport && this.importsManager) { + this.importsManager.addImportFromRoot("core", { namespaceImport: "core" }); + this.hasAddedSerializationImport = true; + } + } + + /** + * Create a serialization call expression and ensure the import is added + */ + private serializationCall(methodName: string, args: ts.Expression[] = []): ts.Expression { + this.ensureSerializationImport(); + return createSerializationCall(methodName, args); + } + + // ==================== Schema Utilities ==================== + + private getSchemaUtils( + baseSchema: ZurgBaseSchema + ): Omit { + return { + nullable: () => this.nullable(baseSchema), + optional: () => this.optional(baseSchema), + optionalNullable: () => this.optionalNullable(baseSchema), + parse: (raw, opts) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "parse"), + undefined, + [raw, ...this.constructSchemaOptionsArgs(opts)] + ), + json: (parsed, opts) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "json"), + undefined, + [parsed, ...this.constructSchemaOptionsArgs(opts)] + ), + parseOrThrow: (raw, opts) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "parseOrThrow"), + undefined, + [raw, ...this.constructSchemaOptionsArgs(opts)] + ), + jsonOrThrow: (parsed, opts) => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(baseSchema.toExpression(), "jsonOrThrow"), + undefined, + [parsed, ...this.constructSchemaOptionsArgs(opts)] + ), + transform: ({ + newShape, + transform, + untransform + }: { + newShape: ts.TypeNode | undefined; + transform: ts.Expression; + untransform: ts.Expression; + }) => + this.transform(baseSchema, { + newShape, + transformer: this.Schema._fromTransformers({ transform, untransform }) + }) + }; + } + + private constructSchemaOptionsArgs(schemaOptions: Required): ts.Expression[] { + const properties: ts.ObjectLiteralElementLike[] = []; + + if (schemaOptions.unrecognizedObjectKeys !== "fail") { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("unrecognizedObjectKeys"), + ts.factory.createStringLiteral(schemaOptions.unrecognizedObjectKeys) + ) + ); + } + if (schemaOptions.allowUnrecognizedUnionMembers) { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("allowUnrecognizedUnionMembers"), + ts.factory.createTrue() + ) + ); + } + if (schemaOptions.allowUnrecognizedEnumValues) { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("allowUnrecognizedEnumValues"), + ts.factory.createTrue() + ) + ); + } + if (schemaOptions.skipValidation) { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("skipValidation"), + ts.factory.createTrue() + ) + ); + } + if (schemaOptions.omitUndefined) { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("omitUndefined"), + ts.factory.createTrue() + ) + ); + } + if (schemaOptions.breadcrumbsPrefix.length > 0) { + properties.push( + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier("breadcrumbsPrefix"), + ts.factory.createArrayLiteralExpression( + schemaOptions.breadcrumbsPrefix.map((breadcrumb) => ts.factory.createStringLiteral(breadcrumb)) + ) + ) + ); + } + + if (properties.length > 0) { + return [ts.factory.createObjectLiteralExpression(properties)]; + } else { + return []; + } + } + + private nullable(schema: ZurgBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: true, + toExpression: () => chainMethod(schema.toExpression(), "nullable") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + private optional(schema: ZurgBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZurgBaseSchema = { + isOptional: true, + isNullable: false, + toExpression: () => chainMethod(schema.toExpression(), "optional") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + private optionalNullable(schema: ZurgBaseSchema): SerializationFormat.SchemaWithUtils { + const baseSchema: ZurgBaseSchema = { + isOptional: true, + isNullable: true, + toExpression: () => chainMethod(schema.toExpression(), "optionalNullable") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + private transform( + schema: ZurgBaseSchema, + { newShape, transformer }: { newShape: ts.TypeNode | undefined; transformer: ZurgBaseSchema } + ): SerializationFormat.SchemaWithUtils { + const baseSchema: ZurgBaseSchema = { + isOptional: transformer.isOptional, + isNullable: transformer.isNullable, + toExpression: () => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + schema.toExpression(), + ts.factory.createIdentifier("transform") + ), + newShape != null ? [newShape] : undefined, + [transformer.toExpression()] + ) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + } + + // ==================== Object-like Utilities ==================== + + private getObjectLikeUtils( + objectLike: ZurgBaseSchema + ): Pick { + return { + withParsedProperties: (additionalProperties: SerializationFormat.AdditionalProperty[]) => + this.withParsedProperties(objectLike, additionalProperties) + }; + } + + private withParsedProperties( + objectLike: ZurgBaseSchema, + additionalProperties: SerializationFormat.AdditionalProperty[] + ): SerializationFormat.ObjectLikeSchema { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + objectLike.toExpression(), + ts.factory.createIdentifier("withParsedProperties") + ), + undefined, + [ + ts.factory.createObjectLiteralExpression( + additionalProperties.map((property) => { + const parsedIdentifier = ts.factory.createIdentifier("parsed"); + const context = { didAccessParsed: false }; + const getReferenceToParsed = () => { + context.didAccessParsed = true; + return parsedIdentifier; + }; + + const value = property.getValue({ getReferenceToParsed }); + + return ts.factory.createPropertyAssignment( + property.key, + context.didAccessParsed + ? ts.factory.createArrowFunction( + undefined, + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + parsedIdentifier + ) + ], + undefined, + undefined, + value + ) + : ts.isCallExpression(value) + ? ts.factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + undefined, + value + ) + : value + ); + }), + true + ) + ] + ) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema) + }; + } + + // ==================== Object Utilities ==================== + + private getObjectUtils( + objectSchema: ZurgBaseSchema + ): Pick { + return { + extend: (extension) => this.extend(objectSchema, extension as ZurgBaseSchema), + passthrough: () => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => chainMethod(objectSchema.toExpression(), "passthrough") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + } + }; + } + + private extend(objectSchema: ZurgBaseSchema, extension: ZurgBaseSchema): SerializationFormat.ObjectSchema { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => chainMethod(objectSchema.toExpression(), "extend", [extension.toExpression()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + } + + // ==================== Object Schema Builders ==================== + + public object = (properties: SerializationFormat.Property[]): SerializationFormat.ObjectSchema => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("object", [this.constructObjectLiteralForProperties(properties)]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + }; + + public objectWithoutOptionalProperties = ( + properties: SerializationFormat.Property[] + ): SerializationFormat.ObjectSchema => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.serializationCall("objectWithoutOptionalProperties", [ + this.constructObjectLiteralForProperties(properties) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + }; + + private constructObjectLiteralForProperties( + properties: SerializationFormat.Property[] + ): ts.ObjectLiteralExpression { + return ts.factory.createObjectLiteralExpression( + properties.map((property) => { + let value = property.value.toExpression(); + if (property.key.raw !== property.key.parsed) { + value = this.property(property.key.raw, value); + } + return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(property.key.parsed), value); + }), + true + ); + } + + private property(rawValue: string, value: ts.Expression): ts.Expression { + return this.serializationCall("property", [ts.factory.createStringLiteral(rawValue), value]); + } + + // ==================== Union Schema Builders ==================== + + public union = ({ + parsedDiscriminant, + rawDiscriminant, + singleUnionTypes + }: SerializationFormat.UnionArgs): SerializationFormat.ObjectLikeSchema => { + const discriminantArgument = + parsedDiscriminant === rawDiscriminant + ? ts.factory.createStringLiteral(parsedDiscriminant) + : this.discriminant({ parsedDiscriminant, rawDiscriminant }); + + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.serializationCall("union", [ + discriminantArgument, + ts.factory.createObjectLiteralExpression( + singleUnionTypes.map((singleUnionType) => + ts.factory.createPropertyAssignment( + ts.factory.createStringLiteral(singleUnionType.discriminantValue), + singleUnionType.nonDiscriminantProperties.toExpression() + ) + ), + true + ) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema) + }; + }; + + private discriminant({ + parsedDiscriminant, + rawDiscriminant + }: { + parsedDiscriminant: string; + rawDiscriminant: string; + }): ts.Expression { + return this.serializationCall("discriminant", [ + ts.factory.createStringLiteral(parsedDiscriminant), + ts.factory.createStringLiteral(rawDiscriminant) + ]); + } + + public undiscriminatedUnion = (schemas: SerializationFormat.Schema[]): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.serializationCall("undiscriminatedUnion", [ + ts.factory.createArrayLiteralExpression(schemas.map((schema) => schema.toExpression())) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Collection Schema Builders ==================== + + public list = (itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("list", [itemSchema.toExpression()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public set = (itemSchema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("set", [itemSchema.toExpression()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public record = ({ + keySchema, + valueSchema + }: { + keySchema: SerializationFormat.Schema; + valueSchema: SerializationFormat.Schema; + }): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("record", [keySchema.toExpression(), valueSchema.toExpression()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Enum Schema Builder ==================== + + public enum = (values: string[]): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.serializationCall("enum_", [ + ts.factory.createArrayLiteralExpression( + values.map((value) => ts.factory.createStringLiteral(value)) + ) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Primitive Schema Builders ==================== + + public string = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("string") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public stringLiteral = (literal: string): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("stringLiteral", [ts.factory.createStringLiteral(literal)]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public booleanLiteral = (literal: boolean): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => + this.serializationCall("booleanLiteral", [literal ? ts.factory.createTrue() : ts.factory.createFalse()]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public number = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("number") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public bigint = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("bigint") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public boolean = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("boolean") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public date = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("date") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public any = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: true, + toExpression: () => this.serializationCall("any") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public unknown = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: true, + isNullable: false, + toExpression: () => this.serializationCall("unknown") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public never = (): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => this.serializationCall("never") + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + // ==================== Lazy Schema Builders ==================== + + public lazy = (schema: SerializationFormat.Schema): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: schema.isOptional, + isNullable: schema.isNullable, + toExpression: () => + this.serializationCall("lazy", [ + ts.factory.createArrowFunction([], undefined, [], undefined, undefined, schema.toExpression()) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }; + + public lazyObject = (schema: SerializationFormat.Schema): SerializationFormat.ObjectSchema => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: schema.isNullable, + toExpression: () => + this.serializationCall("lazyObject", [ + ts.factory.createArrowFunction([], undefined, [], undefined, undefined, schema.toExpression()) + ]) + }; + + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + }; + }; + + // ==================== Type Utilities ==================== + + public Schema = { + _getReferenceToType: ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => { + this.ensureSerializationImport(); + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createQualifiedName(ts.factory.createIdentifier("core"), "serialization"), + "Schema" + ), + [rawShape, parsedShape] + ); + }, + + _fromExpression: ( + expression: ts.Expression, + opts?: { isObject: boolean } + ): SerializationFormat.SchemaWithUtils => { + const baseSchema: ZurgBaseSchema = { + isOptional: false, + isNullable: false, + toExpression: () => expression + }; + if (opts?.isObject) { + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema), + ...this.getObjectLikeUtils(baseSchema), + ...this.getObjectUtils(baseSchema) + } as SerializationFormat.SchemaWithUtils; + } + return { + ...baseSchema, + ...this.getSchemaUtils(baseSchema) + }; + }, + + _fromTransformers: ({ + transform, + untransform + }: { + transform: ts.Expression; + untransform: ts.Expression; + }): ZurgBaseSchema => { + return { + isOptional: false, + isNullable: false, + toExpression: () => + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment("transform", transform), + ts.factory.createPropertyAssignment("untransform", untransform) + ], + true + ) + }; + }, + + _visitMaybeValid: ( + referenceToMaybeValid: ts.Expression, + visitor: { + valid: (referenceToValue: ts.Expression) => ts.Statement[]; + invalid: (referenceToErrors: ts.Expression) => ts.Statement[]; + } + ): ts.Statement[] => { + return [ + ts.factory.createIfStatement( + ts.factory.createPropertyAccessExpression(referenceToMaybeValid, this.MaybeValid.ok), + ts.factory.createBlock( + visitor.valid( + ts.factory.createPropertyAccessExpression( + referenceToMaybeValid, + this.MaybeValid.Valid.value + ) + ), + true + ), + ts.factory.createBlock( + visitor.invalid( + ts.factory.createPropertyAccessExpression( + referenceToMaybeValid, + this.MaybeValid.Invalid.errors + ) + ), + true + ) + ) + ]; + } + }; + + public ObjectSchema = { + _getReferenceToType: ({ rawShape, parsedShape }: { rawShape: ts.TypeNode; parsedShape: ts.TypeNode }) => { + this.ensureSerializationImport(); + return ts.factory.createTypeReferenceNode( + ts.factory.createQualifiedName( + ts.factory.createQualifiedName(ts.factory.createIdentifier("core"), "serialization"), + "ObjectSchema" + ), + [rawShape, parsedShape] + ); + } + }; + + public MaybeValid = { + ok: "ok" as const, + Valid: { + value: "value" as const + }, + Invalid: { + errors: "errors" as const + } + }; + + public ValidationError = { + path: "path" as const, + message: "message" as const + }; + + // ==================== Runtime Configuration ==================== + + public getRuntimeDependencies(): Record { + // Zurg uses locally bundled runtime, no npm dependencies needed + return {}; + } + + public getRuntimeFilePatterns(): { patterns: string[]; ignore?: string[] } | null { + // Return the file patterns for Zurg runtime to be copied + return { patterns: ["src/core/schemas/**", "tests/unit/schemas/**"] }; + } +} diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/formats/index.ts b/generators/typescript/utils/commons/src/serialization-pipeline/formats/index.ts new file mode 100644 index 000000000000..8f9dab57bf37 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/formats/index.ts @@ -0,0 +1,3 @@ +export { PassthroughFormat } from "./PassthroughFormat"; +export { ZOD_VERSION, ZodFormat } from "./ZodFormat"; +export { ZURG_MANIFEST, ZurgFormat } from "./ZurgFormat"; diff --git a/generators/typescript/utils/commons/src/serialization-pipeline/index.ts b/generators/typescript/utils/commons/src/serialization-pipeline/index.ts new file mode 100644 index 000000000000..2fa04699b275 --- /dev/null +++ b/generators/typescript/utils/commons/src/serialization-pipeline/index.ts @@ -0,0 +1,7 @@ +export { PassthroughFormat, ZOD_VERSION, ZodFormat, ZURG_MANIFEST, ZurgFormat } from "./formats"; +export * from "./SerializationFormat"; +export { + type SerializationFormatType, + SerializationPipeline, + type SerializationPipelineConfig +} from "./SerializationPipeline"; diff --git a/generators/typescript/utils/commons/vitest.config.ts b/generators/typescript/utils/commons/vitest.config.ts new file mode 100644 index 000000000000..d11017dc6763 --- /dev/null +++ b/generators/typescript/utils/commons/vitest.config.ts @@ -0,0 +1 @@ +export { default } from "../../../../shared/vitest.config"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/.fern/metadata.json b/seed/ts-sdk/exhaustive/serde-layer-zod/.fern/metadata.json new file mode 100644 index 000000000000..4a881bff00a8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/.fern/metadata.json @@ -0,0 +1,8 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-typescript-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "serializationFormat": "zod" + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/.github/workflows/ci.yml b/seed/ts-sdk/exhaustive/serde-layer-zod/.github/workflows/ci.yml new file mode 100644 index 000000000000..836106996595 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +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/exhaustive/serde-layer-zod/.gitignore b/seed/ts-sdk/exhaustive/serde-layer-zod/.gitignore new file mode 100644 index 000000000000..72271e049c02 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +/dist \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/CONTRIBUTING.md b/seed/ts-sdk/exhaustive/serde-layer-zod/CONTRIBUTING.md new file mode 100644 index 000000000000..fe5bc2f77e0b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/CONTRIBUTING.md @@ -0,0 +1,133 @@ +# 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/exhaustive/serde-layer-zod/README.md b/seed/ts-sdk/exhaustive/serde-layer-zod/README.md new file mode 100644 index 000000000000..140b1fb505f3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/README.md @@ -0,0 +1,241 @@ +# 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/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) + +The Seed TypeScript library provides convenient access to the Seed APIs from TypeScript. + +## Installation + +```sh +npm i -s @fern/exhaustive +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ environment: "YOUR_BASE_URL", token: "YOUR_TOKEN" }); +await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); +``` + +## Request And Response Types + +The SDK exports all request and response types as TypeScript interfaces. Simply import them with the +following namespace: + +```typescript +import { SeedExhaustive } from "@fern/exhaustive"; + +const request: SeedExhaustive.GetWithInlinePath = { + ... +}; +``` + +## 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 { SeedExhaustiveError } from "@fern/exhaustive"; + +try { + await client.endpoints.container.getAndReturnListOfPrimitives(...); +} catch (err) { + if (err instanceof SeedExhaustiveError) { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(...).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 { SeedExhaustiveClient, logging } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + ... + 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 { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + ... + 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/exhaustive/serde-layer-zod/biome.json b/seed/ts-sdk/exhaustive/serde-layer-zod/biome.json new file mode 100644 index 000000000000..a777468e4ae2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/biome.json @@ -0,0 +1,74 @@ +{ + "$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/exhaustive/serde-layer-zod/package.json b/seed/ts-sdk/exhaustive/serde-layer-zod/package.json new file mode 100644 index 000000000000..20397bfc4323 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/package.json @@ -0,0 +1,80 @@ +{ + "name": "@fern/exhaustive", + "version": "0.0.1", + "private": false, + "repository": "github:exhaustive/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" + }, + "./serialization": { + "types": "./dist/cjs/serialization/index.d.ts", + "import": { + "types": "./dist/esm/serialization/index.d.mts", + "default": "./dist/esm/serialization/index.mjs" + }, + "require": { + "types": "./dist/cjs/serialization/index.d.ts", + "default": "./dist/cjs/serialization/index.js" + }, + "default": "./dist/cjs/serialization/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": { + "zod": "^3.23.0" + }, + "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/exhaustive/serde-layer-zod/pnpm-workspace.yaml b/seed/ts-sdk/exhaustive/serde-layer-zod/pnpm-workspace.yaml new file mode 100644 index 000000000000..6e4c395107df --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/pnpm-workspace.yaml @@ -0,0 +1 @@ +packages: ['.'] \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/reference.md b/seed/ts-sdk/exhaustive/serde-layer-zod/reference.md new file mode 100644 index 000000000000..b571afcab9fc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/reference.md @@ -0,0 +1,2761 @@ +# Reference +## Endpoints Container +
client.endpoints.container.getAndReturnListOfPrimitives({ ...params }) -> string[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnListOfObjects({ ...params }) -> SeedExhaustive.ObjectWithRequiredField[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnListOfObjects([{ + string: "string" + }, { + string: "string" + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnSetOfPrimitives({ ...params }) -> Set +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Set` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnSetOfObjects({ ...params }) -> SeedExhaustive.ObjectWithRequiredField[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnSetOfObjects([{ + string: "string" + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnMapPrimToPrim({ ...params }) -> Record +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnMapPrimToPrim({ + "string": "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Record` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnMapOfPrimToObject({ ...params }) -> Record +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnMapOfPrimToObject({ + "string": { + string: "string" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Record` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnOptional({ ...params }) -> SeedExhaustive.ObjectWithRequiredField | undefined +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnOptional({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints ContentType +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentTypeClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentTypeClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Enum +
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.enum.getAndReturnEnum("SUNNY"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.WeatherReport` + +
+
+ +
+
+ +**requestOptions:** `EnumClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints HttpMethods +
client.endpoints.httpMethods.testGet(id) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testGet("id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPost({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPost({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPut(id, { ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPut("id", { + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPatch(id, { ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPatch("id", { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testDelete(id) -> boolean +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testDelete("id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Object +
client.endpoints.object.getAndReturnWithOptionalField({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithOptionalField({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnWithRequiredField({ ...params }) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithRequiredField({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnWithMapOfMap({ ...params }) -> SeedExhaustive.ObjectWithMapOfMap +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithMapOfMap({ + map: { + "map": { + "map": "map" + } + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithMapOfMap` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithOptionalField({ ...params }) -> SeedExhaustive.NestedObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithOptionalField({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithRequiredField(string, { ...params }) -> SeedExhaustive.NestedObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**string:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList({ ...params }) -> SeedExhaustive.NestedObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } + }, { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Params +
client.endpoints.params.getWithPath(param) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithPath("param"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithInlinePath({ ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithInlinePath({ + param: "param" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithInlinePath` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with query param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithQuery({ + query: "query", + number: 1 +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithAllowMultipleQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with multiple of same query param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithAllowMultipleQuery({ + query: "query", + number: 1 +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithMultipleQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithPathAndQuery(param, { ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path and query params +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithPathAndQuery("param", { + query: "query" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithPathAndQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithInlinePathAndQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path and query params +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithInlinePathAndQuery({ + param: "param", + query: "query" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithInlinePathAndQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.modifyWithPath(param, { ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +PUT to update with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.modifyWithPath("param", "string"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.modifyWithInlinePath({ ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +PUT to update with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.modifyWithInlinePath({ + param: "param", + body: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.ModifyResourceAtInlinedPath` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Primitive +
client.endpoints.primitive.getAndReturnString({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnString("string"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnInt({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnInt(1); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnLong({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnLong(1000000); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDouble({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDouble(1.1); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnBool({ ...params }) -> boolean +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnBool(true); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `boolean` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDatetime({ ...params }) -> Date +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Date` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDate({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDate("2023-01-15"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnUuid({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnBase64({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Put +
client.endpoints.put.add({ ...params }) -> SeedExhaustive.PutResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.put.add({ + id: "id" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.PutRequest` + +
+
+ +
+
+ +**requestOptions:** `PutClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Union +
client.endpoints.union.getAndReturnUnion({ ...params }) -> SeedExhaustive.Animal +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.union.getAndReturnUnion({ + animal: "dog", + name: "name", + likesToWoof: true +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.Animal` + +
+
+ +
+
+ +**requestOptions:** `UnionClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Urls +
client.endpoints.urls.withMixedCase() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withMixedCase(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.noEndingSlash() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.noEndingSlash(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.withEndingSlash() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withEndingSlash(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.withUnderscores() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withUnderscores(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## InlinedRequests +
client.inlinedRequests.postWithObjectBodyandResponse({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 📝 Description + +
+
+ +
+
+ +POST with custom object in request body, response is an object +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.PostWithObjectBody` + +
+
+ +
+
+ +**requestOptions:** `InlinedRequestsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NoAuth +
client.noAuth.postWithNoAuth({ ...params }) -> boolean +
+
+ +#### 📝 Description + +
+
+ +
+
+ +POST request with no auth +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noAuth.postWithNoAuth({ + "key": "value" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `unknown` + +
+
+ +
+
+ +**requestOptions:** `NoAuthClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NoReqBody +
client.noReqBody.getWithNoRequestBody() -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noReqBody.getWithNoRequestBody(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `NoReqBodyClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.noReqBody.postWithNoRequestBody() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noReqBody.postWithNoRequestBody(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `NoReqBodyClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## ReqWithHeaders +
client.reqWithHeaders.getWithCustomHeader({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.reqWithHeaders.getWithCustomHeader({ + xTestServiceHeader: "X-TEST-SERVICE-HEADER", + xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + body: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ReqWithHeaders` + +
+
+ +
+
+ +**requestOptions:** `ReqWithHeadersClient.RequestOptions` + +
+
+
+
+ + +
+
+
diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/scripts/rename-to-esm-files.js b/seed/ts-sdk/exhaustive/serde-layer-zod/scripts/rename-to-esm-files.js new file mode 100644 index 000000000000..dc1df1cbbacb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/scripts/rename-to-esm-files.js @@ -0,0 +1,123 @@ +#!/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/exhaustive/serde-layer-zod/snippet.json b/seed/ts-sdk/exhaustive/serde-layer-zod/snippet.json new file mode 100644 index 000000000000..875b76c4bb57 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/snippet.json @@ -0,0 +1,544 @@ +{ + "endpoints": [ + { + "id": { + "path": "/container/list-of-primitives", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnListOfPrimitives" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnListOfPrimitives([\"string\", \"string\"]);\n" + } + }, + { + "id": { + "path": "/container/list-of-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnListOfObjects" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnListOfObjects([{\n string: \"string\"\n }, {\n string: \"string\"\n }]);\n" + } + }, + { + "id": { + "path": "/container/set-of-primitives", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnSetOfPrimitives" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnSetOfPrimitives(new Set([\"string\"]));\n" + } + }, + { + "id": { + "path": "/container/set-of-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnSetOfObjects" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnSetOfObjects([{\n string: \"string\"\n }]);\n" + } + }, + { + "id": { + "path": "/container/map-prim-to-prim", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnMapPrimToPrim" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnMapPrimToPrim({\n \"string\": \"string\"\n});\n" + } + }, + { + "id": { + "path": "/container/map-prim-to-object", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnMapOfPrimToObject" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnMapOfPrimToObject({\n \"string\": {\n string: \"string\"\n }\n});\n" + } + }, + { + "id": { + "path": "/container/opt-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnOptional" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/enum", + "method": "POST", + "identifier_override": "endpoint_endpoints/enum.getAndReturnEnum" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.enum.getAndReturnEnum(\"SUNNY\");\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "GET", + "identifier_override": "endpoint_endpoints/http-methods.testGet" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testGet(\"id\");\n" + } + }, + { + "id": { + "path": "/http-methods", + "method": "POST", + "identifier_override": "endpoint_endpoints/http-methods.testPost" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPost({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/http-methods.testPut" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPut(\"id\", {\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "PATCH", + "identifier_override": "endpoint_endpoints/http-methods.testPatch" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPatch(\"id\", {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "DELETE", + "identifier_override": "endpoint_endpoints/http-methods.testDelete" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testDelete(\"id\");\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-optional-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithOptionalField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithOptionalField({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-required-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithRequiredField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithRequiredField({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-map-of-map", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithMapOfMap" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithMapOfMap({\n map: {\n \"map\": {\n \"map\": \"map\"\n }\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-optional-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithOptionalField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithOptionalField({\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-required-field/{string}", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithRequiredField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithRequiredField(\"string\", {\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-required-field-list", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithRequiredFieldAsList" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n }, {\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n }]);\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithPath(\"param\");\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithInlinePath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithInlinePath({\n param: \"param\"\n});\n" + } + }, + { + "id": { + "path": "/params", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithQuery({\n query: \"query\",\n number: 1\n});\n" + } + }, + { + "id": { + "path": "/params", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithAllowMultipleQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithAllowMultipleQuery({\n query: \"query\",\n number: 1\n});\n" + } + }, + { + "id": { + "path": "/params/path-query/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithPathAndQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithPathAndQuery(\"param\", {\n query: \"query\"\n});\n" + } + }, + { + "id": { + "path": "/params/path-query/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithInlinePathAndQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithInlinePathAndQuery({\n param: \"param\",\n query: \"query\"\n});\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/params.modifyWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithPath(\"param\", \"string\");\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/params.modifyWithInlinePath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/primitive/string", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnString" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnString(\"string\");\n" + } + }, + { + "id": { + "path": "/primitive/integer", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnInt" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnInt(1);\n" + } + }, + { + "id": { + "path": "/primitive/long", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnLong" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnLong(1000000);\n" + } + }, + { + "id": { + "path": "/primitive/double", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDouble" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDouble(1.1);\n" + } + }, + { + "id": { + "path": "/primitive/boolean", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnBool" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnBool(true);\n" + } + }, + { + "id": { + "path": "/primitive/datetime", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDatetime" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDatetime(new Date(\"2024-01-15T09:30:00.000Z\"));\n" + } + }, + { + "id": { + "path": "/primitive/date", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDate" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDate(\"2023-01-15\");\n" + } + }, + { + "id": { + "path": "/primitive/uuid", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnUUID" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnUuid(\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\");\n" + } + }, + { + "id": { + "path": "/primitive/base64", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnBase64" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnBase64(\"SGVsbG8gd29ybGQh\");\n" + } + }, + { + "id": { + "path": "/{id}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/put.add" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.put.add({\n id: \"id\"\n});\n" + } + }, + { + "id": { + "path": "/union", + "method": "POST", + "identifier_override": "endpoint_endpoints/union.getAndReturnUnion" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.union.getAndReturnUnion({\n animal: \"dog\",\n name: \"name\",\n likesToWoof: true\n});\n" + } + }, + { + "id": { + "path": "/urls/MixedCase", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withMixedCase" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withMixedCase();\n" + } + }, + { + "id": { + "path": "/urls/no-ending-slash", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.noEndingSlash" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.noEndingSlash();\n" + } + }, + { + "id": { + "path": "/urls/with-ending-slash/", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withEndingSlash" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withEndingSlash();\n" + } + }, + { + "id": { + "path": "/urls/with_underscores", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withUnderscores" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withUnderscores();\n" + } + }, + { + "id": { + "path": "/req-bodies/object", + "method": "POST", + "identifier_override": "endpoint_inlined-requests.postWithObjectBodyandResponse" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.inlinedRequests.postWithObjectBodyandResponse({\n string: \"string\",\n integer: 1,\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/no-auth", + "method": "POST", + "identifier_override": "endpoint_no-auth.postWithNoAuth" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noAuth.postWithNoAuth({\n \"key\": \"value\"\n});\n" + } + }, + { + "id": { + "path": "/no-req-body", + "method": "GET", + "identifier_override": "endpoint_no-req-body.getWithNoRequestBody" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noReqBody.getWithNoRequestBody();\n" + } + }, + { + "id": { + "path": "/no-req-body", + "method": "POST", + "identifier_override": "endpoint_no-req-body.postWithNoRequestBody" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noReqBody.postWithNoRequestBody();\n" + } + }, + { + "id": { + "path": "/test-headers/custom-header", + "method": "POST", + "identifier_override": "endpoint_req-with-headers.getWithCustomHeader" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.reqWithHeaders.getWithCustomHeader({\n xTestServiceHeader: \"X-TEST-SERVICE-HEADER\",\n xTestEndpointHeader: \"X-TEST-ENDPOINT-HEADER\",\n body: \"string\"\n});\n" + } + } + ], + "types": {} +} \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/BaseClient.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/BaseClient.ts new file mode 100644 index 000000000000..3ab4e838f800 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/BaseClient.ts @@ -0,0 +1,81 @@ +// This file was auto-generated by Fern from our API Definition. + +import { BearerAuthProvider } from "./auth/BearerAuthProvider.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; +} & BearerAuthProvider.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/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/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 ??= new BearerAuthProvider(normalizedWithNoOpAuthProvider); + return normalized; +} + +function withNoOpAuthProvider( + options: NormalizedClientOptions, +): NormalizedClientOptionsWithAuth { + return { + ...options, + authProvider: new core.NoOpAuthProvider(), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/Client.ts new file mode 100644 index 000000000000..370640f5862d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/Client.ts @@ -0,0 +1,48 @@ +// This file was auto-generated by Fern from our API Definition. + +import { EndpointsClient } from "./api/resources/endpoints/client/Client.js"; +import { InlinedRequestsClient } from "./api/resources/inlinedRequests/client/Client.js"; +import { NoAuthClient } from "./api/resources/noAuth/client/Client.js"; +import { NoReqBodyClient } from "./api/resources/noReqBody/client/Client.js"; +import { ReqWithHeadersClient } from "./api/resources/reqWithHeaders/client/Client.js"; +import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "./BaseClient.js"; + +export declare namespace SeedExhaustiveClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class SeedExhaustiveClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _endpoints: EndpointsClient | undefined; + protected _inlinedRequests: InlinedRequestsClient | undefined; + protected _noAuth: NoAuthClient | undefined; + protected _noReqBody: NoReqBodyClient | undefined; + protected _reqWithHeaders: ReqWithHeadersClient | undefined; + + constructor(options: SeedExhaustiveClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + public get endpoints(): EndpointsClient { + return (this._endpoints ??= new EndpointsClient(this._options)); + } + + public get inlinedRequests(): InlinedRequestsClient { + return (this._inlinedRequests ??= new InlinedRequestsClient(this._options)); + } + + public get noAuth(): NoAuthClient { + return (this._noAuth ??= new NoAuthClient(this._options)); + } + + public get noReqBody(): NoReqBodyClient { + return (this._noReqBody ??= new NoReqBodyClient(this._options)); + } + + public get reqWithHeaders(): ReqWithHeadersClient { + return (this._reqWithHeaders ??= new ReqWithHeadersClient(this._options)); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/Client.ts new file mode 100644 index 000000000000..78dcad40a3fa --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/Client.ts @@ -0,0 +1,76 @@ +// 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 { ContainerClient } from "../resources/container/client/Client.js"; +import { ContentTypeClient } from "../resources/contentType/client/Client.js"; +import { EnumClient } from "../resources/enum/client/Client.js"; +import { HttpMethodsClient } from "../resources/httpMethods/client/Client.js"; +import { ObjectClient } from "../resources/object/client/Client.js"; +import { ParamsClient } from "../resources/params/client/Client.js"; +import { PrimitiveClient } from "../resources/primitive/client/Client.js"; +import { PutClient } from "../resources/put/client/Client.js"; +import { UnionClient } from "../resources/union/client/Client.js"; +import { UrlsClient } from "../resources/urls/client/Client.js"; + +export declare namespace EndpointsClient { + export type Options = BaseClientOptions; +} + +export class EndpointsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _container: ContainerClient | undefined; + protected _contentType: ContentTypeClient | undefined; + protected _enum: EnumClient | undefined; + protected _httpMethods: HttpMethodsClient | undefined; + protected _object: ObjectClient | undefined; + protected _params: ParamsClient | undefined; + protected _primitive: PrimitiveClient | undefined; + protected _put: PutClient | undefined; + protected _union: UnionClient | undefined; + protected _urls: UrlsClient | undefined; + + constructor(options: EndpointsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + public get container(): ContainerClient { + return (this._container ??= new ContainerClient(this._options)); + } + + public get contentType(): ContentTypeClient { + return (this._contentType ??= new ContentTypeClient(this._options)); + } + + public get enum(): EnumClient { + return (this._enum ??= new EnumClient(this._options)); + } + + public get httpMethods(): HttpMethodsClient { + return (this._httpMethods ??= new HttpMethodsClient(this._options)); + } + + public get object(): ObjectClient { + return (this._object ??= new ObjectClient(this._options)); + } + + public get params(): ParamsClient { + return (this._params ??= new ParamsClient(this._options)); + } + + public get primitive(): PrimitiveClient { + return (this._primitive ??= new PrimitiveClient(this._options)); + } + + public get put(): PutClient { + return (this._put ??= new PutClient(this._options)); + } + + public get union(): UnionClient { + return (this._union ??= new UnionClient(this._options)); + } + + public get urls(): UrlsClient { + return (this._urls ??= new UrlsClient(this._options)); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/index.ts new file mode 100644 index 000000000000..9eb1192dcc32 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/Client.ts new file mode 100644 index 000000000000..feb97f99da76 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/Client.ts @@ -0,0 +1,477 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ContainerClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ContainerClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ContainerClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]) + */ + public getAndReturnListOfPrimitives( + request: string[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnListOfPrimitives(request, requestOptions)); + } + + private async __getAndReturnListOfPrimitives( + request: string[], + requestOptions?: ContainerClient.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)), + "/container/list-of-primitives", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnListOfPrimitives.Request.json(request), + 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: serializers.endpoints.container.getAndReturnListOfPrimitives.Response._schema.parse( + _response.body, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/container/list-of-primitives", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnListOfObjects([{ + * string: "string" + * }, { + * string: "string" + * }]) + */ + public getAndReturnListOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnListOfObjects(request, requestOptions)); + } + + private async __getAndReturnListOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.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)), + "/container/list-of-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnListOfObjects.Request.json(request), + 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: serializers.endpoints.container.getAndReturnListOfObjects.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/list-of-objects"); + } + + /** + * @param {Set} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])) + */ + public getAndReturnSetOfPrimitives( + request: Set, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnSetOfPrimitives(request, requestOptions)); + } + + private async __getAndReturnSetOfPrimitives( + request: Set, + requestOptions?: ContainerClient.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)), + "/container/set-of-primitives", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnSetOfPrimitives.Request.json(request), + 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: serializers.endpoints.container.getAndReturnSetOfPrimitives.Response._schema.parse( + _response.body, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/set-of-primitives"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnSetOfObjects([{ + * string: "string" + * }]) + */ + public getAndReturnSetOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnSetOfObjects(request, requestOptions)); + } + + private async __getAndReturnSetOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.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)), + "/container/set-of-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnSetOfObjects.Request.json(request), + 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: serializers.endpoints.container.getAndReturnSetOfObjects.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/set-of-objects"); + } + + /** + * @param {Record} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnMapPrimToPrim({ + * "string": "string" + * }) + */ + public getAndReturnMapPrimToPrim( + request: Record, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnMapPrimToPrim(request, requestOptions)); + } + + private async __getAndReturnMapPrimToPrim( + request: Record, + requestOptions?: ContainerClient.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)), + "/container/map-prim-to-prim", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnMapPrimToPrim.Request.json(request), + 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: serializers.endpoints.container.getAndReturnMapPrimToPrim.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/map-prim-to-prim"); + } + + /** + * @param {Record} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnMapOfPrimToObject({ + * "string": { + * string: "string" + * } + * }) + */ + public getAndReturnMapOfPrimToObject( + request: Record, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnMapOfPrimToObject(request, requestOptions)); + } + + private async __getAndReturnMapOfPrimToObject( + request: Record, + requestOptions?: ContainerClient.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)), + "/container/map-prim-to-object", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnMapOfPrimToObject.Request.json(request), + 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: serializers.endpoints.container.getAndReturnMapOfPrimToObject.Response._schema.parse( + _response.body, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/container/map-prim-to-object", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnOptional({ + * string: "string" + * }) + */ + public getAndReturnOptional( + request?: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnOptional(request, requestOptions)); + } + + private async __getAndReturnOptional( + request?: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ContainerClient.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)), + "/container/opt-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: + request != null + ? serializers.endpoints.container.getAndReturnOptional.Request.json(request) + : undefined, + 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: serializers.endpoints.container.getAndReturnOptional.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/opt-objects"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/container/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 000000000000..dcd685cf5028 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,172 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ContentTypeClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ContentTypeClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ContentTypeClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentTypeClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postJsonPatchContentType(request, requestOptions)); + } + + private async __postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.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)), + "/foo/bar", + ), + method: "POST", + headers: _headers, + contentType: "application/json-patch+json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.json(request), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/foo/bar"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentTypeClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__postJsonPatchContentWithCharsetType(request, requestOptions), + ); + } + + private async __postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.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)), + "/foo/baz", + ), + method: "POST", + headers: _headers, + contentType: "application/json-patch+json; charset=utf-8", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.json(request), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/foo/baz"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/Client.ts new file mode 100644 index 000000000000..b81bf663dcc9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/Client.ts @@ -0,0 +1,84 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace EnumClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class EnumClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: EnumClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.WeatherReport} request + * @param {EnumClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.enum.getAndReturnEnum("SUNNY") + */ + public getAndReturnEnum( + request: SeedExhaustive.types.WeatherReport, + requestOptions?: EnumClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnEnum(request, requestOptions)); + } + + private async __getAndReturnEnum( + request: SeedExhaustive.types.WeatherReport, + requestOptions?: EnumClient.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)), + "/enum", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.WeatherReport.json(request), + 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: serializers.types.WeatherReport._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/enum"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/enum/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/Client.ts new file mode 100644 index 000000000000..e4673da90729 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/Client.ts @@ -0,0 +1,341 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace HttpMethodsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class HttpMethodsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: HttpMethodsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string} id + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testGet("id") + */ + public testGet(id: string, requestOptions?: HttpMethodsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testGet(id, requestOptions)); + } + + private async __testGet( + id: string, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + 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: serializers.endpoints.httpMethods.testGet.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/http-methods/{id}"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPost({ + * string: "string" + * }) + */ + public testPost( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPost(request, requestOptions)); + } + + private async __testPost( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.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)), + "/http-methods", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.json(request), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/http-methods"); + } + + /** + * @param {string} id + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPut("id", { + * string: "string" + * }) + */ + public testPut( + id: string, + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPut(id, request, requestOptions)); + } + + private async __testPut( + id: string, + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.json(request), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/http-methods/{id}"); + } + + /** + * @param {string} id + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPatch("id", { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public testPatch( + id: string, + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPatch(id, request, requestOptions)); + } + + private async __testPatch( + id: string, + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.json(request), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PATCH", "/http-methods/{id}"); + } + + /** + * @param {string} id + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testDelete("id") + */ + public testDelete( + id: string, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testDelete(id, requestOptions)); + } + + private async __testDelete( + id: string, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "DELETE", + 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: serializers.endpoints.httpMethods.testDelete.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/http-methods/{id}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/httpMethods/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/index.ts new file mode 100644 index 000000000000..6046843b5e42 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/index.ts @@ -0,0 +1,13 @@ +export * as container from "./container/index.js"; +export * as contentType from "./contentType/index.js"; +export * as enum_ from "./enum/index.js"; +export * as httpMethods from "./httpMethods/index.js"; +export * as object from "./object/index.js"; +export * from "./params/client/requests/index.js"; +export * as params from "./params/index.js"; +export * as primitive from "./primitive/index.js"; +export * from "./put/client/requests/index.js"; +export * as put from "./put/index.js"; +export * from "./put/types/index.js"; +export * as union from "./union/index.js"; +export * as urls from "./urls/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/Client.ts new file mode 100644 index 000000000000..804a77a90ac3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/Client.ts @@ -0,0 +1,523 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ObjectClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ObjectClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ObjectClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithOptionalField({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public getAndReturnWithOptionalField( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithOptionalField(request, requestOptions)); + } + + private async __getAndReturnWithOptionalField( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-optional-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.json(request), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-optional-field", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithRequiredField({ + * string: "string" + * }) + */ + public getAndReturnWithRequiredField( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithRequiredField(request, requestOptions)); + } + + private async __getAndReturnWithRequiredField( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-required-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.json(request), + 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: serializers.types.ObjectWithRequiredField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-required-field", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithMapOfMap} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithMapOfMap({ + * map: { + * "map": { + * "map": "map" + * } + * } + * }) + */ + public getAndReturnWithMapOfMap( + request: SeedExhaustive.types.ObjectWithMapOfMap, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithMapOfMap(request, requestOptions)); + } + + private async __getAndReturnWithMapOfMap( + request: SeedExhaustive.types.ObjectWithMapOfMap, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-map-of-map", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithMapOfMap.json(request), + 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: serializers.types.ObjectWithMapOfMap._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-map-of-map", + ); + } + + /** + * @param {SeedExhaustive.types.NestedObjectWithOptionalField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithOptionalField({ + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public getAndReturnNestedWithOptionalField( + request: SeedExhaustive.types.NestedObjectWithOptionalField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithOptionalField(request, requestOptions), + ); + } + + private async __getAndReturnNestedWithOptionalField( + request: SeedExhaustive.types.NestedObjectWithOptionalField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-nested-with-optional-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.NestedObjectWithOptionalField.json(request), + 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: serializers.types.NestedObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-optional-field", + ); + } + + /** + * @param {string} string + * @param {SeedExhaustive.types.NestedObjectWithRequiredField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public getAndReturnNestedWithRequiredField( + string: string, + request: SeedExhaustive.types.NestedObjectWithRequiredField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithRequiredField(string, request, requestOptions), + ); + } + + private async __getAndReturnNestedWithRequiredField( + string: string, + request: SeedExhaustive.types.NestedObjectWithRequiredField, + requestOptions?: ObjectClient.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)), + `/object/get-and-return-nested-with-required-field/${core.url.encodePathParam(string)}`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.NestedObjectWithRequiredField.json(request), + 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: serializers.types.NestedObjectWithRequiredField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-required-field/{string}", + ); + } + + /** + * @param {SeedExhaustive.types.NestedObjectWithRequiredField[]} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{ + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }, { + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }]) + */ + public getAndReturnNestedWithRequiredFieldAsList( + request: SeedExhaustive.types.NestedObjectWithRequiredField[], + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithRequiredFieldAsList(request, requestOptions), + ); + } + + private async __getAndReturnNestedWithRequiredFieldAsList( + request: SeedExhaustive.types.NestedObjectWithRequiredField[], + requestOptions?: ObjectClient.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)), + "/object/get-and-return-nested-with-required-field-list", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.object.getAndReturnNestedWithRequiredFieldAsList.Request.json(request), + 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: serializers.types.NestedObjectWithRequiredField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-required-field-list", + ); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/Client.ts new file mode 100644 index 000000000000..84f66386db7f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/Client.ts @@ -0,0 +1,535 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ParamsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ParamsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ParamsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * GET with path param + * + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithPath("param") + */ + public getWithPath(param: string, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithPath(param, requestOptions)); + } + + private async __getWithPath( + param: string, + requestOptions?: ParamsClient.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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + 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: serializers.endpoints.params.getWithPath.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path/{param}"); + } + + /** + * GET with path param + * + * @param {SeedExhaustive.endpoints.GetWithInlinePath} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithInlinePath({ + * param: "param" + * }) + */ + public getWithInlinePath( + request: SeedExhaustive.endpoints.GetWithInlinePath, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithInlinePath(request, requestOptions)); + } + + private async __getWithInlinePath( + request: SeedExhaustive.endpoints.GetWithInlinePath, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param } = request; + 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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + 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: serializers.endpoints.params.getWithInlinePath.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path/{param}"); + } + + /** + * GET with query param + * + * @param {SeedExhaustive.endpoints.GetWithQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithQuery({ + * query: "query", + * number: 1 + * }) + */ + public getWithQuery( + request: SeedExhaustive.endpoints.GetWithQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithQuery(request, requestOptions)); + } + + private async __getWithQuery( + request: SeedExhaustive.endpoints.GetWithQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query, number: number_ } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + _queryParams.number = number_.toString(); + 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)), + "/params", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params"); + } + + /** + * GET with multiple of same query param + * + * @param {SeedExhaustive.endpoints.GetWithMultipleQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithAllowMultipleQuery({ + * query: "query", + * number: 1 + * }) + */ + public getWithAllowMultipleQuery( + request: SeedExhaustive.endpoints.GetWithMultipleQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithAllowMultipleQuery(request, requestOptions)); + } + + private async __getWithAllowMultipleQuery( + request: SeedExhaustive.endpoints.GetWithMultipleQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query, number: number_ } = request; + const _queryParams: Record = {}; + if (Array.isArray(query)) { + _queryParams.query = query.map((item) => item); + } else { + _queryParams.query = query; + } + + if (Array.isArray(number_)) { + _queryParams.number = number_.map((item) => item.toString()); + } else { + _queryParams.number = number_.toString(); + } + + 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)), + "/params", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params"); + } + + /** + * GET with path and query params + * + * @param {string} param + * @param {SeedExhaustive.endpoints.GetWithPathAndQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithPathAndQuery("param", { + * query: "query" + * }) + */ + public getWithPathAndQuery( + param: string, + request: SeedExhaustive.endpoints.GetWithPathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithPathAndQuery(param, request, requestOptions)); + } + + private async __getWithPathAndQuery( + param: string, + request: SeedExhaustive.endpoints.GetWithPathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + 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)), + `/params/path-query/${core.url.encodePathParam(param)}`, + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path-query/{param}"); + } + + /** + * GET with path and query params + * + * @param {SeedExhaustive.endpoints.GetWithInlinePathAndQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithInlinePathAndQuery({ + * param: "param", + * query: "query" + * }) + */ + public getWithInlinePathAndQuery( + request: SeedExhaustive.endpoints.GetWithInlinePathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithInlinePathAndQuery(request, requestOptions)); + } + + private async __getWithInlinePathAndQuery( + request: SeedExhaustive.endpoints.GetWithInlinePathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param, query } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + 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)), + `/params/path-query/${core.url.encodePathParam(param)}`, + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path-query/{param}"); + } + + /** + * PUT to update with path param + * + * @param {string} param + * @param {string} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.modifyWithPath("param", "string") + */ + public modifyWithPath( + param: string, + request: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__modifyWithPath(param, request, requestOptions)); + } + + private async __modifyWithPath( + param: string, + request: string, + requestOptions?: ParamsClient.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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.params.modifyWithPath.Request.json(request), + 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: serializers.endpoints.params.modifyWithPath.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); + } + + /** + * PUT to update with path param + * + * @param {SeedExhaustive.endpoints.ModifyResourceAtInlinedPath} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.modifyWithInlinePath({ + * param: "param", + * body: "string" + * }) + */ + public modifyWithInlinePath( + request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__modifyWithInlinePath(request, requestOptions)); + } + + private async __modifyWithInlinePath( + request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param, body: _body } = request; + 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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.params.modifyWithInlinePath.Request.json(_body), + 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: serializers.endpoints.params.modifyWithInlinePath.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts new file mode 100644 index 000000000000..473165eb1638 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param" + * } + */ +export interface GetWithInlinePath { + param: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts new file mode 100644 index 000000000000..9c674a852dce --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param", + * query: "query" + * } + */ +export interface GetWithInlinePathAndQuery { + param: string; + query: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts new file mode 100644 index 000000000000..baca4fafbe62 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query", + * number: 1 + * } + */ +export interface GetWithMultipleQuery { + query: string | string[]; + number: number | number[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts new file mode 100644 index 000000000000..c3f0018a7874 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query" + * } + */ +export interface GetWithPathAndQuery { + query: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts new file mode 100644 index 000000000000..7ad48dee4508 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query", + * number: 1 + * } + */ +export interface GetWithQuery { + query: string; + number: number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts new file mode 100644 index 000000000000..f3f91a06e3f8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param", + * body: "string" + * } + */ +export interface ModifyResourceAtInlinedPath { + param: string; + body: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/index.ts new file mode 100644 index 000000000000..b059d3702a32 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/client/requests/index.ts @@ -0,0 +1,6 @@ +export type { GetWithInlinePath } from "./GetWithInlinePath.js"; +export type { GetWithInlinePathAndQuery } from "./GetWithInlinePathAndQuery.js"; +export type { GetWithMultipleQuery } from "./GetWithMultipleQuery.js"; +export type { GetWithPathAndQuery } from "./GetWithPathAndQuery.js"; +export type { GetWithQuery } from "./GetWithQuery.js"; +export type { ModifyResourceAtInlinedPath } from "./ModifyResourceAtInlinedPath.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/params/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/Client.ts new file mode 100644 index 000000000000..e12ee83a612e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/Client.ts @@ -0,0 +1,563 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; + +export declare namespace PrimitiveClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class PrimitiveClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: PrimitiveClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnString("string") + */ + public getAndReturnString( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnString(request, requestOptions)); + } + + private async __getAndReturnString( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/string", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnString.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnString.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/string"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnInt(1) + */ + public getAndReturnInt( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnInt(request, requestOptions)); + } + + private async __getAndReturnInt( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/integer", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnInt.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnInt.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/integer"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnLong(1000000) + */ + public getAndReturnLong( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnLong(request, requestOptions)); + } + + private async __getAndReturnLong( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/long", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnLong.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnLong.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/long"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDouble(1.1) + */ + public getAndReturnDouble( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDouble(request, requestOptions)); + } + + private async __getAndReturnDouble( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/double", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDouble.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnDouble.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/double"); + } + + /** + * @param {boolean} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnBool(true) + */ + public getAndReturnBool( + request: boolean, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnBool(request, requestOptions)); + } + + private async __getAndReturnBool( + request: boolean, + requestOptions?: PrimitiveClient.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)), + "/primitive/boolean", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnBool.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnBool.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/boolean"); + } + + /** + * @param {Date} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")) + */ + public getAndReturnDatetime( + request: Date, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDatetime(request, requestOptions)); + } + + private async __getAndReturnDatetime( + request: Date, + requestOptions?: PrimitiveClient.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)), + "/primitive/datetime", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDatetime.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnDatetime.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/datetime"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDate("2023-01-15") + */ + public getAndReturnDate( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDate(request, requestOptions)); + } + + private async __getAndReturnDate( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/date", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDate.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnDate.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/date"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32") + */ + public getAndReturnUuid( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnUuid(request, requestOptions)); + } + + private async __getAndReturnUuid( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/uuid", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnUuid.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnUuid.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/uuid"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh") + */ + public getAndReturnBase64( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnBase64(request, requestOptions)); + } + + private async __getAndReturnBase64( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/base64", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnBase64.Request.json(request), + 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: serializers.endpoints.primitive.getAndReturnBase64.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/base64"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/primitive/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/Client.ts new file mode 100644 index 000000000000..d040de02b8ac --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/Client.ts @@ -0,0 +1,84 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace PutClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class PutClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: PutClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.endpoints.PutRequest} request + * @param {PutClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.put.add({ + * id: "id" + * }) + */ + public add( + request: SeedExhaustive.endpoints.PutRequest, + requestOptions?: PutClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__add(request, requestOptions)); + } + + private async __add( + request: SeedExhaustive.endpoints.PutRequest, + requestOptions?: PutClient.RequestOptions, + ): Promise> { + const { id } = request; + 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)), + `${core.url.encodePathParam(id)}`, + ), + method: "PUT", + 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: serializers.endpoints.PutResponse._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/{id}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts new file mode 100644 index 000000000000..2beba9f087cc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * id: "id" + * } + */ +export interface PutRequest { + id: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/index.ts new file mode 100644 index 000000000000..dba0eda4bbee --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/client/requests/index.ts @@ -0,0 +1 @@ +export type { PutRequest } from "./PutRequest.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/index.ts new file mode 100644 index 000000000000..d9adb1af9a93 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts new file mode 100644 index 000000000000..a197fec87887 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +export const ErrorCategory = { + ApiError: "API_ERROR", + AuthenticationError: "AUTHENTICATION_ERROR", + InvalidRequestError: "INVALID_REQUEST_ERROR", +} as const; +export type ErrorCategory = (typeof ErrorCategory)[keyof typeof ErrorCategory]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCode.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCode.ts new file mode 100644 index 000000000000..3943226222d8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/ErrorCode.ts @@ -0,0 +1,16 @@ +// This file was auto-generated by Fern from our API Definition. + +export const ErrorCode = { + InternalServerError: "INTERNAL_SERVER_ERROR", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + BadRequest: "BAD_REQUEST", + Conflict: "CONFLICT", + Gone: "GONE", + UnprocessableEntity: "UNPROCESSABLE_ENTITY", + NotImplemented: "NOT_IMPLEMENTED", + BadGateway: "BAD_GATEWAY", + ServiceUnavailable: "SERVICE_UNAVAILABLE", + Unknown: "Unknown", +} as const; +export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/Error_.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/Error_.ts new file mode 100644 index 000000000000..9cf9f495a09a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/Error_.ts @@ -0,0 +1,10 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface Error_ { + category: SeedExhaustive.endpoints.ErrorCategory; + code: SeedExhaustive.endpoints.ErrorCode; + detail?: string; + field?: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/PutResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/PutResponse.ts new file mode 100644 index 000000000000..628b46ba7298 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/PutResponse.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface PutResponse { + errors?: SeedExhaustive.endpoints.Error_[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/index.ts new file mode 100644 index 000000000000..3293f66bb34c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/put/types/index.ts @@ -0,0 +1,4 @@ +export * from "./Error_.js"; +export * from "./ErrorCategory.js"; +export * from "./ErrorCode.js"; +export * from "./PutResponse.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/Client.ts new file mode 100644 index 000000000000..dbcfd29768a0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/Client.ts @@ -0,0 +1,85 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace UnionClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class UnionClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: UnionClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.Animal} request + * @param {UnionClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.union.getAndReturnUnion({ + * animal: "dog", + * name: "name", + * likesToWoof: true + * }) + */ + public getAndReturnUnion( + request: SeedExhaustive.types.Animal, + requestOptions?: UnionClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnUnion(request, requestOptions)); + } + + private async __getAndReturnUnion( + request: SeedExhaustive.types.Animal, + requestOptions?: UnionClient.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)), + "/union", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.Animal.json(request), + 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: serializers.types.Animal._schema.parse(_response.body), rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/union"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/union/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/Client.ts new file mode 100644 index 000000000000..9bf501ea16fc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/Client.ts @@ -0,0 +1,223 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; + +export declare namespace UrlsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class UrlsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: UrlsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withMixedCase() + */ + public withMixedCase(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withMixedCase(requestOptions)); + } + + private async __withMixedCase(requestOptions?: UrlsClient.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)), + "/urls/MixedCase", + ), + 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: serializers.endpoints.urls.withMixedCase.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/MixedCase"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.noEndingSlash() + */ + public noEndingSlash(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__noEndingSlash(requestOptions)); + } + + private async __noEndingSlash(requestOptions?: UrlsClient.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)), + "/urls/no-ending-slash", + ), + 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: serializers.endpoints.urls.noEndingSlash.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/no-ending-slash"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withEndingSlash() + */ + public withEndingSlash(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withEndingSlash(requestOptions)); + } + + private async __withEndingSlash(requestOptions?: UrlsClient.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)), + "/urls/with-ending-slash/", + ), + 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: serializers.endpoints.urls.withEndingSlash.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/with-ending-slash/"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withUnderscores() + */ + public withUnderscores(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withUnderscores(requestOptions)); + } + + private async __withUnderscores(requestOptions?: UrlsClient.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)), + "/urls/with_underscores", + ), + 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: serializers.endpoints.urls.withUnderscores.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/with_underscores"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/endpoints/resources/urls/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/BadRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/BadRequestBody.ts new file mode 100644 index 000000000000..6c0020d2b75a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/BadRequestBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../core/index.js"; +import * as errors from "../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export class BadRequestBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.BadObjectRequestInfo, rawResponse?: core.RawResponse) { + super({ + message: "BadRequestBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/index.ts new file mode 100644 index 000000000000..61778bda5417 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/errors/index.ts @@ -0,0 +1 @@ +export * from "./BadRequestBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts new file mode 100644 index 000000000000..6ad426742488 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface BadObjectRequestInfo { + message: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/index.ts new file mode 100644 index 000000000000..b10afa3b7749 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/generalErrors/types/index.ts @@ -0,0 +1 @@ +export * from "./BadObjectRequestInfo.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/index.ts new file mode 100644 index 000000000000..3ca611c9fb54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/index.ts @@ -0,0 +1,11 @@ +export * as endpoints from "./endpoints/index.js"; +export * from "./generalErrors/errors/index.js"; +export * as generalErrors from "./generalErrors/index.js"; +export * from "./generalErrors/types/index.js"; +export * from "./inlinedRequests/client/requests/index.js"; +export * as inlinedRequests from "./inlinedRequests/index.js"; +export * as noAuth from "./noAuth/index.js"; +export * as noReqBody from "./noReqBody/index.js"; +export * from "./reqWithHeaders/client/requests/index.js"; +export * as reqWithHeaders from "./reqWithHeaders/index.js"; +export * as types from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/Client.ts new file mode 100644 index 000000000000..40b1e05011e3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/Client.ts @@ -0,0 +1,111 @@ +// 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 * as serializers from "../../../../serialization/index.js"; +import * as SeedExhaustive from "../../../index.js"; + +export declare namespace InlinedRequestsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class InlinedRequestsClient { + protected readonly _options: NormalizedClientOptions; + + constructor(options: InlinedRequestsClient.Options) { + this._options = normalizeClientOptions(options); + } + + /** + * POST with custom object in request body, response is an object + * + * @param {SeedExhaustive.PostWithObjectBody} request + * @param {InlinedRequestsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link SeedExhaustive.BadRequestBody} + * + * @example + * await client.inlinedRequests.postWithObjectBodyandResponse({ + * string: "string", + * integer: 1, + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public postWithObjectBodyandResponse( + request: SeedExhaustive.PostWithObjectBody, + requestOptions?: InlinedRequestsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithObjectBodyandResponse(request, requestOptions)); + } + + private async __postWithObjectBodyandResponse( + request: SeedExhaustive.PostWithObjectBody, + requestOptions?: InlinedRequestsClient.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)), + "/req-bodies/object", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.PostWithObjectBody.json(request), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new SeedExhaustive.BadRequestBody( + serializers.BadObjectRequestInfo._schema.parse(_response.error.body), + _response.rawResponse, + ); + default: + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/req-bodies/object"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts new file mode 100644 index 000000000000..15d3f75bc67d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../index.js"; + +/** + * @example + * { + * string: "string", + * integer: 1, + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * } + */ +export interface PostWithObjectBody { + string: string; + integer: number; + nestedObject: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/index.ts new file mode 100644 index 000000000000..104f84256770 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/client/requests/index.ts @@ -0,0 +1 @@ +export type { PostWithObjectBody } from "./PostWithObjectBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/inlinedRequests/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/Client.ts new file mode 100644 index 000000000000..08508f47c0f9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/Client.ts @@ -0,0 +1,93 @@ +// 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 * as serializers from "../../../../serialization/index.js"; +import * as SeedExhaustive from "../../../index.js"; + +export declare namespace NoAuthClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class NoAuthClient { + protected readonly _options: NormalizedClientOptions; + + constructor(options: NoAuthClient.Options) { + this._options = normalizeClientOptions(options); + } + + /** + * POST request with no auth + * + * @param {unknown} request + * @param {NoAuthClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link SeedExhaustive.BadRequestBody} + * + * @example + * await client.noAuth.postWithNoAuth({ + * "key": "value" + * }) + */ + public postWithNoAuth( + request?: unknown, + requestOptions?: NoAuthClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithNoAuth(request, requestOptions)); + } + + private async __postWithNoAuth( + request?: unknown, + requestOptions?: NoAuthClient.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)), + "/no-auth", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + 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: serializers.noAuth.postWithNoAuth.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new SeedExhaustive.BadRequestBody( + serializers.BadObjectRequestInfo._schema.parse(_response.error.body), + _response.rawResponse, + ); + default: + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/no-auth"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noAuth/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/Client.ts new file mode 100644 index 000000000000..799f6fe6cc00 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/Client.ts @@ -0,0 +1,130 @@ +// 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"; +import * as serializers from "../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export declare namespace NoReqBodyClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class NoReqBodyClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: NoReqBodyClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {NoReqBodyClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.noReqBody.getWithNoRequestBody() + */ + public getWithNoRequestBody( + requestOptions?: NoReqBodyClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithNoRequestBody(requestOptions)); + } + + private async __getWithNoRequestBody( + requestOptions?: NoReqBodyClient.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)), + "/no-req-body", + ), + 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: serializers.types.ObjectWithOptionalField._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/no-req-body"); + } + + /** + * @param {NoReqBodyClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.noReqBody.postWithNoRequestBody() + */ + public postWithNoRequestBody(requestOptions?: NoReqBodyClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithNoRequestBody(requestOptions)); + } + + private async __postWithNoRequestBody( + requestOptions?: NoReqBodyClient.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)), + "/no-req-body", + ), + method: "POST", + 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: serializers.noReqBody.postWithNoRequestBody.Response._schema.parse(_response.body), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/no-req-body"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/noReqBody/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/Client.ts new file mode 100644 index 000000000000..0b6c1b11327c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/Client.ts @@ -0,0 +1,90 @@ +// 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, mergeOnlyDefinedHeaders } 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 * as serializers from "../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export declare namespace ReqWithHeadersClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ReqWithHeadersClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ReqWithHeadersClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.ReqWithHeaders} request + * @param {ReqWithHeadersClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.reqWithHeaders.getWithCustomHeader({ + * xTestServiceHeader: "X-TEST-SERVICE-HEADER", + * xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + * body: "string" + * }) + */ + public getWithCustomHeader( + request: SeedExhaustive.ReqWithHeaders, + requestOptions?: ReqWithHeadersClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithCustomHeader(request, requestOptions)); + } + + private async __getWithCustomHeader( + request: SeedExhaustive.ReqWithHeaders, + requestOptions?: ReqWithHeadersClient.RequestOptions, + ): Promise> { + const { xTestServiceHeader, xTestEndpointHeader, body: _body } = request; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + mergeOnlyDefinedHeaders({ + "X-TEST-SERVICE-HEADER": xTestServiceHeader, + "X-TEST-ENDPOINT-HEADER": xTestEndpointHeader, + }), + 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)), + "/test-headers/custom-header", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.reqWithHeaders.getWithCustomHeader.Request.json(_body), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/test-headers/custom-header"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts new file mode 100644 index 000000000000..b7fce3790bf5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * xTestServiceHeader: "X-TEST-SERVICE-HEADER", + * xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + * body: "string" + * } + */ +export interface ReqWithHeaders { + xTestServiceHeader: string; + xTestEndpointHeader: string; + body: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/index.ts new file mode 100644 index 000000000000..5419b56cd667 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/client/requests/index.ts @@ -0,0 +1 @@ +export type { ReqWithHeaders } from "./ReqWithHeaders.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/reqWithHeaders/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts new file mode 100644 index 000000000000..ed458c66ba3c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts @@ -0,0 +1,66 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithDocs { + /** + * Characters that could lead to broken generated SDKs: + * + * Markdown Escapes: + * - \_: Escaped underscore (e.g., FOO\_BAR) + * - \*: Escaped asterisk + * + * JSDoc (JavaScript/TypeScript): + * - @: Used for JSDoc tags + * - {: }: Used for type definitions + * - <: >: HTML tags + * - *: Can interfere with comment blocks + * - /**: JSDoc comment start + * - ** /: JSDoc comment end + * - &: HTML entities + * + * XMLDoc (C#): + * - <: >: XML tags + * - &: ': ": <: >: XML special characters + * - {: }: Used for interpolated strings + * - ///: Comment marker + * - /**: Block comment start + * - ** /: Block comment end + * + * Javadoc (Java): + * - @: Used for Javadoc tags + * - <: >: HTML tags + * - &: HTML entities + * - *: Can interfere with comment blocks + * - /**: Javadoc comment start + * - ** /: Javadoc comment end + * + * Doxygen (C++): + * - \: Used for Doxygen commands + * - @: Alternative command prefix + * - <: >: XML/HTML tags + * - &: HTML entities + * - /**: C-style comment start + * - ** /: C-style comment end + * + * RDoc (Ruby): + * - :: Used in symbol notation + * - =: Section markers + * - #: Comment marker + * - =begin: Block comment start + * - =end: Block comment end + * - @: Instance variable prefix + * - $: Global variable prefix + * - %: String literal delimiter + * - #{: String interpolation start + * - }: String interpolation end + * + * PHPDoc (PHP): + * - @: Used for PHPDoc tags + * - {: }: Used for type definitions + * - $: Variable prefix + * - /**: PHPDoc comment start + * - ** /: PHPDoc comment end + * - *: Can interfere with comment blocks + * - &: HTML entities + */ + string: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/index.ts new file mode 100644 index 000000000000..3eedfcba912c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/docs/types/index.ts @@ -0,0 +1 @@ +export * from "./ObjectWithDocs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts new file mode 100644 index 000000000000..ef1aee2d386e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ErrorWithEnumBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.WeatherReport, rawResponse?: core.RawResponse) { + super({ + message: "ErrorWithEnumBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/index.ts new file mode 100644 index 000000000000..203651bfb712 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/errors/index.ts @@ -0,0 +1 @@ +export * from "./ErrorWithEnumBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/WeatherReport.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/WeatherReport.ts new file mode 100644 index 000000000000..4023a0068946 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/WeatherReport.ts @@ -0,0 +1,9 @@ +// This file was auto-generated by Fern from our API Definition. + +export const WeatherReport = { + Sunny: "SUNNY", + Cloudy: "CLOUDY", + Raining: "RAINING", + Snowing: "SNOWING", +} as const; +export type WeatherReport = (typeof WeatherReport)[keyof typeof WeatherReport]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/index.ts new file mode 100644 index 000000000000..3b2d63911a15 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/enum/types/index.ts @@ -0,0 +1 @@ +export * from "./WeatherReport.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/index.ts new file mode 100644 index 000000000000..c2a44b58ecd8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/index.ts @@ -0,0 +1,11 @@ +export * as docs from "./docs/index.js"; +export * from "./docs/types/index.js"; +export * from "./enum/errors/index.js"; +export * as enum_ from "./enum/index.js"; +export * from "./enum/types/index.js"; +export * from "./object/errors/index.js"; +export * as object from "./object/index.js"; +export * from "./object/types/index.js"; +export * from "./union/errors/index.js"; +export * as union from "./union/index.js"; +export * from "./union/types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts new file mode 100644 index 000000000000..2681788e4f04 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class NestedObjectWithOptionalFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.NestedObjectWithOptionalField, rawResponse?: core.RawResponse) { + super({ + message: "NestedObjectWithOptionalFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts new file mode 100644 index 000000000000..203710315112 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class NestedObjectWithRequiredFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.NestedObjectWithRequiredField, rawResponse?: core.RawResponse) { + super({ + message: "NestedObjectWithRequiredFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts new file mode 100644 index 000000000000..230964f8467f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ObjectWithOptionalFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.ObjectWithOptionalField, rawResponse?: core.RawResponse) { + super({ + message: "ObjectWithOptionalFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts new file mode 100644 index 000000000000..b8a86dc99dd8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ObjectWithRequiredFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.ObjectWithRequiredField, rawResponse?: core.RawResponse) { + super({ + message: "ObjectWithRequiredFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/index.ts new file mode 100644 index 000000000000..e38a83090455 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/errors/index.ts @@ -0,0 +1,4 @@ +export * from "./NestedObjectWithOptionalFieldError.js"; +export * from "./NestedObjectWithRequiredFieldError.js"; +export * from "./ObjectWithOptionalFieldError.js"; +export * from "./ObjectWithRequiredFieldError.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/DoubleOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/DoubleOptional.ts new file mode 100644 index 000000000000..4898ba728836 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/DoubleOptional.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface DoubleOptional { + optionalAlias?: SeedExhaustive.types.OptionalAlias | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts new file mode 100644 index 000000000000..324bb2e73908 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface NestedObjectWithOptionalField { + string?: string; + nestedObject?: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts new file mode 100644 index 000000000000..322dcfbd5c9b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface NestedObjectWithRequiredField { + string: string; + nestedObject: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts new file mode 100644 index 000000000000..b35138a717bb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithMapOfMap { + map: Record>; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts new file mode 100644 index 000000000000..2e6b3811b575 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithOptionalField { + /** This is a rather long descriptor of this single field in a more complex type. If you ask me I think this is a pretty good description for this field all things considered. */ + string?: string; + integer?: number; + long?: number; + double?: number; + bool?: boolean; + datetime?: Date; + date?: string; + uuid?: string; + base64?: string; + list?: string[]; + set?: Set; + map?: Record; + bigint?: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts new file mode 100644 index 000000000000..032a84135c9e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithRequiredField { + string: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/OptionalAlias.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/OptionalAlias.ts new file mode 100644 index 000000000000..5dac5b761df8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/OptionalAlias.ts @@ -0,0 +1,3 @@ +// This file was auto-generated by Fern from our API Definition. + +export type OptionalAlias = string | undefined; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/index.ts new file mode 100644 index 000000000000..c3c65c311b82 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/object/types/index.ts @@ -0,0 +1,7 @@ +export * from "./DoubleOptional.js"; +export * from "./NestedObjectWithOptionalField.js"; +export * from "./NestedObjectWithRequiredField.js"; +export * from "./ObjectWithMapOfMap.js"; +export * from "./ObjectWithOptionalField.js"; +export * from "./ObjectWithRequiredField.js"; +export * from "./OptionalAlias.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts new file mode 100644 index 000000000000..c0e5b750495f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ErrorWithUnionBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.Animal, rawResponse?: core.RawResponse) { + super({ + message: "ErrorWithUnionBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/index.ts new file mode 100644 index 000000000000..940b191924ee --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/errors/index.ts @@ -0,0 +1 @@ +export * from "./ErrorWithUnionBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Animal.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Animal.ts new file mode 100644 index 000000000000..f5336c6b33bc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Animal.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export type Animal = SeedExhaustive.types.Animal.Dog | SeedExhaustive.types.Animal.Cat; + +export namespace Animal { + export interface Dog extends SeedExhaustive.types.Dog { + animal: "dog"; + } + + export interface Cat extends SeedExhaustive.types.Cat { + animal: "cat"; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Cat.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Cat.ts new file mode 100644 index 000000000000..aa74d5b7dd01 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Cat.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface Cat { + name: string; + likesToMeow: boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Dog.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Dog.ts new file mode 100644 index 000000000000..24bc9aa321b1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/Dog.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface Dog { + name: string; + likesToWoof: boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/index.ts new file mode 100644 index 000000000000..342e90adc057 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/api/resources/types/resources/union/types/index.ts @@ -0,0 +1,3 @@ +export * from "./Animal.js"; +export * from "./Cat.js"; +export * from "./Dog.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/BearerAuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/BearerAuthProvider.ts new file mode 100644 index 000000000000..95ada392f8de --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/BearerAuthProvider.ts @@ -0,0 +1,37 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../core/index.js"; +import * as errors from "../errors/index.js"; + +export namespace BearerAuthProvider { + export interface AuthOptions { + token?: core.Supplier; + } + + export interface Options extends AuthOptions {} +} + +export class BearerAuthProvider implements core.AuthProvider { + private readonly token: core.Supplier | undefined; + + constructor(options: BearerAuthProvider.Options) { + this.token = options.token; + } + + public static canCreate(options: BearerAuthProvider.Options): boolean { + return options.token != null; + } + + public async getAuthRequest(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { + const token = await core.Supplier.get(this.token); + if (token == null) { + throw new errors.SeedExhaustiveError({ + message: "Please specify a token by passing it in to the constructor", + }); + } + + return { + headers: { Authorization: `Bearer ${token}` }, + }; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/index.ts new file mode 100644 index 000000000000..0ecb12b79bdb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/auth/index.ts @@ -0,0 +1 @@ +export { BearerAuthProvider } from "./BearerAuthProvider.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/AuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/AuthProvider.ts new file mode 100644 index 000000000000..895a50ff30da --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/AuthProvider.ts @@ -0,0 +1,6 @@ +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/exhaustive/serde-layer-zod/src/core/auth/AuthRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/AuthRequest.ts new file mode 100644 index 000000000000..f6218b42211e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/AuthRequest.ts @@ -0,0 +1,9 @@ +/** + * Request parameters for authentication requests. + */ +export interface AuthRequest { + /** + * The headers to be included in the request. + */ + headers: Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/BasicAuth.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/BasicAuth.ts new file mode 100644 index 000000000000..a64235910062 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/BasicAuth.ts @@ -0,0 +1,32 @@ +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/exhaustive/serde-layer-zod/src/core/auth/BearerToken.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/BearerToken.ts new file mode 100644 index 000000000000..c44a06c38f06 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/BearerToken.ts @@ -0,0 +1,20 @@ +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/exhaustive/serde-layer-zod/src/core/auth/NoOpAuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/NoOpAuthProvider.ts new file mode 100644 index 000000000000..5b7acfd2bd8b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/NoOpAuthProvider.ts @@ -0,0 +1,8 @@ +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/exhaustive/serde-layer-zod/src/core/auth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/index.ts new file mode 100644 index 000000000000..2215b227709e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/auth/index.ts @@ -0,0 +1,5 @@ +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/exhaustive/serde-layer-zod/src/core/base64.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/base64.ts new file mode 100644 index 000000000000..448a0db638a6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/base64.ts @@ -0,0 +1,27 @@ +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/exhaustive/serde-layer-zod/src/core/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/exports.ts new file mode 100644 index 000000000000..69296d7100d6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/exports.ts @@ -0,0 +1 @@ +export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/APIResponse.ts new file mode 100644 index 000000000000..97ab83c2b195 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/BinaryResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/BinaryResponse.ts new file mode 100644 index 000000000000..bca7f4c77981 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/BinaryResponse.ts @@ -0,0 +1,34 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/EndpointMetadata.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/EndpointMetadata.ts new file mode 100644 index 000000000000..998d68f5c20c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/EndpointMetadata.ts @@ -0,0 +1,13 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/EndpointSupplier.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/EndpointSupplier.ts new file mode 100644 index 000000000000..8079841c4062 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/EndpointSupplier.ts @@ -0,0 +1,14 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Fetcher.ts new file mode 100644 index 000000000000..58bb0e3ef7d9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Fetcher.ts @@ -0,0 +1,391 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/Headers.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Headers.ts new file mode 100644 index 000000000000..af841aa24f55 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Headers.ts @@ -0,0 +1,93 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/HttpResponsePromise.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 000000000000..692ca7d795f0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/RawResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/RawResponse.ts new file mode 100644 index 000000000000..37fb44e2aa99 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/RawResponse.ts @@ -0,0 +1,61 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/Supplier.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Supplier.ts new file mode 100644 index 000000000000..867c931c02f4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 000000000000..88e13265e112 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,6 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/getErrorResponseBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getErrorResponseBody.ts new file mode 100644 index 000000000000..7cf4e623c2f5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getErrorResponseBody.ts @@ -0,0 +1,33 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getFetchFn.ts new file mode 100644 index 000000000000..9f845b956392 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,3 @@ +export async function getFetchFn(): Promise { + return fetch; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getHeader.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getHeader.ts new file mode 100644 index 000000000000..50f922b0e87f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getRequestBody.ts new file mode 100644 index 000000000000..91d9d81f50e5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,20 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getResponseBody.ts new file mode 100644 index 000000000000..708d55728f2b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,58 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/index.ts new file mode 100644 index 000000000000..c3bc6da20f49 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/index.ts @@ -0,0 +1,11 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/makeRequest.ts new file mode 100644 index 000000000000..921565eb0063 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/makeRequest.ts @@ -0,0 +1,42 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 000000000000..1f689688c4b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,64 @@ +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/exhaustive/serde-layer-zod/src/core/fetcher/signals.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/signals.ts new file mode 100644 index 000000000000..7bd3757ec3a7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/fetcher/signals.ts @@ -0,0 +1,26 @@ +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/exhaustive/serde-layer-zod/src/core/headers.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/headers.ts new file mode 100644 index 000000000000..78ed8b500c95 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/headers.ts @@ -0,0 +1,35 @@ +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/exhaustive/serde-layer-zod/src/core/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/index.ts new file mode 100644 index 000000000000..92290bfadcac --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/index.ts @@ -0,0 +1,6 @@ +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/exhaustive/serde-layer-zod/src/core/json.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/json.ts new file mode 100644 index 000000000000..c052f3249f4f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/json.ts @@ -0,0 +1,27 @@ +/** + * 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/exhaustive/serde-layer-zod/src/core/logging/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/exports.ts new file mode 100644 index 000000000000..88f6c00db0cf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/exports.ts @@ -0,0 +1,19 @@ +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/exhaustive/serde-layer-zod/src/core/logging/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/index.ts new file mode 100644 index 000000000000..d81cc32c40f9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/index.ts @@ -0,0 +1 @@ +export * from "./logger.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/logger.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/logger.ts new file mode 100644 index 000000000000..a3f3673cda93 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/logging/logger.ts @@ -0,0 +1,203 @@ +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/exhaustive/serde-layer-zod/src/core/runtime/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/runtime/index.ts new file mode 100644 index 000000000000..cfab23f9a834 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/runtime/runtime.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/runtime/runtime.ts new file mode 100644 index 000000000000..56ebbb87c4d3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/runtime/runtime.ts @@ -0,0 +1,134 @@ +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/exhaustive/serde-layer-zod/src/core/url/encodePathParam.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/encodePathParam.ts new file mode 100644 index 000000000000..19b901244218 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/encodePathParam.ts @@ -0,0 +1,18 @@ +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/exhaustive/serde-layer-zod/src/core/url/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/index.ts new file mode 100644 index 000000000000..f2e0fa2d2221 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/index.ts @@ -0,0 +1,3 @@ +export { encodePathParam } from "./encodePathParam.js"; +export { join } from "./join.js"; +export { toQueryString } from "./qs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/join.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/join.ts new file mode 100644 index 000000000000..7ca7daef094d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/join.ts @@ -0,0 +1,79 @@ +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/exhaustive/serde-layer-zod/src/core/url/qs.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/qs.ts new file mode 100644 index 000000000000..13e89be9d9a6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/core/url/qs.ts @@ -0,0 +1,74 @@ +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/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveError.ts new file mode 100644 index 000000000000..d2e515f5d4e2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveError.ts @@ -0,0 +1,58 @@ +// 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 SeedExhaustiveError 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/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveTimeoutError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveTimeoutError.ts new file mode 100644 index 000000000000..82a3cb8de266 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/SeedExhaustiveTimeoutError.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +export class SeedExhaustiveTimeoutError 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/exhaustive/serde-layer-zod/src/errors/handleNonStatusCodeError.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/handleNonStatusCodeError.ts new file mode 100644 index 000000000000..deb55c602654 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/handleNonStatusCodeError.ts @@ -0,0 +1,37 @@ +// 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.SeedExhaustiveError({ + statusCode: error.statusCode, + body: error.rawBody, + rawResponse: rawResponse, + }); + case "body-is-null": + throw new errors.SeedExhaustiveError({ + statusCode: error.statusCode, + rawResponse: rawResponse, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(`Timeout exceeded when calling ${method} ${path}.`); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: error.errorMessage, + rawResponse: rawResponse, + }); + default: + throw new errors.SeedExhaustiveError({ + message: "Unknown error", + rawResponse: rawResponse, + }); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/index.ts new file mode 100644 index 000000000000..2e2853073a94 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/errors/index.ts @@ -0,0 +1,2 @@ +export { SeedExhaustiveError } from "./SeedExhaustiveError.js"; +export { SeedExhaustiveTimeoutError } from "./SeedExhaustiveTimeoutError.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/exports.ts new file mode 100644 index 000000000000..7b70ee14fc02 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/exports.ts @@ -0,0 +1 @@ +export * from "./core/exports.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/index.ts new file mode 100644 index 000000000000..9f0bdd34e0d5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/index.ts @@ -0,0 +1,6 @@ +export * as SeedExhaustive from "./api/index.js"; +export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +export { SeedExhaustiveClient } from "./Client.js"; +export { SeedExhaustiveError, SeedExhaustiveTimeoutError } from "./errors/index.js"; +export * from "./exports.js"; +export * as serialization from "./serialization/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts new file mode 100644 index 000000000000..3e5dc038a0ae --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts @@ -0,0 +1,46 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +const _Request_Schema = z.array(ObjectWithRequiredField._schema); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField[]; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField[], + ) => serializers.endpoints.container.getAndReturnListOfObjects.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField[], + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField[]) => + parsed.map((item) => + ObjectWithRequiredField.json(item), + ) as serializers.endpoints.container.getAndReturnListOfObjects.Request.Raw, +}; + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw[]; +} + +const _Response_Schema = z.array(ObjectWithRequiredField._schema); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField[]; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField[], + ) => serializers.endpoints.container.getAndReturnListOfObjects.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField[], + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField[]) => + parsed.map((item) => + ObjectWithRequiredField.json(item), + ) as serializers.endpoints.container.getAndReturnListOfObjects.Response.Raw, +}; + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts new file mode 100644 index 000000000000..d5c3cd1ae608 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.array(z.string()); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string[]; + json: (parsed: string[]) => serializers.endpoints.container.getAndReturnListOfPrimitives.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string[], + json: (parsed: string[]) => parsed as serializers.endpoints.container.getAndReturnListOfPrimitives.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string[]; +} + +const _Response_Schema = z.array(z.string()); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string[]; + json: (parsed: string[]) => serializers.endpoints.container.getAndReturnListOfPrimitives.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string[], + json: (parsed: string[]) => parsed as serializers.endpoints.container.getAndReturnListOfPrimitives.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts new file mode 100644 index 000000000000..000bdb1a9f9a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts @@ -0,0 +1,47 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +const _Request_Schema = z.record(z.string(), ObjectWithRequiredField._schema); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Record; + json: ( + parsed: Record, + ) => serializers.endpoints.container.getAndReturnMapOfPrimToObject.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as Record, + json: (parsed: Record) => + Object.fromEntries( + Object.entries(parsed).map(([k, v]) => [k, ObjectWithRequiredField.json(v)]), + ) as serializers.endpoints.container.getAndReturnMapOfPrimToObject.Request.Raw, +}; + +export declare namespace Request { + export type Raw = Record; +} + +const _Response_Schema = z.record(z.string(), ObjectWithRequiredField._schema); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Record; + json: ( + parsed: Record, + ) => serializers.endpoints.container.getAndReturnMapOfPrimToObject.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => + _Response_Schema.parse(raw) as Record, + json: (parsed: Record) => + Object.fromEntries( + Object.entries(parsed).map(([k, v]) => [k, ObjectWithRequiredField.json(v)]), + ) as serializers.endpoints.container.getAndReturnMapOfPrimToObject.Response.Raw, +}; + +export declare namespace Response { + export type Raw = Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts new file mode 100644 index 000000000000..f5db8ef1efae --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts @@ -0,0 +1,36 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.record(z.string(), z.string()); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Record; + json: (parsed: Record) => serializers.endpoints.container.getAndReturnMapPrimToPrim.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as Record, + json: (parsed: Record) => + parsed as serializers.endpoints.container.getAndReturnMapPrimToPrim.Request.Raw, +}; + +export declare namespace Request { + export type Raw = Record; +} + +const _Response_Schema = z.record(z.string(), z.string()); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Record; + json: (parsed: Record) => serializers.endpoints.container.getAndReturnMapPrimToPrim.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as Record, + json: (parsed: Record) => + parsed as serializers.endpoints.container.getAndReturnMapPrimToPrim.Response.Raw, +}; + +export declare namespace Response { + export type Raw = Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts new file mode 100644 index 000000000000..fb36dd017dc2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts @@ -0,0 +1,46 @@ +// This file was auto-generated by Fern from our API Definition. + +import type { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +const _Request_Schema = ObjectWithRequiredField._schema.optional(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField | undefined; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField | undefined, + ) => serializers.endpoints.container.getAndReturnOptional.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField | undefined, + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField | undefined) => + parsed != null + ? ObjectWithRequiredField.json(parsed) + : (parsed as serializers.endpoints.container.getAndReturnOptional.Request.Raw), +}; + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw | null | undefined; +} + +const _Response_Schema = ObjectWithRequiredField._schema.optional(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField | undefined; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField | undefined, + ) => serializers.endpoints.container.getAndReturnOptional.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField | undefined, + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField | undefined) => + parsed != null + ? ObjectWithRequiredField.json(parsed) + : (parsed as serializers.endpoints.container.getAndReturnOptional.Response.Raw), +}; + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw | null | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts new file mode 100644 index 000000000000..0c85a32c4a09 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts @@ -0,0 +1,46 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +const _Request_Schema = z.array(ObjectWithRequiredField._schema); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField[]; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField[], + ) => serializers.endpoints.container.getAndReturnSetOfObjects.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField[], + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField[]) => + parsed.map((item) => + ObjectWithRequiredField.json(item), + ) as serializers.endpoints.container.getAndReturnSetOfObjects.Request.Raw, +}; + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw[]; +} + +const _Response_Schema = z.array(ObjectWithRequiredField._schema); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField[]; + json: ( + parsed: SeedExhaustive.types.ObjectWithRequiredField[], + ) => serializers.endpoints.container.getAndReturnSetOfObjects.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField[], + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField[]) => + parsed.map((item) => + ObjectWithRequiredField.json(item), + ) as serializers.endpoints.container.getAndReturnSetOfObjects.Response.Raw, +}; + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts new file mode 100644 index 000000000000..f80628d1136e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts @@ -0,0 +1,36 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.array(z.string()).transform((arr) => new Set(arr)); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Set; + json: (parsed: Set) => serializers.endpoints.container.getAndReturnSetOfPrimitives.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as Set, + json: (parsed: Set) => + Array.from(parsed) as serializers.endpoints.container.getAndReturnSetOfPrimitives.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string[]; +} + +const _Response_Schema = z.array(z.string()).transform((arr) => new Set(arr)); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Set; + json: (parsed: Set) => serializers.endpoints.container.getAndReturnSetOfPrimitives.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as Set, + json: (parsed: Set) => + Array.from(parsed) as serializers.endpoints.container.getAndReturnSetOfPrimitives.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/index.ts new file mode 100644 index 000000000000..1972f5b270f4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/client/index.ts @@ -0,0 +1,7 @@ +export * as getAndReturnListOfObjects from "./getAndReturnListOfObjects.js"; +export * as getAndReturnListOfPrimitives from "./getAndReturnListOfPrimitives.js"; +export * as getAndReturnMapOfPrimToObject from "./getAndReturnMapOfPrimToObject.js"; +export * as getAndReturnMapPrimToPrim from "./getAndReturnMapPrimToPrim.js"; +export * as getAndReturnOptional from "./getAndReturnOptional.js"; +export * as getAndReturnSetOfObjects from "./getAndReturnSetOfObjects.js"; +export * as getAndReturnSetOfPrimitives from "./getAndReturnSetOfPrimitives.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/container/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts new file mode 100644 index 000000000000..e053119b972c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts @@ -0,0 +1,2 @@ +export * as testDelete from "./testDelete.js"; +export * as testGet from "./testGet.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts new file mode 100644 index 000000000000..f770bb3d0434 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.boolean(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => boolean; + json: (parsed: boolean) => serializers.endpoints.httpMethods.testDelete.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as boolean, + json: (parsed: boolean) => parsed as serializers.endpoints.httpMethods.testDelete.Response.Raw, +}; + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts new file mode 100644 index 000000000000..ee29c6d64e90 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.httpMethods.testGet.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.httpMethods.testGet.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/httpMethods/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/index.ts new file mode 100644 index 000000000000..cd7e640937da --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/index.ts @@ -0,0 +1,8 @@ +export * as container from "./container/index.js"; +export * as httpMethods from "./httpMethods/index.js"; +export * as object from "./object/index.js"; +export * as params from "./params/index.js"; +export * as primitive from "./primitive/index.js"; +export * as put from "./put/index.js"; +export * from "./put/types/index.js"; +export * as urls from "./urls/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts new file mode 100644 index 000000000000..2ffc5ec115c4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { NestedObjectWithRequiredField } from "../../../../types/resources/object/types/NestedObjectWithRequiredField.js"; + +const _Request_Schema = z.array(NestedObjectWithRequiredField._schema); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.NestedObjectWithRequiredField[]; + json: ( + parsed: SeedExhaustive.types.NestedObjectWithRequiredField[], + ) => serializers.endpoints.object.getAndReturnNestedWithRequiredFieldAsList.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as SeedExhaustive.types.NestedObjectWithRequiredField[], + json: (parsed: SeedExhaustive.types.NestedObjectWithRequiredField[]) => + parsed.map((item) => + NestedObjectWithRequiredField.json(item), + ) as serializers.endpoints.object.getAndReturnNestedWithRequiredFieldAsList.Request.Raw, +}; + +export declare namespace Request { + export type Raw = NestedObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/index.ts new file mode 100644 index 000000000000..d00c4c582910 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/client/index.ts @@ -0,0 +1 @@ +export * as getAndReturnNestedWithRequiredFieldAsList from "./getAndReturnNestedWithRequiredFieldAsList.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts new file mode 100644 index 000000000000..62d66628879c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.getWithInlinePath.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.getWithInlinePath.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts new file mode 100644 index 000000000000..28e0a88b1335 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.getWithPath.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.getWithPath.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/index.ts new file mode 100644 index 000000000000..b30603cae602 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/index.ts @@ -0,0 +1,4 @@ +export * as getWithInlinePath from "./getWithInlinePath.js"; +export * as getWithPath from "./getWithPath.js"; +export * as modifyWithInlinePath from "./modifyWithInlinePath.js"; +export * as modifyWithPath from "./modifyWithPath.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts new file mode 100644 index 000000000000..920948641520 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.modifyWithInlinePath.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.modifyWithInlinePath.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.modifyWithInlinePath.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.modifyWithInlinePath.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts new file mode 100644 index 000000000000..f2004c062f3b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.modifyWithPath.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.modifyWithPath.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.params.modifyWithPath.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.params.modifyWithPath.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/params/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts new file mode 100644 index 000000000000..7761ce306fe3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnBase64.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnBase64.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnBase64.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnBase64.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts new file mode 100644 index 000000000000..ea9575e1ce1e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.boolean(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => boolean; + json: (parsed: boolean) => serializers.endpoints.primitive.getAndReturnBool.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as boolean, + json: (parsed: boolean) => parsed as serializers.endpoints.primitive.getAndReturnBool.Request.Raw, +}; + +export declare namespace Request { + export type Raw = boolean; +} + +const _Response_Schema = z.boolean(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => boolean; + json: (parsed: boolean) => serializers.endpoints.primitive.getAndReturnBool.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as boolean, + json: (parsed: boolean) => parsed as serializers.endpoints.primitive.getAndReturnBool.Response.Raw, +}; + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts new file mode 100644 index 000000000000..52cc217d53a2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnDate.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnDate.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnDate.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnDate.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts new file mode 100644 index 000000000000..337a002320dc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string().transform((s) => new Date(s)); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Date; + json: (parsed: Date) => serializers.endpoints.primitive.getAndReturnDatetime.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as Date, + json: (parsed: Date) => parsed.toISOString() as serializers.endpoints.primitive.getAndReturnDatetime.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string().transform((s) => new Date(s)); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => Date; + json: (parsed: Date) => serializers.endpoints.primitive.getAndReturnDatetime.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as Date, + json: (parsed: Date) => parsed.toISOString() as serializers.endpoints.primitive.getAndReturnDatetime.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts new file mode 100644 index 000000000000..b04a1153d28b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.number(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnDouble.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnDouble.Request.Raw, +}; + +export declare namespace Request { + export type Raw = number; +} + +const _Response_Schema = z.number(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnDouble.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnDouble.Response.Raw, +}; + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts new file mode 100644 index 000000000000..40b6b5b97418 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.number(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnInt.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnInt.Request.Raw, +}; + +export declare namespace Request { + export type Raw = number; +} + +const _Response_Schema = z.number(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnInt.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnInt.Response.Raw, +}; + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts new file mode 100644 index 000000000000..0fc1f2cdff59 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.number(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnLong.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnLong.Request.Raw, +}; + +export declare namespace Request { + export type Raw = number; +} + +const _Response_Schema = z.number(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => number; + json: (parsed: number) => serializers.endpoints.primitive.getAndReturnLong.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as number, + json: (parsed: number) => parsed as serializers.endpoints.primitive.getAndReturnLong.Response.Raw, +}; + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts new file mode 100644 index 000000000000..ec9d5995de47 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnString.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnString.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnString.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnString.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts new file mode 100644 index 000000000000..7f5c79800271 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts @@ -0,0 +1,34 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnUuid.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnUuid.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.primitive.getAndReturnUuid.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.primitive.getAndReturnUuid.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/index.ts new file mode 100644 index 000000000000..9805ab86b129 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/client/index.ts @@ -0,0 +1,9 @@ +export * as getAndReturnBase64 from "./getAndReturnBase64.js"; +export * as getAndReturnBool from "./getAndReturnBool.js"; +export * as getAndReturnDate from "./getAndReturnDate.js"; +export * as getAndReturnDatetime from "./getAndReturnDatetime.js"; +export * as getAndReturnDouble from "./getAndReturnDouble.js"; +export * as getAndReturnInt from "./getAndReturnInt.js"; +export * as getAndReturnLong from "./getAndReturnLong.js"; +export * as getAndReturnString from "./getAndReturnString.js"; +export * as getAndReturnUuid from "./getAndReturnUuid.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/primitive/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts new file mode 100644 index 000000000000..fda956fe3da3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ErrorCategory_Schema = z.enum(["API_ERROR", "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR"]); +export const ErrorCategory: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.endpoints.ErrorCategory; + json: (parsed: SeedExhaustive.endpoints.ErrorCategory) => serializers.endpoints.ErrorCategory.Raw; +} = { + _schema: _ErrorCategory_Schema, + parse: (raw: unknown) => _ErrorCategory_Schema.parse(raw) as SeedExhaustive.endpoints.ErrorCategory, + json: (parsed: SeedExhaustive.endpoints.ErrorCategory) => parsed as serializers.endpoints.ErrorCategory.Raw, +}; + +export declare namespace ErrorCategory { + export type Raw = "API_ERROR" | "AUTHENTICATION_ERROR" | "INVALID_REQUEST_ERROR"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts new file mode 100644 index 000000000000..0daa10ba1c93 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts @@ -0,0 +1,43 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ErrorCode_Schema = z.enum([ + "INTERNAL_SERVER_ERROR", + "UNAUTHORIZED", + "FORBIDDEN", + "BAD_REQUEST", + "CONFLICT", + "GONE", + "UNPROCESSABLE_ENTITY", + "NOT_IMPLEMENTED", + "BAD_GATEWAY", + "SERVICE_UNAVAILABLE", + "Unknown", +]); +export const ErrorCode: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.endpoints.ErrorCode; + json: (parsed: SeedExhaustive.endpoints.ErrorCode) => serializers.endpoints.ErrorCode.Raw; +} = { + _schema: _ErrorCode_Schema, + parse: (raw: unknown) => _ErrorCode_Schema.parse(raw) as SeedExhaustive.endpoints.ErrorCode, + json: (parsed: SeedExhaustive.endpoints.ErrorCode) => parsed as serializers.endpoints.ErrorCode.Raw, +}; + +export declare namespace ErrorCode { + export type Raw = + | "INTERNAL_SERVER_ERROR" + | "UNAUTHORIZED" + | "FORBIDDEN" + | "BAD_REQUEST" + | "CONFLICT" + | "GONE" + | "UNPROCESSABLE_ENTITY" + | "NOT_IMPLEMENTED" + | "BAD_GATEWAY" + | "SERVICE_UNAVAILABLE" + | "Unknown"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/Error_.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/Error_.ts new file mode 100644 index 000000000000..917889e06380 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/Error_.ts @@ -0,0 +1,38 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ErrorCategory } from "./ErrorCategory.js"; +import { ErrorCode } from "./ErrorCode.js"; + +const _Error__Schema = z.object({ + category: ErrorCategory._schema, + code: ErrorCode._schema, + detail: z.string().optional(), + field: z.string().optional(), +}); +export const Error_: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.endpoints.Error_; + json: (parsed: SeedExhaustive.endpoints.Error_) => serializers.endpoints.Error_.Raw; +} = { + _schema: _Error__Schema, + parse: (raw: unknown) => _Error__Schema.parse(raw) as SeedExhaustive.endpoints.Error_, + json: (parsed: SeedExhaustive.endpoints.Error_) => + ({ + category: ErrorCategory.json(parsed.category), + code: ErrorCode.json(parsed.code), + detail: parsed.detail, + field: parsed.field, + }) as serializers.endpoints.Error_.Raw, +}; + +export declare namespace Error_ { + export interface Raw { + category: ErrorCategory.Raw; + code: ErrorCode.Raw; + detail?: string | null; + field?: string | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts new file mode 100644 index 000000000000..520e6cfb62b7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { Error_ } from "./Error_.js"; + +const _PutResponse_Schema = z.object({ + errors: z.array(Error_._schema).optional(), +}); +export const PutResponse: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.endpoints.PutResponse; + json: (parsed: SeedExhaustive.endpoints.PutResponse) => serializers.endpoints.PutResponse.Raw; +} = { + _schema: _PutResponse_Schema, + parse: (raw: unknown) => _PutResponse_Schema.parse(raw) as SeedExhaustive.endpoints.PutResponse, + json: (parsed: SeedExhaustive.endpoints.PutResponse) => + ({ + errors: + parsed.errors != null + ? parsed.errors != null + ? parsed.errors.map((item) => Error_.json(item)) + : parsed.errors + : parsed.errors, + }) as serializers.endpoints.PutResponse.Raw, +}; + +export declare namespace PutResponse { + export interface Raw { + errors?: Error_.Raw[] | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/index.ts new file mode 100644 index 000000000000..3293f66bb34c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/put/types/index.ts @@ -0,0 +1,4 @@ +export * from "./Error_.js"; +export * from "./ErrorCategory.js"; +export * from "./ErrorCode.js"; +export * from "./PutResponse.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/index.ts new file mode 100644 index 000000000000..66e101cdc2d7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/index.ts @@ -0,0 +1,4 @@ +export * as noEndingSlash from "./noEndingSlash.js"; +export * as withEndingSlash from "./withEndingSlash.js"; +export * as withMixedCase from "./withMixedCase.js"; +export * as withUnderscores from "./withUnderscores.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts new file mode 100644 index 000000000000..792cdff843dd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.urls.noEndingSlash.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.urls.noEndingSlash.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts new file mode 100644 index 000000000000..05918aed9575 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.urls.withEndingSlash.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.urls.withEndingSlash.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts new file mode 100644 index 000000000000..ef6df73d7ec0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.urls.withMixedCase.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.urls.withMixedCase.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts new file mode 100644 index 000000000000..3e792cbc8530 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.endpoints.urls.withUnderscores.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.endpoints.urls.withUnderscores.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/endpoints/resources/urls/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts new file mode 100644 index 000000000000..f7a345d3368a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../api/index.js"; +import type * as serializers from "../../../index.js"; + +const _BadObjectRequestInfo_Schema = z.object({ + message: z.string(), +}); +export const BadObjectRequestInfo: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.BadObjectRequestInfo; + json: (parsed: SeedExhaustive.BadObjectRequestInfo) => serializers.BadObjectRequestInfo.Raw; +} = { + _schema: _BadObjectRequestInfo_Schema, + parse: (raw: unknown) => _BadObjectRequestInfo_Schema.parse(raw) as SeedExhaustive.BadObjectRequestInfo, + json: (parsed: SeedExhaustive.BadObjectRequestInfo) => parsed as serializers.BadObjectRequestInfo.Raw, +}; + +export declare namespace BadObjectRequestInfo { + export interface Raw { + message: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/index.ts new file mode 100644 index 000000000000..b10afa3b7749 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/generalErrors/types/index.ts @@ -0,0 +1 @@ +export * from "./BadObjectRequestInfo.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/index.ts new file mode 100644 index 000000000000..20b614a89e2f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/index.ts @@ -0,0 +1,9 @@ +export * as endpoints from "./endpoints/index.js"; +export * as generalErrors from "./generalErrors/index.js"; +export * from "./generalErrors/types/index.js"; +export * from "./inlinedRequests/client/requests/index.js"; +export * as inlinedRequests from "./inlinedRequests/index.js"; +export * as noAuth from "./noAuth/index.js"; +export * as noReqBody from "./noReqBody/index.js"; +export * as reqWithHeaders from "./reqWithHeaders/index.js"; +export * as types from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts new file mode 100644 index 000000000000..afdc575ededd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts @@ -0,0 +1,40 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../api/index.js"; +import type * as serializers from "../../../../index.js"; +import { ObjectWithOptionalField } from "../../../types/resources/object/types/ObjectWithOptionalField.js"; + +const _PostWithObjectBody_Schema = z + .object({ + string: z.string(), + integer: z.number(), + NestedObject: ObjectWithOptionalField._schema, + }) + .transform((data) => ({ + string: data.string, + integer: data.integer, + nestedObject: data.NestedObject, + })); +export const PostWithObjectBody: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.PostWithObjectBody; + json: (parsed: SeedExhaustive.PostWithObjectBody) => serializers.PostWithObjectBody.Raw; +} = { + _schema: _PostWithObjectBody_Schema, + parse: (raw: unknown) => _PostWithObjectBody_Schema.parse(raw) as SeedExhaustive.PostWithObjectBody, + json: (parsed: SeedExhaustive.PostWithObjectBody) => + ({ + string: parsed.string, + integer: parsed.integer, + NestedObject: ObjectWithOptionalField.json(parsed.nestedObject), + }) as serializers.PostWithObjectBody.Raw, +}; + +export declare namespace PostWithObjectBody { + export interface Raw { + string: string; + integer: number; + NestedObject: ObjectWithOptionalField.Raw; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/index.ts new file mode 100644 index 000000000000..d62a81ce61fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/client/requests/index.ts @@ -0,0 +1 @@ +export { PostWithObjectBody } from "./PostWithObjectBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/inlinedRequests/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/index.ts new file mode 100644 index 000000000000..549edcdba7f2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/index.ts @@ -0,0 +1 @@ +export * as postWithNoAuth from "./postWithNoAuth.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/postWithNoAuth.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/postWithNoAuth.ts new file mode 100644 index 000000000000..7c8d3bf63cc1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/client/postWithNoAuth.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../index.js"; + +const _Response_Schema = z.boolean(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => boolean; + json: (parsed: boolean) => serializers.noAuth.postWithNoAuth.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as boolean, + json: (parsed: boolean) => parsed as serializers.noAuth.postWithNoAuth.Response.Raw, +}; + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noAuth/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/index.ts new file mode 100644 index 000000000000..b62616349ec6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/index.ts @@ -0,0 +1 @@ +export * as postWithNoRequestBody from "./postWithNoRequestBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts new file mode 100644 index 000000000000..5880dd1a7111 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../index.js"; + +const _Response_Schema = z.string(); +export const Response: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.noReqBody.postWithNoRequestBody.Response.Raw; +} = { + _schema: _Response_Schema, + parse: (raw: unknown) => _Response_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.noReqBody.postWithNoRequestBody.Response.Raw, +}; + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/noReqBody/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts new file mode 100644 index 000000000000..f3d5598805ba --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as serializers from "../../../index.js"; + +const _Request_Schema = z.string(); +export const Request: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => string; + json: (parsed: string) => serializers.reqWithHeaders.getWithCustomHeader.Request.Raw; +} = { + _schema: _Request_Schema, + parse: (raw: unknown) => _Request_Schema.parse(raw) as string, + json: (parsed: string) => parsed as serializers.reqWithHeaders.getWithCustomHeader.Request.Raw, +}; + +export declare namespace Request { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/index.ts new file mode 100644 index 000000000000..fb7f25afb170 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/client/index.ts @@ -0,0 +1 @@ +export * as getWithCustomHeader from "./getWithCustomHeader.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/reqWithHeaders/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts new file mode 100644 index 000000000000..fda1be806c19 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ObjectWithDocs_Schema = z.object({ + string: z.string(), +}); +export const ObjectWithDocs: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithDocs; + json: (parsed: SeedExhaustive.types.ObjectWithDocs) => serializers.types.ObjectWithDocs.Raw; +} = { + _schema: _ObjectWithDocs_Schema, + parse: (raw: unknown) => _ObjectWithDocs_Schema.parse(raw) as SeedExhaustive.types.ObjectWithDocs, + json: (parsed: SeedExhaustive.types.ObjectWithDocs) => parsed as serializers.types.ObjectWithDocs.Raw, +}; + +export declare namespace ObjectWithDocs { + export interface Raw { + string: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/index.ts new file mode 100644 index 000000000000..3eedfcba912c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/docs/types/index.ts @@ -0,0 +1 @@ +export * from "./ObjectWithDocs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/WeatherReport.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/WeatherReport.ts new file mode 100644 index 000000000000..feebc1f6a916 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/WeatherReport.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _WeatherReport_Schema = z.enum(["SUNNY", "CLOUDY", "RAINING", "SNOWING"]); +export const WeatherReport: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.WeatherReport; + json: (parsed: SeedExhaustive.types.WeatherReport) => serializers.types.WeatherReport.Raw; +} = { + _schema: _WeatherReport_Schema, + parse: (raw: unknown) => _WeatherReport_Schema.parse(raw) as SeedExhaustive.types.WeatherReport, + json: (parsed: SeedExhaustive.types.WeatherReport) => parsed as serializers.types.WeatherReport.Raw, +}; + +export declare namespace WeatherReport { + export type Raw = "SUNNY" | "CLOUDY" | "RAINING" | "SNOWING"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/index.ts new file mode 100644 index 000000000000..3b2d63911a15 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/enum/types/index.ts @@ -0,0 +1 @@ +export * from "./WeatherReport.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/index.ts new file mode 100644 index 000000000000..52175f3cee16 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/index.ts @@ -0,0 +1,8 @@ +export * as docs from "./docs/index.js"; +export * from "./docs/types/index.js"; +export * as enum_ from "./enum/index.js"; +export * from "./enum/types/index.js"; +export * as object from "./object/index.js"; +export * from "./object/types/index.js"; +export * as union from "./union/index.js"; +export * from "./union/types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/DoubleOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/DoubleOptional.ts new file mode 100644 index 000000000000..a68dc56bdfba --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/DoubleOptional.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { OptionalAlias } from "./OptionalAlias.js"; + +const _DoubleOptional_Schema = z.object({ + optionalAlias: OptionalAlias._schema.optional(), +}); +export const DoubleOptional: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.DoubleOptional; + json: (parsed: SeedExhaustive.types.DoubleOptional) => serializers.types.DoubleOptional.Raw; +} = { + _schema: _DoubleOptional_Schema, + parse: (raw: unknown) => _DoubleOptional_Schema.parse(raw) as SeedExhaustive.types.DoubleOptional, + json: (parsed: SeedExhaustive.types.DoubleOptional) => + ({ + optionalAlias: + parsed.optionalAlias != null + ? parsed.optionalAlias != null + ? OptionalAlias.json(parsed.optionalAlias) + : parsed.optionalAlias + : parsed.optionalAlias, + }) as serializers.types.DoubleOptional.Raw, +}; + +export declare namespace DoubleOptional { + export interface Raw { + optionalAlias?: (OptionalAlias.Raw | undefined) | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts new file mode 100644 index 000000000000..c90be8618d11 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts @@ -0,0 +1,44 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithOptionalField } from "./ObjectWithOptionalField.js"; + +const _NestedObjectWithOptionalField_Schema = z + .object({ + string: z.string().optional(), + NestedObject: ObjectWithOptionalField._schema.optional(), + }) + .transform((data) => ({ + string: data.string, + nestedObject: data.NestedObject, + })); +export const NestedObjectWithOptionalField: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.NestedObjectWithOptionalField; + json: ( + parsed: SeedExhaustive.types.NestedObjectWithOptionalField, + ) => serializers.types.NestedObjectWithOptionalField.Raw; +} = { + _schema: _NestedObjectWithOptionalField_Schema, + parse: (raw: unknown) => + _NestedObjectWithOptionalField_Schema.parse(raw) as SeedExhaustive.types.NestedObjectWithOptionalField, + json: (parsed: SeedExhaustive.types.NestedObjectWithOptionalField) => + ({ + string: parsed.string, + NestedObject: + parsed.nestedObject != null + ? parsed.nestedObject != null + ? ObjectWithOptionalField.json(parsed.nestedObject) + : parsed.nestedObject + : parsed.nestedObject, + }) as serializers.types.NestedObjectWithOptionalField.Raw, +}; + +export declare namespace NestedObjectWithOptionalField { + export interface Raw { + string?: string | null; + NestedObject?: ObjectWithOptionalField.Raw | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts new file mode 100644 index 000000000000..ef7b8353d527 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts @@ -0,0 +1,39 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithOptionalField } from "./ObjectWithOptionalField.js"; + +const _NestedObjectWithRequiredField_Schema = z + .object({ + string: z.string(), + NestedObject: ObjectWithOptionalField._schema, + }) + .transform((data) => ({ + string: data.string, + nestedObject: data.NestedObject, + })); +export const NestedObjectWithRequiredField: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.NestedObjectWithRequiredField; + json: ( + parsed: SeedExhaustive.types.NestedObjectWithRequiredField, + ) => serializers.types.NestedObjectWithRequiredField.Raw; +} = { + _schema: _NestedObjectWithRequiredField_Schema, + parse: (raw: unknown) => + _NestedObjectWithRequiredField_Schema.parse(raw) as SeedExhaustive.types.NestedObjectWithRequiredField, + json: (parsed: SeedExhaustive.types.NestedObjectWithRequiredField) => + ({ + string: parsed.string, + NestedObject: ObjectWithOptionalField.json(parsed.nestedObject), + }) as serializers.types.NestedObjectWithRequiredField.Raw, +}; + +export declare namespace NestedObjectWithRequiredField { + export interface Raw { + string: string; + NestedObject: ObjectWithOptionalField.Raw; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts new file mode 100644 index 000000000000..714c7594bacd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ObjectWithMapOfMap_Schema = z.object({ + map: z.record(z.string(), z.record(z.string(), z.string())), +}); +export const ObjectWithMapOfMap: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithMapOfMap; + json: (parsed: SeedExhaustive.types.ObjectWithMapOfMap) => serializers.types.ObjectWithMapOfMap.Raw; +} = { + _schema: _ObjectWithMapOfMap_Schema, + parse: (raw: unknown) => _ObjectWithMapOfMap_Schema.parse(raw) as SeedExhaustive.types.ObjectWithMapOfMap, + json: (parsed: SeedExhaustive.types.ObjectWithMapOfMap) => parsed as serializers.types.ObjectWithMapOfMap.Raw, +}; + +export declare namespace ObjectWithMapOfMap { + export interface Raw { + map: Record>; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts new file mode 100644 index 000000000000..29084d0e7dc7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts @@ -0,0 +1,74 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ObjectWithOptionalField_Schema = z.object({ + string: z.string().optional(), + integer: z.number().optional(), + long: z.number().optional(), + double: z.number().optional(), + bool: z.boolean().optional(), + datetime: z + .string() + .transform((s) => new Date(s)) + .optional(), + date: z.string().optional(), + uuid: z.string().optional(), + base64: z.string().optional(), + list: z.array(z.string()).optional(), + set: z + .array(z.string()) + .transform((arr) => new Set(arr)) + .optional(), + map: z.record(z.string(), z.string()).optional(), + bigint: z.string().optional(), +}); +export const ObjectWithOptionalField: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithOptionalField; + json: (parsed: SeedExhaustive.types.ObjectWithOptionalField) => serializers.types.ObjectWithOptionalField.Raw; +} = { + _schema: _ObjectWithOptionalField_Schema, + parse: (raw: unknown) => _ObjectWithOptionalField_Schema.parse(raw) as SeedExhaustive.types.ObjectWithOptionalField, + json: (parsed: SeedExhaustive.types.ObjectWithOptionalField) => + ({ + string: parsed.string, + integer: parsed.integer, + long: parsed.long, + double: parsed.double, + bool: parsed.bool, + datetime: + parsed.datetime != null + ? parsed.datetime != null + ? parsed.datetime.toISOString() + : parsed.datetime + : parsed.datetime, + date: parsed.date, + uuid: parsed.uuid, + base64: parsed.base64, + list: parsed.list, + set: parsed.set != null ? (parsed.set != null ? Array.from(parsed.set) : parsed.set) : parsed.set, + map: parsed.map, + bigint: parsed.bigint, + }) as serializers.types.ObjectWithOptionalField.Raw, +}; + +export declare namespace ObjectWithOptionalField { + export interface Raw { + string?: string | null; + integer?: number | null; + long?: number | null; + double?: number | null; + bool?: boolean | null; + datetime?: string | null; + date?: string | null; + uuid?: string | null; + base64?: string | null; + list?: string[] | null; + set?: string[] | null; + map?: Record | null; + bigint?: string | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts new file mode 100644 index 000000000000..a721771913f9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts @@ -0,0 +1,25 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _ObjectWithRequiredField_Schema = z.object({ + string: z.string(), +}); +export const ObjectWithRequiredField: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.ObjectWithRequiredField; + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField) => serializers.types.ObjectWithRequiredField.Raw; +} = { + _schema: _ObjectWithRequiredField_Schema, + parse: (raw: unknown) => _ObjectWithRequiredField_Schema.parse(raw) as SeedExhaustive.types.ObjectWithRequiredField, + json: (parsed: SeedExhaustive.types.ObjectWithRequiredField) => + parsed as serializers.types.ObjectWithRequiredField.Raw, +}; + +export declare namespace ObjectWithRequiredField { + export interface Raw { + string: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/OptionalAlias.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/OptionalAlias.ts new file mode 100644 index 000000000000..a243622f1ee5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/OptionalAlias.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _OptionalAlias_Schema = z.string().optional(); +export const OptionalAlias: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.OptionalAlias; + json: (parsed: SeedExhaustive.types.OptionalAlias) => serializers.types.OptionalAlias.Raw; +} = { + _schema: _OptionalAlias_Schema, + parse: (raw: unknown) => _OptionalAlias_Schema.parse(raw) as SeedExhaustive.types.OptionalAlias, + json: (parsed: SeedExhaustive.types.OptionalAlias) => parsed as serializers.types.OptionalAlias.Raw, +}; + +export declare namespace OptionalAlias { + export type Raw = string | null | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/index.ts new file mode 100644 index 000000000000..c3c65c311b82 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/object/types/index.ts @@ -0,0 +1,7 @@ +export * from "./DoubleOptional.js"; +export * from "./NestedObjectWithOptionalField.js"; +export * from "./NestedObjectWithRequiredField.js"; +export * from "./ObjectWithMapOfMap.js"; +export * from "./ObjectWithOptionalField.js"; +export * from "./ObjectWithRequiredField.js"; +export * from "./OptionalAlias.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Animal.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Animal.ts new file mode 100644 index 000000000000..91b493ad4f9a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Animal.ts @@ -0,0 +1,35 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; +import { Cat } from "./Cat.js"; +import { Dog } from "./Dog.js"; + +const _Animal_Schema = z + .discriminatedUnion("animal", [ + z.object({ animal: z.literal("dog") }).merge(Dog._schema as z.AnyZodObject), + z.object({ animal: z.literal("cat") }).merge(Cat._schema as z.AnyZodObject), + ]) + .transform((value) => value); +export const Animal: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.Animal; + json: (parsed: SeedExhaustive.types.Animal) => serializers.types.Animal.Raw; +} = { + _schema: _Animal_Schema, + parse: (raw: unknown) => _Animal_Schema.parse(raw) as SeedExhaustive.types.Animal, + json: (parsed: SeedExhaustive.types.Animal) => parsed as serializers.types.Animal.Raw, +}; + +export declare namespace Animal { + export type Raw = Animal.Dog | Animal.Cat; + + export interface Dog extends Dog.Raw { + animal: "dog"; + } + + export interface Cat extends Cat.Raw { + animal: "cat"; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Cat.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Cat.ts new file mode 100644 index 000000000000..0d6da57ce642 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Cat.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _Cat_Schema = z.object({ + name: z.string(), + likesToMeow: z.boolean(), +}); +export const Cat: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.Cat; + json: (parsed: SeedExhaustive.types.Cat) => serializers.types.Cat.Raw; +} = { + _schema: _Cat_Schema, + parse: (raw: unknown) => _Cat_Schema.parse(raw) as SeedExhaustive.types.Cat, + json: (parsed: SeedExhaustive.types.Cat) => parsed as serializers.types.Cat.Raw, +}; + +export declare namespace Cat { + export interface Raw { + name: string; + likesToMeow: boolean; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Dog.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Dog.ts new file mode 100644 index 000000000000..0547a8bc7e85 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/Dog.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import { z } from "zod"; +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as serializers from "../../../../../index.js"; + +const _Dog_Schema = z.object({ + name: z.string(), + likesToWoof: z.boolean(), +}); +export const Dog: { + _schema: z.ZodTypeAny; + parse: (raw: unknown) => SeedExhaustive.types.Dog; + json: (parsed: SeedExhaustive.types.Dog) => serializers.types.Dog.Raw; +} = { + _schema: _Dog_Schema, + parse: (raw: unknown) => _Dog_Schema.parse(raw) as SeedExhaustive.types.Dog, + json: (parsed: SeedExhaustive.types.Dog) => parsed as serializers.types.Dog.Raw, +}; + +export declare namespace Dog { + export interface Raw { + name: string; + likesToWoof: boolean; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/index.ts new file mode 100644 index 000000000000..342e90adc057 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/serialization/resources/types/resources/union/types/index.ts @@ -0,0 +1,3 @@ +export * from "./Animal.js"; +export * from "./Cat.js"; +export * from "./Dog.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/src/version.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/src/version.ts new file mode 100644 index 000000000000..b643a3e3ea27 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/custom.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/custom.test.ts new file mode 100644 index 000000000000..7f5e031c8396 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/custom.test.ts @@ -0,0 +1,13 @@ +/** + * 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/exhaustive/serde-layer-zod/tests/mock-server/MockServer.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/MockServer.ts new file mode 100644 index 000000000000..954872157d52 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/MockServer.ts @@ -0,0 +1,29 @@ +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/exhaustive/serde-layer-zod/tests/mock-server/MockServerPool.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/MockServerPool.ts new file mode 100644 index 000000000000..e1a90f7fb2e3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/MockServerPool.ts @@ -0,0 +1,106 @@ +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/exhaustive/serde-layer-zod/tests/mock-server/mockEndpointBuilder.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/mockEndpointBuilder.ts new file mode 100644 index 000000000000..1b0e51079e6b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/mockEndpointBuilder.ts @@ -0,0 +1,227 @@ +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/exhaustive/serde-layer-zod/tests/mock-server/randomBaseUrl.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/randomBaseUrl.ts new file mode 100644 index 000000000000..031aa6408aca --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/randomBaseUrl.ts @@ -0,0 +1,4 @@ +export function randomBaseUrl(): string { + const randomString = Math.random().toString(36).substring(2, 15); + return `http://${randomString}.localhost`; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/setup.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/setup.ts new file mode 100644 index 000000000000..aeb3a95af7dc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/setup.ts @@ -0,0 +1,10 @@ +import { afterAll, beforeAll } from "vitest"; + +import { mockServerPool } from "./MockServerPool"; + +beforeAll(() => { + mockServerPool.listen(); +}); +afterAll(() => { + mockServerPool.close(); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withFormUrlEncoded.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withFormUrlEncoded.ts new file mode 100644 index 000000000000..e9e6ff2d9cf1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withFormUrlEncoded.ts @@ -0,0 +1,80 @@ +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/exhaustive/serde-layer-zod/tests/mock-server/withHeaders.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withHeaders.ts new file mode 100644 index 000000000000..6599d2b4a92d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withHeaders.ts @@ -0,0 +1,70 @@ +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/exhaustive/serde-layer-zod/tests/mock-server/withJson.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withJson.ts new file mode 100644 index 000000000000..b627638b015f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/mock-server/withJson.ts @@ -0,0 +1,158 @@ +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/exhaustive/serde-layer-zod/tests/setup.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/setup.ts new file mode 100644 index 000000000000..a5651f81ba10 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/setup.ts @@ -0,0 +1,80 @@ +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/exhaustive/serde-layer-zod/tests/tsconfig.json b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/tsconfig.json new file mode 100644 index 000000000000..a477df47920c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": null, + "rootDir": "..", + "baseUrl": "..", + "types": ["vitest/globals"] + }, + "include": ["../src", "../tests"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/auth/BasicAuth.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/auth/BasicAuth.test.ts new file mode 100644 index 000000000000..9b5123364c47 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/auth/BasicAuth.test.ts @@ -0,0 +1,92 @@ +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/exhaustive/serde-layer-zod/tests/unit/auth/BearerToken.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/auth/BearerToken.test.ts new file mode 100644 index 000000000000..7757b87cb97e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/auth/BearerToken.test.ts @@ -0,0 +1,14 @@ +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/exhaustive/serde-layer-zod/tests/unit/base64.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/base64.test.ts new file mode 100644 index 000000000000..939594ca277b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/base64.test.ts @@ -0,0 +1,53 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/Fetcher.test.ts new file mode 100644 index 000000000000..60df2b5e4824 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/Fetcher.test.ts @@ -0,0 +1,261 @@ +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 reader = 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 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/exhaustive/serde-layer-zod/tests/unit/fetcher/HttpResponsePromise.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/HttpResponsePromise.test.ts new file mode 100644 index 000000000000..2ec008e581d8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/HttpResponsePromise.test.ts @@ -0,0 +1,143 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/RawResponse.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/RawResponse.test.ts new file mode 100644 index 000000000000..375ee3f38064 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/RawResponse.test.ts @@ -0,0 +1,34 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/createRequestUrl.test.ts new file mode 100644 index 000000000000..a92f1b5e81d1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/createRequestUrl.test.ts @@ -0,0 +1,163 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/getRequestBody.test.ts new file mode 100644 index 000000000000..8a6c3a57e211 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/getRequestBody.test.ts @@ -0,0 +1,129 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/getResponseBody.test.ts new file mode 100644 index 000000000000..ad6be7fc2c9b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/getResponseBody.test.ts @@ -0,0 +1,97 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/logging.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/logging.test.ts new file mode 100644 index 000000000000..366c9b6ced61 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/logging.test.ts @@ -0,0 +1,517 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/makeRequest.test.ts new file mode 100644 index 000000000000..ea49466a55fc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/makeRequest.test.ts @@ -0,0 +1,54 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/redacting.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/redacting.test.ts new file mode 100644 index 000000000000..d599376b9bcf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/redacting.test.ts @@ -0,0 +1,1115 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/requestWithRetries.test.ts new file mode 100644 index 000000000000..d22661367f4e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/requestWithRetries.test.ts @@ -0,0 +1,230 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/signals.test.ts new file mode 100644 index 000000000000..d7b6d1e63caa --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/signals.test.ts @@ -0,0 +1,69 @@ +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/exhaustive/serde-layer-zod/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/fetcher/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/logging/logger.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/logging/logger.test.ts new file mode 100644 index 000000000000..2e0b5fe5040c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/logging/logger.test.ts @@ -0,0 +1,454 @@ +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/exhaustive/serde-layer-zod/tests/unit/url/join.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/url/join.test.ts new file mode 100644 index 000000000000..123488f084ea --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/url/join.test.ts @@ -0,0 +1,284 @@ +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/exhaustive/serde-layer-zod/tests/unit/url/qs.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/url/qs.test.ts new file mode 100644 index 000000000000..42cdffb9e5ea --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/unit/url/qs.test.ts @@ -0,0 +1,278 @@ +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/exhaustive/serde-layer-zod/tests/wire/.gitkeep b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/container.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/container.test.ts new file mode 100644 index 000000000000..98b9ec0c58b9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/container.test.ts @@ -0,0 +1,170 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ContainerClient", () => { + test("getAndReturnListOfPrimitives", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = ["string", "string"]; + const rawResponseBody = ["string", "string"]; + server + .mockEndpoint() + .post("/container/list-of-primitives") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); + expect(response).toEqual(["string", "string"]); + }); + + test("getAndReturnListOfObjects", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [{ string: "string" }, { string: "string" }]; + const rawResponseBody = [{ string: "string" }, { string: "string" }]; + server + .mockEndpoint() + .post("/container/list-of-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnListOfObjects([ + { + string: "string", + }, + { + string: "string", + }, + ]); + expect(response).toEqual([ + { + string: "string", + }, + { + string: "string", + }, + ]); + }); + + test("getAndReturnSetOfPrimitives", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = ["string"]; + const rawResponseBody = ["string"]; + server + .mockEndpoint() + .post("/container/set-of-primitives") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])); + expect(response).toEqual(new Set(["string"])); + }); + + test("getAndReturnSetOfObjects", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [{ string: "string" }]; + const rawResponseBody = [{ string: "string" }]; + server + .mockEndpoint() + .post("/container/set-of-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnSetOfObjects([ + { + string: "string", + }, + ]); + expect(response).toEqual([ + { + string: "string", + }, + ]); + }); + + test("getAndReturnMapPrimToPrim", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/container/map-prim-to-prim") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnMapPrimToPrim({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); + + test("getAndReturnMapOfPrimToObject", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: { string: "string" } }; + const rawResponseBody = { string: { string: "string" } }; + server + .mockEndpoint() + .post("/container/map-prim-to-object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnMapOfPrimToObject({ + string: { + string: "string", + }, + }); + expect(response).toEqual({ + string: { + string: "string", + }, + }); + }); + + test("getAndReturnOptional", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/container/opt-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnOptional({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/contentType.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/contentType.test.ts new file mode 100644 index 000000000000..4bc8916f7ce5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/contentType.test.ts @@ -0,0 +1,88 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ContentTypeClient", () => { + test("postJsonPatchContentType", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + + server.mockEndpoint().post("/foo/bar").jsonBody(rawRequestBody).respondWith().statusCode(200).build(); + + const response = await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual(undefined); + }); + + test("postJsonPatchContentWithCharsetType", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + + server.mockEndpoint().post("/foo/baz").jsonBody(rawRequestBody).respondWith().statusCode(200).build(); + + const response = await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual(undefined); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/enum.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/enum.test.ts new file mode 100644 index 000000000000..5770be42a097 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/enum.test.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("EnumClient", () => { + test("getAndReturnEnum", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "SUNNY"; + const rawResponseBody = "SUNNY"; + server + .mockEndpoint() + .post("/enum") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.enum.getAndReturnEnum("SUNNY"); + expect(response).toEqual("SUNNY"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/httpMethods.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/httpMethods.test.ts new file mode 100644 index 000000000000..b9e26c032ff9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/httpMethods.test.ts @@ -0,0 +1,212 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("HttpMethodsClient", () => { + test("testGet", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/http-methods/id").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.httpMethods.testGet("id"); + expect(response).toEqual("string"); + }); + + test("testPost", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/http-methods") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPost({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testPut", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .put("/http-methods/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPut("id", { + string: "string", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testPatch", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .patch("/http-methods/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPatch("id", { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testDelete", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = true; + server + .mockEndpoint() + .delete("/http-methods/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testDelete("id"); + expect(response).toEqual(true); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/object.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/object.test.ts new file mode 100644 index 000000000000..685826602e5e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/object.test.ts @@ -0,0 +1,448 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ObjectClient", () => { + test("getAndReturnWithOptionalField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/object/get-and-return-with-optional-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithOptionalField({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("getAndReturnWithRequiredField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/object/get-and-return-with-required-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithRequiredField({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); + + test("getAndReturnWithMapOfMap", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { map: { map: { map: "map" } } }; + const rawResponseBody = { map: { map: { map: "map" } } }; + server + .mockEndpoint() + .post("/object/get-and-return-with-map-of-map") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithMapOfMap({ + map: { + map: { + map: "map", + }, + }, + }); + expect(response).toEqual({ + map: { + map: { + map: "map", + }, + }, + }); + }); + + test("getAndReturnNestedWithOptionalField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-optional-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithOptionalField({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); + + test("getAndReturnNestedWithRequiredField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-required-field/string") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); + + test("getAndReturnNestedWithRequiredFieldAsList", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [ + { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }, + { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }, + ]; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-required-field-list") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([ + { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }, + { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }, + ]); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/params.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/params.test.ts new file mode 100644 index 000000000000..be2e7ddd6591 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/params.test.ts @@ -0,0 +1,120 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ParamsClient", () => { + test("getWithPath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/params/path/param").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.params.getWithPath("param"); + expect(response).toEqual("string"); + }); + + test("getWithInlinePath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/params/path/param").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.params.getWithInlinePath({ + param: "param", + }); + expect(response).toEqual("string"); + }); + + test("getWithQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithQuery({ + query: "query", + number: 1, + }); + expect(response).toEqual(undefined); + }); + + test("getWithAllowMultipleQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithAllowMultipleQuery({ + query: "query", + number: 1, + }); + expect(response).toEqual(undefined); + }); + + test("getWithPathAndQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params/path-query/param").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithPathAndQuery("param", { + query: "query", + }); + expect(response).toEqual(undefined); + }); + + test("getWithInlinePathAndQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params/path-query/param").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithInlinePathAndQuery({ + param: "param", + query: "query", + }); + expect(response).toEqual(undefined); + }); + + test("modifyWithPath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .put("/params/path/param") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.params.modifyWithPath("param", "string"); + expect(response).toEqual("string"); + }); + + test("modifyWithInlinePath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .put("/params/path/param") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.params.modifyWithInlinePath({ + param: "param", + body: "string", + }); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/primitive.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/primitive.test.ts new file mode 100644 index 000000000000..650fc620bb1d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/primitive.test.ts @@ -0,0 +1,168 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("PrimitiveClient", () => { + test("getAndReturnString", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .post("/primitive/string") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnString("string"); + expect(response).toEqual("string"); + }); + + test("getAndReturnInt", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1; + const rawResponseBody = 1; + server + .mockEndpoint() + .post("/primitive/integer") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnInt(1); + expect(response).toEqual(1); + }); + + test("getAndReturnLong", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1000000; + const rawResponseBody = 1000000; + server + .mockEndpoint() + .post("/primitive/long") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnLong(1000000); + expect(response).toEqual(1000000); + }); + + test("getAndReturnDouble", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1.1; + const rawResponseBody = 1.1; + server + .mockEndpoint() + .post("/primitive/double") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDouble(1.1); + expect(response).toEqual(1.1); + }); + + test("getAndReturnBool", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = true; + const rawResponseBody = true; + server + .mockEndpoint() + .post("/primitive/boolean") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnBool(true); + expect(response).toEqual(true); + }); + + test("getAndReturnDatetime", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "2024-01-15T09:30:00Z"; + const rawResponseBody = "2024-01-15T09:30:00Z"; + server + .mockEndpoint() + .post("/primitive/datetime") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")); + expect(response).toEqual(new Date("2024-01-15T09:30:00.000Z")); + }); + + test("getAndReturnDate", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "2023-01-15"; + const rawResponseBody = "2023-01-15"; + server + .mockEndpoint() + .post("/primitive/date") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDate("2023-01-15"); + expect(response).toEqual("2023-01-15"); + }); + + test("getAndReturnUUID", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"; + const rawResponseBody = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"; + server + .mockEndpoint() + .post("/primitive/uuid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + expect(response).toEqual("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + }); + + test("getAndReturnBase64", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "SGVsbG8gd29ybGQh"; + const rawResponseBody = "SGVsbG8gd29ybGQh"; + server + .mockEndpoint() + .post("/primitive/base64") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh"); + expect(response).toEqual("SGVsbG8gd29ybGQh"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/put.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/put.test.ts new file mode 100644 index 000000000000..1481521d77b5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/put.test.ts @@ -0,0 +1,39 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("PutClient", () => { + test("add", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + errors: [ + { category: "API_ERROR", code: "INTERNAL_SERVER_ERROR", detail: "detail", field: "field" }, + { category: "API_ERROR", code: "INTERNAL_SERVER_ERROR", detail: "detail", field: "field" }, + ], + }; + server.mockEndpoint().put("/id").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.put.add({ + id: "id", + }); + expect(response).toEqual({ + errors: [ + { + category: "API_ERROR", + code: "INTERNAL_SERVER_ERROR", + detail: "detail", + field: "field", + }, + { + category: "API_ERROR", + code: "INTERNAL_SERVER_ERROR", + detail: "detail", + field: "field", + }, + ], + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/union.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/union.test.ts new file mode 100644 index 000000000000..35b1405e89ca --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/union.test.ts @@ -0,0 +1,32 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("UnionClient", () => { + test("getAndReturnUnion", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { animal: "dog", name: "name", likesToWoof: true }; + const rawResponseBody = { animal: "dog", name: "name", likesToWoof: true }; + server + .mockEndpoint() + .post("/union") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.union.getAndReturnUnion({ + animal: "dog", + name: "name", + likesToWoof: true, + }); + expect(response).toEqual({ + animal: "dog", + name: "name", + likesToWoof: true, + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/urls.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/urls.test.ts new file mode 100644 index 000000000000..b450fbeea217 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/endpoints/urls.test.ts @@ -0,0 +1,68 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("UrlsClient", () => { + test("withMixedCase", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/urls/MixedCase").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.urls.withMixedCase(); + expect(response).toEqual("string"); + }); + + test("noEndingSlash", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/no-ending-slash") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.noEndingSlash(); + expect(response).toEqual("string"); + }); + + test("withEndingSlash", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/with-ending-slash/") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.withEndingSlash(); + expect(response).toEqual("string"); + }); + + test("withUnderscores", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/with_underscores") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.withUnderscores(); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/inlinedRequests.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/inlinedRequests.test.ts new file mode 100644 index 000000000000..61b5a64bce8f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/inlinedRequests.test.ts @@ -0,0 +1,150 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as SeedExhaustive from "../../src/api/index"; +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("InlinedRequestsClient", () => { + test("postWithObjectBodyandResponse (1)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/req-bodies/object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("postWithObjectBodyandResponse (2)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { message: "message" }; + server + .mockEndpoint() + .post("/req-bodies/object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }).rejects.toThrow(SeedExhaustive.BadRequestBody); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noAuth.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noAuth.test.ts new file mode 100644 index 000000000000..3317cf1a69d1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noAuth.test.ts @@ -0,0 +1,48 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as SeedExhaustive from "../../src/api/index"; +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("NoAuthClient", () => { + test("postWithNoAuth (1)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { key: "value" }; + const rawResponseBody = true; + server + .mockEndpoint() + .post("/no-auth") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.noAuth.postWithNoAuth({ + key: "value", + }); + expect(response).toEqual(true); + }); + + test("postWithNoAuth (2)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { key: "value" }; + const rawResponseBody = { message: "message" }; + server + .mockEndpoint() + .post("/no-auth") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.noAuth.postWithNoAuth({ + key: "value", + }); + }).rejects.toThrow(SeedExhaustive.BadRequestBody); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noReqBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noReqBody.test.ts new file mode 100644 index 000000000000..8f5f6a59a512 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/noReqBody.test.ts @@ -0,0 +1,58 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("NoReqBodyClient", () => { + test("getWithNoRequestBody", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server.mockEndpoint().get("/no-req-body").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.noReqBody.getWithNoRequestBody(); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("postWithNoRequestBody", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().post("/no-req-body").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.noReqBody.postWithNoRequestBody(); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/reqWithHeaders.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/reqWithHeaders.test.ts new file mode 100644 index 000000000000..a128d4844ba2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tests/wire/reqWithHeaders.test.ts @@ -0,0 +1,29 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("ReqWithHeadersClient", () => { + test("getWithCustomHeader", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + + server + .mockEndpoint() + .post("/test-headers/custom-header") + .header("X-TEST-SERVICE-HEADER", "X-TEST-SERVICE-HEADER") + .header("X-TEST-ENDPOINT-HEADER", "X-TEST-ENDPOINT-HEADER") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .build(); + + const response = await client.reqWithHeaders.getWithCustomHeader({ + xTestServiceHeader: "X-TEST-SERVICE-HEADER", + xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + body: "string", + }); + expect(response).toEqual(undefined); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.base.json b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.base.json new file mode 100644 index 000000000000..d7627675de20 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.base.json @@ -0,0 +1,18 @@ +{ + "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/exhaustive/serde-layer-zod/tsconfig.cjs.json b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.cjs.json new file mode 100644 index 000000000000..5c11446f5984 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs" + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.esm.json b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.esm.json new file mode 100644 index 000000000000..6ce909748b2c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.esm.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm", + "verbatimModuleSyntax": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.json b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.json new file mode 100644 index 000000000000..d77fdf00d259 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.cjs.json" +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zod/vitest.config.mts b/seed/ts-sdk/exhaustive/serde-layer-zod/vitest.config.mts new file mode 100644 index 000000000000..ba2ec4f9d45a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zod/vitest.config.mts @@ -0,0 +1,28 @@ +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/exhaustive/serde-layer-zurg/.fern/metadata.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/.fern/metadata.json new file mode 100644 index 000000000000..bc0673c609b5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/.fern/metadata.json @@ -0,0 +1,8 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-typescript-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "serializationFormat": "zurg" + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/.github/workflows/ci.yml b/seed/ts-sdk/exhaustive/serde-layer-zurg/.github/workflows/ci.yml new file mode 100644 index 000000000000..836106996595 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +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/exhaustive/serde-layer-zurg/.gitignore b/seed/ts-sdk/exhaustive/serde-layer-zurg/.gitignore new file mode 100644 index 000000000000..72271e049c02 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +/dist \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/CONTRIBUTING.md b/seed/ts-sdk/exhaustive/serde-layer-zurg/CONTRIBUTING.md new file mode 100644 index 000000000000..fe5bc2f77e0b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/CONTRIBUTING.md @@ -0,0 +1,133 @@ +# 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/exhaustive/serde-layer-zurg/README.md b/seed/ts-sdk/exhaustive/serde-layer-zurg/README.md new file mode 100644 index 000000000000..140b1fb505f3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/README.md @@ -0,0 +1,241 @@ +# 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/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) + +The Seed TypeScript library provides convenient access to the Seed APIs from TypeScript. + +## Installation + +```sh +npm i -s @fern/exhaustive +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ environment: "YOUR_BASE_URL", token: "YOUR_TOKEN" }); +await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); +``` + +## Request And Response Types + +The SDK exports all request and response types as TypeScript interfaces. Simply import them with the +following namespace: + +```typescript +import { SeedExhaustive } from "@fern/exhaustive"; + +const request: SeedExhaustive.GetWithInlinePath = { + ... +}; +``` + +## 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 { SeedExhaustiveError } from "@fern/exhaustive"; + +try { + await client.endpoints.container.getAndReturnListOfPrimitives(...); +} catch (err) { + if (err instanceof SeedExhaustiveError) { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(..., { + 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.endpoints.container.getAndReturnListOfPrimitives(...).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 { SeedExhaustiveClient, logging } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + ... + 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 { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + ... + 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/exhaustive/serde-layer-zurg/biome.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/biome.json new file mode 100644 index 000000000000..a777468e4ae2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/biome.json @@ -0,0 +1,74 @@ +{ + "$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/exhaustive/serde-layer-zurg/package.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/package.json new file mode 100644 index 000000000000..ac3de0f37468 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/package.json @@ -0,0 +1,78 @@ +{ + "name": "@fern/exhaustive", + "version": "0.0.1", + "private": false, + "repository": "github:exhaustive/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" + }, + "./serialization": { + "types": "./dist/cjs/serialization/index.d.ts", + "import": { + "types": "./dist/esm/serialization/index.d.mts", + "default": "./dist/esm/serialization/index.mjs" + }, + "require": { + "types": "./dist/cjs/serialization/index.d.ts", + "default": "./dist/cjs/serialization/index.js" + }, + "default": "./dist/cjs/serialization/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/exhaustive/serde-layer-zurg/pnpm-workspace.yaml b/seed/ts-sdk/exhaustive/serde-layer-zurg/pnpm-workspace.yaml new file mode 100644 index 000000000000..6e4c395107df --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/pnpm-workspace.yaml @@ -0,0 +1 @@ +packages: ['.'] \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/reference.md b/seed/ts-sdk/exhaustive/serde-layer-zurg/reference.md new file mode 100644 index 000000000000..b571afcab9fc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/reference.md @@ -0,0 +1,2761 @@ +# Reference +## Endpoints Container +
client.endpoints.container.getAndReturnListOfPrimitives({ ...params }) -> string[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnListOfObjects({ ...params }) -> SeedExhaustive.ObjectWithRequiredField[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnListOfObjects([{ + string: "string" + }, { + string: "string" + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnSetOfPrimitives({ ...params }) -> Set +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Set` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnSetOfObjects({ ...params }) -> SeedExhaustive.ObjectWithRequiredField[] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnSetOfObjects([{ + string: "string" + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnMapPrimToPrim({ ...params }) -> Record +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnMapPrimToPrim({ + "string": "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Record` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnMapOfPrimToObject({ ...params }) -> Record +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnMapOfPrimToObject({ + "string": { + string: "string" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Record` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.container.getAndReturnOptional({ ...params }) -> SeedExhaustive.ObjectWithRequiredField | undefined +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.container.getAndReturnOptional({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ContainerClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints ContentType +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentTypeClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentTypeClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Enum +
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.enum.getAndReturnEnum("SUNNY"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.WeatherReport` + +
+
+ +
+
+ +**requestOptions:** `EnumClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints HttpMethods +
client.endpoints.httpMethods.testGet(id) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testGet("id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPost({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPost({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPut(id, { ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPut("id", { + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testPatch(id, { ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testPatch("id", { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.httpMethods.testDelete(id) -> boolean +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.httpMethods.testDelete("id"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` + +
+
+ +
+
+ +**requestOptions:** `HttpMethodsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Object +
client.endpoints.object.getAndReturnWithOptionalField({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithOptionalField({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnWithRequiredField({ ...params }) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithRequiredField({ + string: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnWithMapOfMap({ ...params }) -> SeedExhaustive.ObjectWithMapOfMap +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnWithMapOfMap({ + map: { + "map": { + "map": "map" + } + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithMapOfMap` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithOptionalField({ ...params }) -> SeedExhaustive.NestedObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithOptionalField({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithRequiredField(string, { ...params }) -> SeedExhaustive.NestedObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**string:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithRequiredField` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList({ ...params }) -> SeedExhaustive.NestedObjectWithRequiredField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } + }, { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } + }]); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.NestedObjectWithRequiredField[]` + +
+
+ +
+
+ +**requestOptions:** `ObjectClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Params +
client.endpoints.params.getWithPath(param) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithPath("param"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithInlinePath({ ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithInlinePath({ + param: "param" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithInlinePath` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with query param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithQuery({ + query: "query", + number: 1 +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithAllowMultipleQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with multiple of same query param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithAllowMultipleQuery({ + query: "query", + number: 1 +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithMultipleQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithPathAndQuery(param, { ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path and query params +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithPathAndQuery("param", { + query: "query" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithPathAndQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.getWithInlinePathAndQuery({ ...params }) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +GET with path and query params +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.getWithInlinePathAndQuery({ + param: "param", + query: "query" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.GetWithInlinePathAndQuery` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.modifyWithPath(param, { ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +PUT to update with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.modifyWithPath("param", "string"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.params.modifyWithInlinePath({ ...params }) -> string +
+
+ +#### 📝 Description + +
+
+ +
+
+ +PUT to update with path param +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.modifyWithInlinePath({ + param: "param", + body: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.ModifyResourceAtInlinedPath` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Primitive +
client.endpoints.primitive.getAndReturnString({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnString("string"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnInt({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnInt(1); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnLong({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnLong(1000000); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDouble({ ...params }) -> number +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDouble(1.1); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `number` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnBool({ ...params }) -> boolean +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnBool(true); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `boolean` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDatetime({ ...params }) -> Date +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Date` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnDate({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnDate("2023-01-15"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnUuid({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.primitive.getAndReturnBase64({ ...params }) -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh"); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `string` + +
+
+ +
+
+ +**requestOptions:** `PrimitiveClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Put +
client.endpoints.put.add({ ...params }) -> SeedExhaustive.PutResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.put.add({ + id: "id" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.endpoints.PutRequest` + +
+
+ +
+
+ +**requestOptions:** `PutClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Union +
client.endpoints.union.getAndReturnUnion({ ...params }) -> SeedExhaustive.Animal +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.union.getAndReturnUnion({ + animal: "dog", + name: "name", + likesToWoof: true +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.Animal` + +
+
+ +
+
+ +**requestOptions:** `UnionClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Endpoints Urls +
client.endpoints.urls.withMixedCase() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withMixedCase(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.noEndingSlash() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.noEndingSlash(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.withEndingSlash() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withEndingSlash(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.endpoints.urls.withUnderscores() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.urls.withUnderscores(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## InlinedRequests +
client.inlinedRequests.postWithObjectBodyandResponse({ ...params }) -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 📝 Description + +
+
+ +
+
+ +POST with custom object in request body, response is an object +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map" + }, + bigint: "1000000" + } +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.PostWithObjectBody` + +
+
+ +
+
+ +**requestOptions:** `InlinedRequestsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NoAuth +
client.noAuth.postWithNoAuth({ ...params }) -> boolean +
+
+ +#### 📝 Description + +
+
+ +
+
+ +POST request with no auth +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noAuth.postWithNoAuth({ + "key": "value" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `unknown` + +
+
+ +
+
+ +**requestOptions:** `NoAuthClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NoReqBody +
client.noReqBody.getWithNoRequestBody() -> SeedExhaustive.ObjectWithOptionalField +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noReqBody.getWithNoRequestBody(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `NoReqBodyClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.noReqBody.postWithNoRequestBody() -> string +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.noReqBody.postWithNoRequestBody(); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `NoReqBodyClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +## ReqWithHeaders +
client.reqWithHeaders.getWithCustomHeader({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.reqWithHeaders.getWithCustomHeader({ + xTestServiceHeader: "X-TEST-SERVICE-HEADER", + xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + body: "string" +}); + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ReqWithHeaders` + +
+
+ +
+
+ +**requestOptions:** `ReqWithHeadersClient.RequestOptions` + +
+
+
+
+ + +
+
+
diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/scripts/rename-to-esm-files.js b/seed/ts-sdk/exhaustive/serde-layer-zurg/scripts/rename-to-esm-files.js new file mode 100644 index 000000000000..dc1df1cbbacb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/scripts/rename-to-esm-files.js @@ -0,0 +1,123 @@ +#!/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/exhaustive/serde-layer-zurg/snippet.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/snippet.json new file mode 100644 index 000000000000..875b76c4bb57 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/snippet.json @@ -0,0 +1,544 @@ +{ + "endpoints": [ + { + "id": { + "path": "/container/list-of-primitives", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnListOfPrimitives" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnListOfPrimitives([\"string\", \"string\"]);\n" + } + }, + { + "id": { + "path": "/container/list-of-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnListOfObjects" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnListOfObjects([{\n string: \"string\"\n }, {\n string: \"string\"\n }]);\n" + } + }, + { + "id": { + "path": "/container/set-of-primitives", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnSetOfPrimitives" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnSetOfPrimitives(new Set([\"string\"]));\n" + } + }, + { + "id": { + "path": "/container/set-of-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnSetOfObjects" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnSetOfObjects([{\n string: \"string\"\n }]);\n" + } + }, + { + "id": { + "path": "/container/map-prim-to-prim", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnMapPrimToPrim" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnMapPrimToPrim({\n \"string\": \"string\"\n});\n" + } + }, + { + "id": { + "path": "/container/map-prim-to-object", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnMapOfPrimToObject" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnMapOfPrimToObject({\n \"string\": {\n string: \"string\"\n }\n});\n" + } + }, + { + "id": { + "path": "/container/opt-objects", + "method": "POST", + "identifier_override": "endpoint_endpoints/container.getAndReturnOptional" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/enum", + "method": "POST", + "identifier_override": "endpoint_endpoints/enum.getAndReturnEnum" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.enum.getAndReturnEnum(\"SUNNY\");\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "GET", + "identifier_override": "endpoint_endpoints/http-methods.testGet" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testGet(\"id\");\n" + } + }, + { + "id": { + "path": "/http-methods", + "method": "POST", + "identifier_override": "endpoint_endpoints/http-methods.testPost" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPost({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/http-methods.testPut" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPut(\"id\", {\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "PATCH", + "identifier_override": "endpoint_endpoints/http-methods.testPatch" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testPatch(\"id\", {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/http-methods/{id}", + "method": "DELETE", + "identifier_override": "endpoint_endpoints/http-methods.testDelete" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.httpMethods.testDelete(\"id\");\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-optional-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithOptionalField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithOptionalField({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-required-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithRequiredField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithRequiredField({\n string: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-with-map-of-map", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnWithMapOfMap" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnWithMapOfMap({\n map: {\n \"map\": {\n \"map\": \"map\"\n }\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-optional-field", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithOptionalField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithOptionalField({\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-required-field/{string}", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithRequiredField" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithRequiredField(\"string\", {\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/object/get-and-return-nested-with-required-field-list", + "method": "POST", + "identifier_override": "endpoint_endpoints/object.getAndReturnNestedWithRequiredFieldAsList" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n }, {\n string: \"string\",\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n }]);\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithPath(\"param\");\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithInlinePath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithInlinePath({\n param: \"param\"\n});\n" + } + }, + { + "id": { + "path": "/params", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithQuery({\n query: \"query\",\n number: 1\n});\n" + } + }, + { + "id": { + "path": "/params", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithAllowMultipleQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithAllowMultipleQuery({\n query: \"query\",\n number: 1\n});\n" + } + }, + { + "id": { + "path": "/params/path-query/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithPathAndQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithPathAndQuery(\"param\", {\n query: \"query\"\n});\n" + } + }, + { + "id": { + "path": "/params/path-query/{param}", + "method": "GET", + "identifier_override": "endpoint_endpoints/params.getWithInlinePathAndQuery" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.getWithInlinePathAndQuery({\n param: \"param\",\n query: \"query\"\n});\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/params.modifyWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithPath(\"param\", \"string\");\n" + } + }, + { + "id": { + "path": "/params/path/{param}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/params.modifyWithInlinePath" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" + } + }, + { + "id": { + "path": "/primitive/string", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnString" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnString(\"string\");\n" + } + }, + { + "id": { + "path": "/primitive/integer", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnInt" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnInt(1);\n" + } + }, + { + "id": { + "path": "/primitive/long", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnLong" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnLong(1000000);\n" + } + }, + { + "id": { + "path": "/primitive/double", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDouble" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDouble(1.1);\n" + } + }, + { + "id": { + "path": "/primitive/boolean", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnBool" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnBool(true);\n" + } + }, + { + "id": { + "path": "/primitive/datetime", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDatetime" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDatetime(new Date(\"2024-01-15T09:30:00.000Z\"));\n" + } + }, + { + "id": { + "path": "/primitive/date", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnDate" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnDate(\"2023-01-15\");\n" + } + }, + { + "id": { + "path": "/primitive/uuid", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnUUID" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnUuid(\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\");\n" + } + }, + { + "id": { + "path": "/primitive/base64", + "method": "POST", + "identifier_override": "endpoint_endpoints/primitive.getAndReturnBase64" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.primitive.getAndReturnBase64(\"SGVsbG8gd29ybGQh\");\n" + } + }, + { + "id": { + "path": "/{id}", + "method": "PUT", + "identifier_override": "endpoint_endpoints/put.add" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.put.add({\n id: \"id\"\n});\n" + } + }, + { + "id": { + "path": "/union", + "method": "POST", + "identifier_override": "endpoint_endpoints/union.getAndReturnUnion" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.union.getAndReturnUnion({\n animal: \"dog\",\n name: \"name\",\n likesToWoof: true\n});\n" + } + }, + { + "id": { + "path": "/urls/MixedCase", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withMixedCase" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withMixedCase();\n" + } + }, + { + "id": { + "path": "/urls/no-ending-slash", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.noEndingSlash" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.noEndingSlash();\n" + } + }, + { + "id": { + "path": "/urls/with-ending-slash/", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withEndingSlash" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withEndingSlash();\n" + } + }, + { + "id": { + "path": "/urls/with_underscores", + "method": "GET", + "identifier_override": "endpoint_endpoints/urls.withUnderscores" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.urls.withUnderscores();\n" + } + }, + { + "id": { + "path": "/req-bodies/object", + "method": "POST", + "identifier_override": "endpoint_inlined-requests.postWithObjectBodyandResponse" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.inlinedRequests.postWithObjectBodyandResponse({\n string: \"string\",\n integer: 1,\n nestedObject: {\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: new Date(\"2024-01-15T09:30:00.000Z\"),\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n }\n});\n" + } + }, + { + "id": { + "path": "/no-auth", + "method": "POST", + "identifier_override": "endpoint_no-auth.postWithNoAuth" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noAuth.postWithNoAuth({\n \"key\": \"value\"\n});\n" + } + }, + { + "id": { + "path": "/no-req-body", + "method": "GET", + "identifier_override": "endpoint_no-req-body.getWithNoRequestBody" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noReqBody.getWithNoRequestBody();\n" + } + }, + { + "id": { + "path": "/no-req-body", + "method": "POST", + "identifier_override": "endpoint_no-req-body.postWithNoRequestBody" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.noReqBody.postWithNoRequestBody();\n" + } + }, + { + "id": { + "path": "/test-headers/custom-header", + "method": "POST", + "identifier_override": "endpoint_req-with-headers.getWithCustomHeader" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.reqWithHeaders.getWithCustomHeader({\n xTestServiceHeader: \"X-TEST-SERVICE-HEADER\",\n xTestEndpointHeader: \"X-TEST-ENDPOINT-HEADER\",\n body: \"string\"\n});\n" + } + } + ], + "types": {} +} \ No newline at end of file diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/BaseClient.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/BaseClient.ts new file mode 100644 index 000000000000..3ab4e838f800 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/BaseClient.ts @@ -0,0 +1,81 @@ +// This file was auto-generated by Fern from our API Definition. + +import { BearerAuthProvider } from "./auth/BearerAuthProvider.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; +} & BearerAuthProvider.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/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/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 ??= new BearerAuthProvider(normalizedWithNoOpAuthProvider); + return normalized; +} + +function withNoOpAuthProvider( + options: NormalizedClientOptions, +): NormalizedClientOptionsWithAuth { + return { + ...options, + authProvider: new core.NoOpAuthProvider(), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/Client.ts new file mode 100644 index 000000000000..370640f5862d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/Client.ts @@ -0,0 +1,48 @@ +// This file was auto-generated by Fern from our API Definition. + +import { EndpointsClient } from "./api/resources/endpoints/client/Client.js"; +import { InlinedRequestsClient } from "./api/resources/inlinedRequests/client/Client.js"; +import { NoAuthClient } from "./api/resources/noAuth/client/Client.js"; +import { NoReqBodyClient } from "./api/resources/noReqBody/client/Client.js"; +import { ReqWithHeadersClient } from "./api/resources/reqWithHeaders/client/Client.js"; +import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "./BaseClient.js"; + +export declare namespace SeedExhaustiveClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class SeedExhaustiveClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _endpoints: EndpointsClient | undefined; + protected _inlinedRequests: InlinedRequestsClient | undefined; + protected _noAuth: NoAuthClient | undefined; + protected _noReqBody: NoReqBodyClient | undefined; + protected _reqWithHeaders: ReqWithHeadersClient | undefined; + + constructor(options: SeedExhaustiveClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + public get endpoints(): EndpointsClient { + return (this._endpoints ??= new EndpointsClient(this._options)); + } + + public get inlinedRequests(): InlinedRequestsClient { + return (this._inlinedRequests ??= new InlinedRequestsClient(this._options)); + } + + public get noAuth(): NoAuthClient { + return (this._noAuth ??= new NoAuthClient(this._options)); + } + + public get noReqBody(): NoReqBodyClient { + return (this._noReqBody ??= new NoReqBodyClient(this._options)); + } + + public get reqWithHeaders(): ReqWithHeadersClient { + return (this._reqWithHeaders ??= new ReqWithHeadersClient(this._options)); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/Client.ts new file mode 100644 index 000000000000..78dcad40a3fa --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/Client.ts @@ -0,0 +1,76 @@ +// 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 { ContainerClient } from "../resources/container/client/Client.js"; +import { ContentTypeClient } from "../resources/contentType/client/Client.js"; +import { EnumClient } from "../resources/enum/client/Client.js"; +import { HttpMethodsClient } from "../resources/httpMethods/client/Client.js"; +import { ObjectClient } from "../resources/object/client/Client.js"; +import { ParamsClient } from "../resources/params/client/Client.js"; +import { PrimitiveClient } from "../resources/primitive/client/Client.js"; +import { PutClient } from "../resources/put/client/Client.js"; +import { UnionClient } from "../resources/union/client/Client.js"; +import { UrlsClient } from "../resources/urls/client/Client.js"; + +export declare namespace EndpointsClient { + export type Options = BaseClientOptions; +} + +export class EndpointsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _container: ContainerClient | undefined; + protected _contentType: ContentTypeClient | undefined; + protected _enum: EnumClient | undefined; + protected _httpMethods: HttpMethodsClient | undefined; + protected _object: ObjectClient | undefined; + protected _params: ParamsClient | undefined; + protected _primitive: PrimitiveClient | undefined; + protected _put: PutClient | undefined; + protected _union: UnionClient | undefined; + protected _urls: UrlsClient | undefined; + + constructor(options: EndpointsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + public get container(): ContainerClient { + return (this._container ??= new ContainerClient(this._options)); + } + + public get contentType(): ContentTypeClient { + return (this._contentType ??= new ContentTypeClient(this._options)); + } + + public get enum(): EnumClient { + return (this._enum ??= new EnumClient(this._options)); + } + + public get httpMethods(): HttpMethodsClient { + return (this._httpMethods ??= new HttpMethodsClient(this._options)); + } + + public get object(): ObjectClient { + return (this._object ??= new ObjectClient(this._options)); + } + + public get params(): ParamsClient { + return (this._params ??= new ParamsClient(this._options)); + } + + public get primitive(): PrimitiveClient { + return (this._primitive ??= new PrimitiveClient(this._options)); + } + + public get put(): PutClient { + return (this._put ??= new PutClient(this._options)); + } + + public get union(): UnionClient { + return (this._union ??= new UnionClient(this._options)); + } + + public get urls(): UrlsClient { + return (this._urls ??= new UrlsClient(this._options)); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/index.ts new file mode 100644 index 000000000000..9eb1192dcc32 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/Client.ts new file mode 100644 index 000000000000..6046c13355f5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/Client.ts @@ -0,0 +1,543 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ContainerClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ContainerClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ContainerClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]) + */ + public getAndReturnListOfPrimitives( + request: string[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnListOfPrimitives(request, requestOptions)); + } + + private async __getAndReturnListOfPrimitives( + request: string[], + requestOptions?: ContainerClient.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)), + "/container/list-of-primitives", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnListOfPrimitives.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnListOfPrimitives.Response.parseOrThrow( + _response.body, + { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/container/list-of-primitives", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnListOfObjects([{ + * string: "string" + * }, { + * string: "string" + * }]) + */ + public getAndReturnListOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnListOfObjects(request, requestOptions)); + } + + private async __getAndReturnListOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.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)), + "/container/list-of-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnListOfObjects.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnListOfObjects.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/list-of-objects"); + } + + /** + * @param {Set} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])) + */ + public getAndReturnSetOfPrimitives( + request: Set, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnSetOfPrimitives(request, requestOptions)); + } + + private async __getAndReturnSetOfPrimitives( + request: Set, + requestOptions?: ContainerClient.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)), + "/container/set-of-primitives", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnSetOfPrimitives.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnSetOfPrimitives.Response.parseOrThrow( + _response.body, + { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/set-of-primitives"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField[]} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnSetOfObjects([{ + * string: "string" + * }]) + */ + public getAndReturnSetOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnSetOfObjects(request, requestOptions)); + } + + private async __getAndReturnSetOfObjects( + request: SeedExhaustive.types.ObjectWithRequiredField[], + requestOptions?: ContainerClient.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)), + "/container/set-of-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnSetOfObjects.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnSetOfObjects.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/set-of-objects"); + } + + /** + * @param {Record} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnMapPrimToPrim({ + * "string": "string" + * }) + */ + public getAndReturnMapPrimToPrim( + request: Record, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnMapPrimToPrim(request, requestOptions)); + } + + private async __getAndReturnMapPrimToPrim( + request: Record, + requestOptions?: ContainerClient.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)), + "/container/map-prim-to-prim", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnMapPrimToPrim.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnMapPrimToPrim.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/map-prim-to-prim"); + } + + /** + * @param {Record} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnMapOfPrimToObject({ + * "string": { + * string: "string" + * } + * }) + */ + public getAndReturnMapOfPrimToObject( + request: Record, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnMapOfPrimToObject(request, requestOptions)); + } + + private async __getAndReturnMapOfPrimToObject( + request: Record, + requestOptions?: ContainerClient.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)), + "/container/map-prim-to-object", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.container.getAndReturnMapOfPrimToObject.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.container.getAndReturnMapOfPrimToObject.Response.parseOrThrow( + _response.body, + { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }, + ), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/container/map-prim-to-object", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {ContainerClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.container.getAndReturnOptional({ + * string: "string" + * }) + */ + public getAndReturnOptional( + request?: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ContainerClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnOptional(request, requestOptions)); + } + + private async __getAndReturnOptional( + request?: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ContainerClient.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)), + "/container/opt-objects", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: + request != null + ? serializers.endpoints.container.getAndReturnOptional.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }) + : undefined, + 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: serializers.endpoints.container.getAndReturnOptional.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/container/opt-objects"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/container/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 000000000000..0596a90d8006 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,178 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ContentTypeClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ContentTypeClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ContentTypeClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentTypeClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postJsonPatchContentType(request, requestOptions)); + } + + private async __postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.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)), + "/foo/bar", + ), + method: "POST", + headers: _headers, + contentType: "application/json-patch+json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/foo/bar"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentTypeClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__postJsonPatchContentWithCharsetType(request, requestOptions), + ); + } + + private async __postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentTypeClient.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)), + "/foo/baz", + ), + method: "POST", + headers: _headers, + contentType: "application/json-patch+json; charset=utf-8", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/foo/baz"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/Client.ts new file mode 100644 index 000000000000..6e2af2b2daeb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/Client.ts @@ -0,0 +1,93 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace EnumClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class EnumClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: EnumClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.WeatherReport} request + * @param {EnumClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.enum.getAndReturnEnum("SUNNY") + */ + public getAndReturnEnum( + request: SeedExhaustive.types.WeatherReport, + requestOptions?: EnumClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnEnum(request, requestOptions)); + } + + private async __getAndReturnEnum( + request: SeedExhaustive.types.WeatherReport, + requestOptions?: EnumClient.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)), + "/enum", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.WeatherReport.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.WeatherReport.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/enum"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/enum/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/Client.ts new file mode 100644 index 000000000000..c34b46ee48a7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/Client.ts @@ -0,0 +1,380 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace HttpMethodsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class HttpMethodsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: HttpMethodsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string} id + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testGet("id") + */ + public testGet(id: string, requestOptions?: HttpMethodsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testGet(id, requestOptions)); + } + + private async __testGet( + id: string, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + 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: serializers.endpoints.httpMethods.testGet.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/http-methods/{id}"); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPost({ + * string: "string" + * }) + */ + public testPost( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPost(request, requestOptions)); + } + + private async __testPost( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.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)), + "/http-methods", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/http-methods"); + } + + /** + * @param {string} id + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPut("id", { + * string: "string" + * }) + */ + public testPut( + id: string, + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPut(id, request, requestOptions)); + } + + private async __testPut( + id: string, + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/http-methods/{id}"); + } + + /** + * @param {string} id + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testPatch("id", { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public testPatch( + id: string, + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testPatch(id, request, requestOptions)); + } + + private async __testPatch( + id: string, + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PATCH", "/http-methods/{id}"); + } + + /** + * @param {string} id + * @param {HttpMethodsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.httpMethods.testDelete("id") + */ + public testDelete( + id: string, + requestOptions?: HttpMethodsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__testDelete(id, requestOptions)); + } + + private async __testDelete( + id: string, + requestOptions?: HttpMethodsClient.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)), + `/http-methods/${core.url.encodePathParam(id)}`, + ), + method: "DELETE", + 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: serializers.endpoints.httpMethods.testDelete.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/http-methods/{id}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/httpMethods/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/index.ts new file mode 100644 index 000000000000..6046843b5e42 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/index.ts @@ -0,0 +1,13 @@ +export * as container from "./container/index.js"; +export * as contentType from "./contentType/index.js"; +export * as enum_ from "./enum/index.js"; +export * as httpMethods from "./httpMethods/index.js"; +export * as object from "./object/index.js"; +export * from "./params/client/requests/index.js"; +export * as params from "./params/index.js"; +export * as primitive from "./primitive/index.js"; +export * from "./put/client/requests/index.js"; +export * as put from "./put/index.js"; +export * from "./put/types/index.js"; +export * as union from "./union/index.js"; +export * as urls from "./urls/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/Client.ts new file mode 100644 index 000000000000..261e067c7512 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/Client.ts @@ -0,0 +1,577 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ObjectClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ObjectClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ObjectClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithOptionalField({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public getAndReturnWithOptionalField( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithOptionalField(request, requestOptions)); + } + + private async __getAndReturnWithOptionalField( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-optional-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-optional-field", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithRequiredField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithRequiredField({ + * string: "string" + * }) + */ + public getAndReturnWithRequiredField( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithRequiredField(request, requestOptions)); + } + + private async __getAndReturnWithRequiredField( + request: SeedExhaustive.types.ObjectWithRequiredField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-required-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithRequiredField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithRequiredField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-required-field", + ); + } + + /** + * @param {SeedExhaustive.types.ObjectWithMapOfMap} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnWithMapOfMap({ + * map: { + * "map": { + * "map": "map" + * } + * } + * }) + */ + public getAndReturnWithMapOfMap( + request: SeedExhaustive.types.ObjectWithMapOfMap, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnWithMapOfMap(request, requestOptions)); + } + + private async __getAndReturnWithMapOfMap( + request: SeedExhaustive.types.ObjectWithMapOfMap, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-with-map-of-map", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.ObjectWithMapOfMap.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithMapOfMap.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-with-map-of-map", + ); + } + + /** + * @param {SeedExhaustive.types.NestedObjectWithOptionalField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithOptionalField({ + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public getAndReturnNestedWithOptionalField( + request: SeedExhaustive.types.NestedObjectWithOptionalField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithOptionalField(request, requestOptions), + ); + } + + private async __getAndReturnNestedWithOptionalField( + request: SeedExhaustive.types.NestedObjectWithOptionalField, + requestOptions?: ObjectClient.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)), + "/object/get-and-return-nested-with-optional-field", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.NestedObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.NestedObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-optional-field", + ); + } + + /** + * @param {string} string + * @param {SeedExhaustive.types.NestedObjectWithRequiredField} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public getAndReturnNestedWithRequiredField( + string: string, + request: SeedExhaustive.types.NestedObjectWithRequiredField, + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithRequiredField(string, request, requestOptions), + ); + } + + private async __getAndReturnNestedWithRequiredField( + string: string, + request: SeedExhaustive.types.NestedObjectWithRequiredField, + requestOptions?: ObjectClient.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)), + `/object/get-and-return-nested-with-required-field/${core.url.encodePathParam(string)}`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.NestedObjectWithRequiredField.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.NestedObjectWithRequiredField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-required-field/{string}", + ); + } + + /** + * @param {SeedExhaustive.types.NestedObjectWithRequiredField[]} request + * @param {ObjectClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([{ + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }, { + * string: "string", + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }]) + */ + public getAndReturnNestedWithRequiredFieldAsList( + request: SeedExhaustive.types.NestedObjectWithRequiredField[], + requestOptions?: ObjectClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise( + this.__getAndReturnNestedWithRequiredFieldAsList(request, requestOptions), + ); + } + + private async __getAndReturnNestedWithRequiredFieldAsList( + request: SeedExhaustive.types.NestedObjectWithRequiredField[], + requestOptions?: ObjectClient.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)), + "/object/get-and-return-nested-with-required-field-list", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.object.getAndReturnNestedWithRequiredFieldAsList.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.NestedObjectWithRequiredField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/object/get-and-return-nested-with-required-field-list", + ); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/Client.ts new file mode 100644 index 000000000000..36bf861df2d2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/Client.ts @@ -0,0 +1,565 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace ParamsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ParamsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ParamsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * GET with path param + * + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithPath("param") + */ + public getWithPath(param: string, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithPath(param, requestOptions)); + } + + private async __getWithPath( + param: string, + requestOptions?: ParamsClient.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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + 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: serializers.endpoints.params.getWithPath.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path/{param}"); + } + + /** + * GET with path param + * + * @param {SeedExhaustive.endpoints.GetWithInlinePath} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithInlinePath({ + * param: "param" + * }) + */ + public getWithInlinePath( + request: SeedExhaustive.endpoints.GetWithInlinePath, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithInlinePath(request, requestOptions)); + } + + private async __getWithInlinePath( + request: SeedExhaustive.endpoints.GetWithInlinePath, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param } = request; + 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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + 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: serializers.endpoints.params.getWithInlinePath.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path/{param}"); + } + + /** + * GET with query param + * + * @param {SeedExhaustive.endpoints.GetWithQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithQuery({ + * query: "query", + * number: 1 + * }) + */ + public getWithQuery( + request: SeedExhaustive.endpoints.GetWithQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithQuery(request, requestOptions)); + } + + private async __getWithQuery( + request: SeedExhaustive.endpoints.GetWithQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query, number: number_ } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + _queryParams.number = number_.toString(); + 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)), + "/params", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params"); + } + + /** + * GET with multiple of same query param + * + * @param {SeedExhaustive.endpoints.GetWithMultipleQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithAllowMultipleQuery({ + * query: "query", + * number: 1 + * }) + */ + public getWithAllowMultipleQuery( + request: SeedExhaustive.endpoints.GetWithMultipleQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithAllowMultipleQuery(request, requestOptions)); + } + + private async __getWithAllowMultipleQuery( + request: SeedExhaustive.endpoints.GetWithMultipleQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query, number: number_ } = request; + const _queryParams: Record = {}; + if (Array.isArray(query)) { + _queryParams.query = query.map((item) => item); + } else { + _queryParams.query = query; + } + + if (Array.isArray(number_)) { + _queryParams.number = number_.map((item) => item.toString()); + } else { + _queryParams.number = number_.toString(); + } + + 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)), + "/params", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params"); + } + + /** + * GET with path and query params + * + * @param {string} param + * @param {SeedExhaustive.endpoints.GetWithPathAndQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithPathAndQuery("param", { + * query: "query" + * }) + */ + public getWithPathAndQuery( + param: string, + request: SeedExhaustive.endpoints.GetWithPathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithPathAndQuery(param, request, requestOptions)); + } + + private async __getWithPathAndQuery( + param: string, + request: SeedExhaustive.endpoints.GetWithPathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { query } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + 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)), + `/params/path-query/${core.url.encodePathParam(param)}`, + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path-query/{param}"); + } + + /** + * GET with path and query params + * + * @param {SeedExhaustive.endpoints.GetWithInlinePathAndQuery} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.getWithInlinePathAndQuery({ + * param: "param", + * query: "query" + * }) + */ + public getWithInlinePathAndQuery( + request: SeedExhaustive.endpoints.GetWithInlinePathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithInlinePathAndQuery(request, requestOptions)); + } + + private async __getWithInlinePathAndQuery( + request: SeedExhaustive.endpoints.GetWithInlinePathAndQuery, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param, query } = request; + const _queryParams: Record = {}; + _queryParams.query = query; + 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)), + `/params/path-query/${core.url.encodePathParam(param)}`, + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/params/path-query/{param}"); + } + + /** + * PUT to update with path param + * + * @param {string} param + * @param {string} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.modifyWithPath("param", "string") + */ + public modifyWithPath( + param: string, + request: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__modifyWithPath(param, request, requestOptions)); + } + + private async __modifyWithPath( + param: string, + request: string, + requestOptions?: ParamsClient.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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.params.modifyWithPath.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.params.modifyWithPath.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); + } + + /** + * PUT to update with path param + * + * @param {SeedExhaustive.endpoints.ModifyResourceAtInlinedPath} request + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.params.modifyWithInlinePath({ + * param: "param", + * body: "string" + * }) + */ + public modifyWithInlinePath( + request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__modifyWithInlinePath(request, requestOptions)); + } + + private async __modifyWithInlinePath( + request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const { param, body: _body } = request; + 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)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.params.modifyWithInlinePath.Request.jsonOrThrow(_body, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.params.modifyWithInlinePath.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts new file mode 100644 index 000000000000..473165eb1638 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePath.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param" + * } + */ +export interface GetWithInlinePath { + param: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts new file mode 100644 index 000000000000..9c674a852dce --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithInlinePathAndQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param", + * query: "query" + * } + */ +export interface GetWithInlinePathAndQuery { + param: string; + query: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts new file mode 100644 index 000000000000..baca4fafbe62 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithMultipleQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query", + * number: 1 + * } + */ +export interface GetWithMultipleQuery { + query: string | string[]; + number: number | number[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts new file mode 100644 index 000000000000..c3f0018a7874 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithPathAndQuery.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query" + * } + */ +export interface GetWithPathAndQuery { + query: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts new file mode 100644 index 000000000000..7ad48dee4508 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/GetWithQuery.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * query: "query", + * number: 1 + * } + */ +export interface GetWithQuery { + query: string; + number: number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts new file mode 100644 index 000000000000..f3f91a06e3f8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/ModifyResourceAtInlinedPath.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * param: "param", + * body: "string" + * } + */ +export interface ModifyResourceAtInlinedPath { + param: string; + body: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/index.ts new file mode 100644 index 000000000000..b059d3702a32 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/client/requests/index.ts @@ -0,0 +1,6 @@ +export type { GetWithInlinePath } from "./GetWithInlinePath.js"; +export type { GetWithInlinePathAndQuery } from "./GetWithInlinePathAndQuery.js"; +export type { GetWithMultipleQuery } from "./GetWithMultipleQuery.js"; +export type { GetWithPathAndQuery } from "./GetWithPathAndQuery.js"; +export type { GetWithQuery } from "./GetWithQuery.js"; +export type { ModifyResourceAtInlinedPath } from "./ModifyResourceAtInlinedPath.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/params/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/Client.ts new file mode 100644 index 000000000000..87b4fe331e4f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/Client.ts @@ -0,0 +1,644 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; + +export declare namespace PrimitiveClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class PrimitiveClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: PrimitiveClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnString("string") + */ + public getAndReturnString( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnString(request, requestOptions)); + } + + private async __getAndReturnString( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/string", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnString.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnString.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/string"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnInt(1) + */ + public getAndReturnInt( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnInt(request, requestOptions)); + } + + private async __getAndReturnInt( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/integer", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnInt.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnInt.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/integer"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnLong(1000000) + */ + public getAndReturnLong( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnLong(request, requestOptions)); + } + + private async __getAndReturnLong( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/long", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnLong.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnLong.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/long"); + } + + /** + * @param {number} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDouble(1.1) + */ + public getAndReturnDouble( + request: number, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDouble(request, requestOptions)); + } + + private async __getAndReturnDouble( + request: number, + requestOptions?: PrimitiveClient.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)), + "/primitive/double", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDouble.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnDouble.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/double"); + } + + /** + * @param {boolean} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnBool(true) + */ + public getAndReturnBool( + request: boolean, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnBool(request, requestOptions)); + } + + private async __getAndReturnBool( + request: boolean, + requestOptions?: PrimitiveClient.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)), + "/primitive/boolean", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnBool.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnBool.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/boolean"); + } + + /** + * @param {Date} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")) + */ + public getAndReturnDatetime( + request: Date, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDatetime(request, requestOptions)); + } + + private async __getAndReturnDatetime( + request: Date, + requestOptions?: PrimitiveClient.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)), + "/primitive/datetime", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDatetime.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnDatetime.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/datetime"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnDate("2023-01-15") + */ + public getAndReturnDate( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnDate(request, requestOptions)); + } + + private async __getAndReturnDate( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/date", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnDate.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnDate.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/date"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32") + */ + public getAndReturnUuid( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnUuid(request, requestOptions)); + } + + private async __getAndReturnUuid( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/uuid", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnUuid.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnUuid.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/uuid"); + } + + /** + * @param {string} request + * @param {PrimitiveClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh") + */ + public getAndReturnBase64( + request: string, + requestOptions?: PrimitiveClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnBase64(request, requestOptions)); + } + + private async __getAndReturnBase64( + request: string, + requestOptions?: PrimitiveClient.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)), + "/primitive/base64", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.endpoints.primitive.getAndReturnBase64.Request.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.endpoints.primitive.getAndReturnBase64.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/primitive/base64"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/primitive/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/Client.ts new file mode 100644 index 000000000000..c9ffdb8a8cc5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/Client.ts @@ -0,0 +1,90 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace PutClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class PutClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: PutClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.endpoints.PutRequest} request + * @param {PutClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.put.add({ + * id: "id" + * }) + */ + public add( + request: SeedExhaustive.endpoints.PutRequest, + requestOptions?: PutClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__add(request, requestOptions)); + } + + private async __add( + request: SeedExhaustive.endpoints.PutRequest, + requestOptions?: PutClient.RequestOptions, + ): Promise> { + const { id } = request; + 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)), + `${core.url.encodePathParam(id)}`, + ), + method: "PUT", + 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: serializers.endpoints.PutResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/{id}"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts new file mode 100644 index 000000000000..2beba9f087cc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/PutRequest.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * id: "id" + * } + */ +export interface PutRequest { + id: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/index.ts new file mode 100644 index 000000000000..dba0eda4bbee --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/client/requests/index.ts @@ -0,0 +1 @@ +export type { PutRequest } from "./PutRequest.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/index.ts new file mode 100644 index 000000000000..d9adb1af9a93 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts new file mode 100644 index 000000000000..a197fec87887 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCategory.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +export const ErrorCategory = { + ApiError: "API_ERROR", + AuthenticationError: "AUTHENTICATION_ERROR", + InvalidRequestError: "INVALID_REQUEST_ERROR", +} as const; +export type ErrorCategory = (typeof ErrorCategory)[keyof typeof ErrorCategory]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCode.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCode.ts new file mode 100644 index 000000000000..3943226222d8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/ErrorCode.ts @@ -0,0 +1,16 @@ +// This file was auto-generated by Fern from our API Definition. + +export const ErrorCode = { + InternalServerError: "INTERNAL_SERVER_ERROR", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + BadRequest: "BAD_REQUEST", + Conflict: "CONFLICT", + Gone: "GONE", + UnprocessableEntity: "UNPROCESSABLE_ENTITY", + NotImplemented: "NOT_IMPLEMENTED", + BadGateway: "BAD_GATEWAY", + ServiceUnavailable: "SERVICE_UNAVAILABLE", + Unknown: "Unknown", +} as const; +export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/Error_.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/Error_.ts new file mode 100644 index 000000000000..9cf9f495a09a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/Error_.ts @@ -0,0 +1,10 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface Error_ { + category: SeedExhaustive.endpoints.ErrorCategory; + code: SeedExhaustive.endpoints.ErrorCode; + detail?: string; + field?: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/PutResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/PutResponse.ts new file mode 100644 index 000000000000..628b46ba7298 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/PutResponse.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface PutResponse { + errors?: SeedExhaustive.endpoints.Error_[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/index.ts new file mode 100644 index 000000000000..3293f66bb34c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/put/types/index.ts @@ -0,0 +1,4 @@ +export * from "./Error_.js"; +export * from "./ErrorCategory.js"; +export * from "./ErrorCode.js"; +export * from "./PutResponse.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/Client.ts new file mode 100644 index 000000000000..886ba2a0978d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/Client.ts @@ -0,0 +1,97 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export declare namespace UnionClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class UnionClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: UnionClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.types.Animal} request + * @param {UnionClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.union.getAndReturnUnion({ + * animal: "dog", + * name: "name", + * likesToWoof: true + * }) + */ + public getAndReturnUnion( + request: SeedExhaustive.types.Animal, + requestOptions?: UnionClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getAndReturnUnion(request, requestOptions)); + } + + private async __getAndReturnUnion( + request: SeedExhaustive.types.Animal, + requestOptions?: UnionClient.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)), + "/union", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.types.Animal.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.Animal.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/union"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/union/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/Client.ts new file mode 100644 index 000000000000..eee779a5df14 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/Client.ts @@ -0,0 +1,247 @@ +// 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"; +import * as serializers from "../../../../../../serialization/index.js"; + +export declare namespace UrlsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class UrlsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: UrlsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withMixedCase() + */ + public withMixedCase(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withMixedCase(requestOptions)); + } + + private async __withMixedCase(requestOptions?: UrlsClient.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)), + "/urls/MixedCase", + ), + 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: serializers.endpoints.urls.withMixedCase.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/MixedCase"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.noEndingSlash() + */ + public noEndingSlash(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__noEndingSlash(requestOptions)); + } + + private async __noEndingSlash(requestOptions?: UrlsClient.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)), + "/urls/no-ending-slash", + ), + 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: serializers.endpoints.urls.noEndingSlash.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/no-ending-slash"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withEndingSlash() + */ + public withEndingSlash(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withEndingSlash(requestOptions)); + } + + private async __withEndingSlash(requestOptions?: UrlsClient.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)), + "/urls/with-ending-slash/", + ), + 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: serializers.endpoints.urls.withEndingSlash.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/with-ending-slash/"); + } + + /** + * @param {UrlsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.urls.withUnderscores() + */ + public withUnderscores(requestOptions?: UrlsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__withUnderscores(requestOptions)); + } + + private async __withUnderscores(requestOptions?: UrlsClient.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)), + "/urls/with_underscores", + ), + 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: serializers.endpoints.urls.withUnderscores.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/urls/with_underscores"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/endpoints/resources/urls/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/BadRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/BadRequestBody.ts new file mode 100644 index 000000000000..6c0020d2b75a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/BadRequestBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../core/index.js"; +import * as errors from "../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export class BadRequestBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.BadObjectRequestInfo, rawResponse?: core.RawResponse) { + super({ + message: "BadRequestBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/index.ts new file mode 100644 index 000000000000..61778bda5417 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/errors/index.ts @@ -0,0 +1 @@ +export * from "./BadRequestBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts new file mode 100644 index 000000000000..6ad426742488 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/BadObjectRequestInfo.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface BadObjectRequestInfo { + message: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/index.ts new file mode 100644 index 000000000000..b10afa3b7749 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/generalErrors/types/index.ts @@ -0,0 +1 @@ +export * from "./BadObjectRequestInfo.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/index.ts new file mode 100644 index 000000000000..3ca611c9fb54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/index.ts @@ -0,0 +1,11 @@ +export * as endpoints from "./endpoints/index.js"; +export * from "./generalErrors/errors/index.js"; +export * as generalErrors from "./generalErrors/index.js"; +export * from "./generalErrors/types/index.js"; +export * from "./inlinedRequests/client/requests/index.js"; +export * as inlinedRequests from "./inlinedRequests/index.js"; +export * as noAuth from "./noAuth/index.js"; +export * as noReqBody from "./noReqBody/index.js"; +export * from "./reqWithHeaders/client/requests/index.js"; +export * as reqWithHeaders from "./reqWithHeaders/index.js"; +export * as types from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/Client.ts new file mode 100644 index 000000000000..17040d946500 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/Client.ts @@ -0,0 +1,126 @@ +// 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 * as serializers from "../../../../serialization/index.js"; +import * as SeedExhaustive from "../../../index.js"; + +export declare namespace InlinedRequestsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class InlinedRequestsClient { + protected readonly _options: NormalizedClientOptions; + + constructor(options: InlinedRequestsClient.Options) { + this._options = normalizeClientOptions(options); + } + + /** + * POST with custom object in request body, response is an object + * + * @param {SeedExhaustive.PostWithObjectBody} request + * @param {InlinedRequestsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link SeedExhaustive.BadRequestBody} + * + * @example + * await client.inlinedRequests.postWithObjectBodyandResponse({ + * string: "string", + * integer: 1, + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * }) + */ + public postWithObjectBodyandResponse( + request: SeedExhaustive.PostWithObjectBody, + requestOptions?: InlinedRequestsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithObjectBodyandResponse(request, requestOptions)); + } + + private async __postWithObjectBodyandResponse( + request: SeedExhaustive.PostWithObjectBody, + requestOptions?: InlinedRequestsClient.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)), + "/req-bodies/object", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.PostWithObjectBody.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new SeedExhaustive.BadRequestBody( + serializers.BadObjectRequestInfo.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/req-bodies/object"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts new file mode 100644 index 000000000000..15d3f75bc67d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/PostWithObjectBody.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../index.js"; + +/** + * @example + * { + * string: "string", + * integer: 1, + * nestedObject: { + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: new Date("2024-01-15T09:30:00.000Z"), + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * } + * } + */ +export interface PostWithObjectBody { + string: string; + integer: number; + nestedObject: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/index.ts new file mode 100644 index 000000000000..104f84256770 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/client/requests/index.ts @@ -0,0 +1 @@ +export type { PostWithObjectBody } from "./PostWithObjectBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/inlinedRequests/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/Client.ts new file mode 100644 index 000000000000..e2da6ff52947 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/Client.ts @@ -0,0 +1,105 @@ +// 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 * as serializers from "../../../../serialization/index.js"; +import * as SeedExhaustive from "../../../index.js"; + +export declare namespace NoAuthClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class NoAuthClient { + protected readonly _options: NormalizedClientOptions; + + constructor(options: NoAuthClient.Options) { + this._options = normalizeClientOptions(options); + } + + /** + * POST request with no auth + * + * @param {unknown} request + * @param {NoAuthClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link SeedExhaustive.BadRequestBody} + * + * @example + * await client.noAuth.postWithNoAuth({ + * "key": "value" + * }) + */ + public postWithNoAuth( + request?: unknown, + requestOptions?: NoAuthClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithNoAuth(request, requestOptions)); + } + + private async __postWithNoAuth( + request?: unknown, + requestOptions?: NoAuthClient.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)), + "/no-auth", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + 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: serializers.noAuth.postWithNoAuth.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new SeedExhaustive.BadRequestBody( + serializers.BadObjectRequestInfo.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + _response.rawResponse, + ); + default: + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/no-auth"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noAuth/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/Client.ts new file mode 100644 index 000000000000..0e5a1d42f2a7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/Client.ts @@ -0,0 +1,142 @@ +// 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"; +import * as serializers from "../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export declare namespace NoReqBodyClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class NoReqBodyClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: NoReqBodyClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {NoReqBodyClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.noReqBody.getWithNoRequestBody() + */ + public getWithNoRequestBody( + requestOptions?: NoReqBodyClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithNoRequestBody(requestOptions)); + } + + private async __getWithNoRequestBody( + requestOptions?: NoReqBodyClient.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)), + "/no-req-body", + ), + 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: serializers.types.ObjectWithOptionalField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/no-req-body"); + } + + /** + * @param {NoReqBodyClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.noReqBody.postWithNoRequestBody() + */ + public postWithNoRequestBody(requestOptions?: NoReqBodyClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__postWithNoRequestBody(requestOptions)); + } + + private async __postWithNoRequestBody( + requestOptions?: NoReqBodyClient.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)), + "/no-req-body", + ), + method: "POST", + 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: serializers.noReqBody.postWithNoRequestBody.Response.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/no-req-body"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/noReqBody/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/Client.ts new file mode 100644 index 000000000000..081ea65568cd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/Client.ts @@ -0,0 +1,93 @@ +// 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, mergeOnlyDefinedHeaders } 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 * as serializers from "../../../../serialization/index.js"; +import type * as SeedExhaustive from "../../../index.js"; + +export declare namespace ReqWithHeadersClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class ReqWithHeadersClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: ReqWithHeadersClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * @param {SeedExhaustive.ReqWithHeaders} request + * @param {ReqWithHeadersClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.reqWithHeaders.getWithCustomHeader({ + * xTestServiceHeader: "X-TEST-SERVICE-HEADER", + * xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + * body: "string" + * }) + */ + public getWithCustomHeader( + request: SeedExhaustive.ReqWithHeaders, + requestOptions?: ReqWithHeadersClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getWithCustomHeader(request, requestOptions)); + } + + private async __getWithCustomHeader( + request: SeedExhaustive.ReqWithHeaders, + requestOptions?: ReqWithHeadersClient.RequestOptions, + ): Promise> { + const { xTestServiceHeader, xTestEndpointHeader, body: _body } = request; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + mergeOnlyDefinedHeaders({ + "X-TEST-SERVICE-HEADER": xTestServiceHeader, + "X-TEST-ENDPOINT-HEADER": xTestEndpointHeader, + }), + 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)), + "/test-headers/custom-header", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: serializers.reqWithHeaders.getWithCustomHeader.Request.jsonOrThrow(_body, { + unrecognizedObjectKeys: "strip", + omitUndefined: true, + }), + 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.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/test-headers/custom-header"); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts new file mode 100644 index 000000000000..b7fce3790bf5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/ReqWithHeaders.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * @example + * { + * xTestServiceHeader: "X-TEST-SERVICE-HEADER", + * xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + * body: "string" + * } + */ +export interface ReqWithHeaders { + xTestServiceHeader: string; + xTestEndpointHeader: string; + body: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/index.ts new file mode 100644 index 000000000000..5419b56cd667 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/client/requests/index.ts @@ -0,0 +1 @@ +export type { ReqWithHeaders } from "./ReqWithHeaders.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/reqWithHeaders/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts new file mode 100644 index 000000000000..ed458c66ba3c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/ObjectWithDocs.ts @@ -0,0 +1,66 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithDocs { + /** + * Characters that could lead to broken generated SDKs: + * + * Markdown Escapes: + * - \_: Escaped underscore (e.g., FOO\_BAR) + * - \*: Escaped asterisk + * + * JSDoc (JavaScript/TypeScript): + * - @: Used for JSDoc tags + * - {: }: Used for type definitions + * - <: >: HTML tags + * - *: Can interfere with comment blocks + * - /**: JSDoc comment start + * - ** /: JSDoc comment end + * - &: HTML entities + * + * XMLDoc (C#): + * - <: >: XML tags + * - &: ': ": <: >: XML special characters + * - {: }: Used for interpolated strings + * - ///: Comment marker + * - /**: Block comment start + * - ** /: Block comment end + * + * Javadoc (Java): + * - @: Used for Javadoc tags + * - <: >: HTML tags + * - &: HTML entities + * - *: Can interfere with comment blocks + * - /**: Javadoc comment start + * - ** /: Javadoc comment end + * + * Doxygen (C++): + * - \: Used for Doxygen commands + * - @: Alternative command prefix + * - <: >: XML/HTML tags + * - &: HTML entities + * - /**: C-style comment start + * - ** /: C-style comment end + * + * RDoc (Ruby): + * - :: Used in symbol notation + * - =: Section markers + * - #: Comment marker + * - =begin: Block comment start + * - =end: Block comment end + * - @: Instance variable prefix + * - $: Global variable prefix + * - %: String literal delimiter + * - #{: String interpolation start + * - }: String interpolation end + * + * PHPDoc (PHP): + * - @: Used for PHPDoc tags + * - {: }: Used for type definitions + * - $: Variable prefix + * - /**: PHPDoc comment start + * - ** /: PHPDoc comment end + * - *: Can interfere with comment blocks + * - &: HTML entities + */ + string: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/index.ts new file mode 100644 index 000000000000..3eedfcba912c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/docs/types/index.ts @@ -0,0 +1 @@ +export * from "./ObjectWithDocs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts new file mode 100644 index 000000000000..ef1aee2d386e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/ErrorWithEnumBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ErrorWithEnumBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.WeatherReport, rawResponse?: core.RawResponse) { + super({ + message: "ErrorWithEnumBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/index.ts new file mode 100644 index 000000000000..203651bfb712 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/errors/index.ts @@ -0,0 +1 @@ +export * from "./ErrorWithEnumBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/WeatherReport.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/WeatherReport.ts new file mode 100644 index 000000000000..4023a0068946 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/WeatherReport.ts @@ -0,0 +1,9 @@ +// This file was auto-generated by Fern from our API Definition. + +export const WeatherReport = { + Sunny: "SUNNY", + Cloudy: "CLOUDY", + Raining: "RAINING", + Snowing: "SNOWING", +} as const; +export type WeatherReport = (typeof WeatherReport)[keyof typeof WeatherReport]; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/index.ts new file mode 100644 index 000000000000..3b2d63911a15 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/enum/types/index.ts @@ -0,0 +1 @@ +export * from "./WeatherReport.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/index.ts new file mode 100644 index 000000000000..c2a44b58ecd8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/index.ts @@ -0,0 +1,11 @@ +export * as docs from "./docs/index.js"; +export * from "./docs/types/index.js"; +export * from "./enum/errors/index.js"; +export * as enum_ from "./enum/index.js"; +export * from "./enum/types/index.js"; +export * from "./object/errors/index.js"; +export * as object from "./object/index.js"; +export * from "./object/types/index.js"; +export * from "./union/errors/index.js"; +export * as union from "./union/index.js"; +export * from "./union/types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts new file mode 100644 index 000000000000..2681788e4f04 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithOptionalFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class NestedObjectWithOptionalFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.NestedObjectWithOptionalField, rawResponse?: core.RawResponse) { + super({ + message: "NestedObjectWithOptionalFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts new file mode 100644 index 000000000000..203710315112 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/NestedObjectWithRequiredFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class NestedObjectWithRequiredFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.NestedObjectWithRequiredField, rawResponse?: core.RawResponse) { + super({ + message: "NestedObjectWithRequiredFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts new file mode 100644 index 000000000000..230964f8467f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithOptionalFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ObjectWithOptionalFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.ObjectWithOptionalField, rawResponse?: core.RawResponse) { + super({ + message: "ObjectWithOptionalFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts new file mode 100644 index 000000000000..b8a86dc99dd8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/ObjectWithRequiredFieldError.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ObjectWithRequiredFieldError extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.ObjectWithRequiredField, rawResponse?: core.RawResponse) { + super({ + message: "ObjectWithRequiredFieldError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/index.ts new file mode 100644 index 000000000000..e38a83090455 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/errors/index.ts @@ -0,0 +1,4 @@ +export * from "./NestedObjectWithOptionalFieldError.js"; +export * from "./NestedObjectWithRequiredFieldError.js"; +export * from "./ObjectWithOptionalFieldError.js"; +export * from "./ObjectWithRequiredFieldError.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/DoubleOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/DoubleOptional.ts new file mode 100644 index 000000000000..4898ba728836 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/DoubleOptional.ts @@ -0,0 +1,7 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface DoubleOptional { + optionalAlias?: SeedExhaustive.types.OptionalAlias | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts new file mode 100644 index 000000000000..324bb2e73908 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithOptionalField.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface NestedObjectWithOptionalField { + string?: string; + nestedObject?: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts new file mode 100644 index 000000000000..322dcfbd5c9b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/NestedObjectWithRequiredField.ts @@ -0,0 +1,8 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export interface NestedObjectWithRequiredField { + string: string; + nestedObject: SeedExhaustive.types.ObjectWithOptionalField; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts new file mode 100644 index 000000000000..b35138a717bb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithMapOfMap.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithMapOfMap { + map: Record>; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts new file mode 100644 index 000000000000..2e6b3811b575 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithOptionalField.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithOptionalField { + /** This is a rather long descriptor of this single field in a more complex type. If you ask me I think this is a pretty good description for this field all things considered. */ + string?: string; + integer?: number; + long?: number; + double?: number; + bool?: boolean; + datetime?: Date; + date?: string; + uuid?: string; + base64?: string; + list?: string[]; + set?: Set; + map?: Record; + bigint?: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts new file mode 100644 index 000000000000..032a84135c9e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/ObjectWithRequiredField.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ObjectWithRequiredField { + string: string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/OptionalAlias.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/OptionalAlias.ts new file mode 100644 index 000000000000..5dac5b761df8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/OptionalAlias.ts @@ -0,0 +1,3 @@ +// This file was auto-generated by Fern from our API Definition. + +export type OptionalAlias = string | undefined; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/index.ts new file mode 100644 index 000000000000..c3c65c311b82 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/object/types/index.ts @@ -0,0 +1,7 @@ +export * from "./DoubleOptional.js"; +export * from "./NestedObjectWithOptionalField.js"; +export * from "./NestedObjectWithRequiredField.js"; +export * from "./ObjectWithMapOfMap.js"; +export * from "./ObjectWithOptionalField.js"; +export * from "./ObjectWithRequiredField.js"; +export * from "./OptionalAlias.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts new file mode 100644 index 000000000000..c0e5b750495f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/ErrorWithUnionBody.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import * as errors from "../../../../../../errors/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export class ErrorWithUnionBody extends errors.SeedExhaustiveError { + constructor(body: SeedExhaustive.types.Animal, rawResponse?: core.RawResponse) { + super({ + message: "ErrorWithUnionBody", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + 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/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/index.ts new file mode 100644 index 000000000000..940b191924ee --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/errors/index.ts @@ -0,0 +1 @@ +export * from "./ErrorWithUnionBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/index.ts new file mode 100644 index 000000000000..38688e58bd6f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/index.ts @@ -0,0 +1,2 @@ +export * from "./errors/index.js"; +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Animal.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Animal.ts new file mode 100644 index 000000000000..f5336c6b33bc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Animal.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../index.js"; + +export type Animal = SeedExhaustive.types.Animal.Dog | SeedExhaustive.types.Animal.Cat; + +export namespace Animal { + export interface Dog extends SeedExhaustive.types.Dog { + animal: "dog"; + } + + export interface Cat extends SeedExhaustive.types.Cat { + animal: "cat"; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Cat.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Cat.ts new file mode 100644 index 000000000000..aa74d5b7dd01 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Cat.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface Cat { + name: string; + likesToMeow: boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Dog.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Dog.ts new file mode 100644 index 000000000000..24bc9aa321b1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/Dog.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface Dog { + name: string; + likesToWoof: boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/index.ts new file mode 100644 index 000000000000..342e90adc057 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/api/resources/types/resources/union/types/index.ts @@ -0,0 +1,3 @@ +export * from "./Animal.js"; +export * from "./Cat.js"; +export * from "./Dog.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/BearerAuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/BearerAuthProvider.ts new file mode 100644 index 000000000000..95ada392f8de --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/BearerAuthProvider.ts @@ -0,0 +1,37 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../core/index.js"; +import * as errors from "../errors/index.js"; + +export namespace BearerAuthProvider { + export interface AuthOptions { + token?: core.Supplier; + } + + export interface Options extends AuthOptions {} +} + +export class BearerAuthProvider implements core.AuthProvider { + private readonly token: core.Supplier | undefined; + + constructor(options: BearerAuthProvider.Options) { + this.token = options.token; + } + + public static canCreate(options: BearerAuthProvider.Options): boolean { + return options.token != null; + } + + public async getAuthRequest(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { + const token = await core.Supplier.get(this.token); + if (token == null) { + throw new errors.SeedExhaustiveError({ + message: "Please specify a token by passing it in to the constructor", + }); + } + + return { + headers: { Authorization: `Bearer ${token}` }, + }; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/index.ts new file mode 100644 index 000000000000..0ecb12b79bdb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/auth/index.ts @@ -0,0 +1 @@ +export { BearerAuthProvider } from "./BearerAuthProvider.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/AuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/AuthProvider.ts new file mode 100644 index 000000000000..895a50ff30da --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/AuthProvider.ts @@ -0,0 +1,6 @@ +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/exhaustive/serde-layer-zurg/src/core/auth/AuthRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/AuthRequest.ts new file mode 100644 index 000000000000..f6218b42211e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/AuthRequest.ts @@ -0,0 +1,9 @@ +/** + * Request parameters for authentication requests. + */ +export interface AuthRequest { + /** + * The headers to be included in the request. + */ + headers: Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/BasicAuth.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/BasicAuth.ts new file mode 100644 index 000000000000..a64235910062 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/BasicAuth.ts @@ -0,0 +1,32 @@ +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/exhaustive/serde-layer-zurg/src/core/auth/BearerToken.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/BearerToken.ts new file mode 100644 index 000000000000..c44a06c38f06 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/BearerToken.ts @@ -0,0 +1,20 @@ +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/exhaustive/serde-layer-zurg/src/core/auth/NoOpAuthProvider.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/NoOpAuthProvider.ts new file mode 100644 index 000000000000..5b7acfd2bd8b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/NoOpAuthProvider.ts @@ -0,0 +1,8 @@ +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/exhaustive/serde-layer-zurg/src/core/auth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/index.ts new file mode 100644 index 000000000000..2215b227709e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/auth/index.ts @@ -0,0 +1,5 @@ +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/exhaustive/serde-layer-zurg/src/core/base64.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/base64.ts new file mode 100644 index 000000000000..448a0db638a6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/base64.ts @@ -0,0 +1,27 @@ +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/exhaustive/serde-layer-zurg/src/core/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/exports.ts new file mode 100644 index 000000000000..69296d7100d6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/exports.ts @@ -0,0 +1 @@ +export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/APIResponse.ts new file mode 100644 index 000000000000..97ab83c2b195 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/BinaryResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/BinaryResponse.ts new file mode 100644 index 000000000000..bca7f4c77981 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/BinaryResponse.ts @@ -0,0 +1,34 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointMetadata.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointMetadata.ts new file mode 100644 index 000000000000..998d68f5c20c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointMetadata.ts @@ -0,0 +1,13 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointSupplier.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointSupplier.ts new file mode 100644 index 000000000000..8079841c4062 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/EndpointSupplier.ts @@ -0,0 +1,14 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Fetcher.ts new file mode 100644 index 000000000000..58bb0e3ef7d9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Fetcher.ts @@ -0,0 +1,391 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/Headers.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Headers.ts new file mode 100644 index 000000000000..af841aa24f55 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Headers.ts @@ -0,0 +1,93 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/HttpResponsePromise.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 000000000000..692ca7d795f0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/RawResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/RawResponse.ts new file mode 100644 index 000000000000..37fb44e2aa99 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/RawResponse.ts @@ -0,0 +1,61 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/Supplier.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Supplier.ts new file mode 100644 index 000000000000..867c931c02f4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 000000000000..88e13265e112 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,6 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/getErrorResponseBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getErrorResponseBody.ts new file mode 100644 index 000000000000..7cf4e623c2f5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getErrorResponseBody.ts @@ -0,0 +1,33 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getFetchFn.ts new file mode 100644 index 000000000000..9f845b956392 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,3 @@ +export async function getFetchFn(): Promise { + return fetch; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getHeader.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getHeader.ts new file mode 100644 index 000000000000..50f922b0e87f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getRequestBody.ts new file mode 100644 index 000000000000..91d9d81f50e5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,20 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getResponseBody.ts new file mode 100644 index 000000000000..708d55728f2b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,58 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/index.ts new file mode 100644 index 000000000000..c3bc6da20f49 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/index.ts @@ -0,0 +1,11 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/makeRequest.ts new file mode 100644 index 000000000000..921565eb0063 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/makeRequest.ts @@ -0,0 +1,42 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 000000000000..1f689688c4b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,64 @@ +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/exhaustive/serde-layer-zurg/src/core/fetcher/signals.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/signals.ts new file mode 100644 index 000000000000..7bd3757ec3a7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/fetcher/signals.ts @@ -0,0 +1,26 @@ +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/exhaustive/serde-layer-zurg/src/core/headers.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/headers.ts new file mode 100644 index 000000000000..78ed8b500c95 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/headers.ts @@ -0,0 +1,35 @@ +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/exhaustive/serde-layer-zurg/src/core/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/index.ts new file mode 100644 index 000000000000..874173e71808 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/index.ts @@ -0,0 +1,7 @@ +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 serialization from "./schemas/index.js"; +export * as url from "./url/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/json.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/json.ts new file mode 100644 index 000000000000..c052f3249f4f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/json.ts @@ -0,0 +1,27 @@ +/** + * 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/exhaustive/serde-layer-zurg/src/core/logging/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/exports.ts new file mode 100644 index 000000000000..88f6c00db0cf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/exports.ts @@ -0,0 +1,19 @@ +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/exhaustive/serde-layer-zurg/src/core/logging/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/index.ts new file mode 100644 index 000000000000..d81cc32c40f9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/index.ts @@ -0,0 +1 @@ +export * from "./logger.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/logger.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/logger.ts new file mode 100644 index 000000000000..a3f3673cda93 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/logging/logger.ts @@ -0,0 +1,203 @@ +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/exhaustive/serde-layer-zurg/src/core/runtime/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/runtime/index.ts new file mode 100644 index 000000000000..cfab23f9a834 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/runtime/runtime.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/runtime/runtime.ts new file mode 100644 index 000000000000..56ebbb87c4d3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/runtime/runtime.ts @@ -0,0 +1,134 @@ +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/exhaustive/serde-layer-zurg/src/core/schemas/Schema.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/Schema.ts new file mode 100644 index 000000000000..4cd8b1d95934 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/Schema.ts @@ -0,0 +1,103 @@ +import type { SchemaUtils } from "./builders/index.js"; + +export type Schema = BaseSchema & SchemaUtils; + +export type inferRaw = S extends Schema ? Raw : never; +export type inferParsed = S extends Schema ? Parsed : never; + +export interface BaseSchema { + parse: (raw: unknown, opts?: SchemaOptions) => MaybeValid; + json: (parsed: unknown, opts?: SchemaOptions) => MaybeValid; + getType: () => SchemaType | SchemaType; +} + +export const SchemaType = { + BIGINT: "bigint", + DATE: "date", + ENUM: "enum", + LIST: "list", + STRING_LITERAL: "stringLiteral", + BOOLEAN_LITERAL: "booleanLiteral", + OBJECT: "object", + ANY: "any", + BOOLEAN: "boolean", + NUMBER: "number", + STRING: "string", + UNKNOWN: "unknown", + NEVER: "never", + RECORD: "record", + SET: "set", + UNION: "union", + UNDISCRIMINATED_UNION: "undiscriminatedUnion", + NULLABLE: "nullable", + OPTIONAL: "optional", + OPTIONAL_NULLABLE: "optionalNullable", +} as const; + +export type SchemaType = (typeof SchemaType)[keyof typeof SchemaType]; + +export type MaybeValid = Valid | Invalid; + +export interface Valid { + ok: true; + value: T; +} + +export interface Invalid { + ok: false; + errors: ValidationError[]; +} + +export interface ValidationError { + path: string[]; + message: string; +} + +export interface SchemaOptions { + /** + * how to handle unrecognized keys in objects + * + * @default "fail" + */ + unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; + + /** + * whether to fail when an unrecognized discriminant value is + * encountered in a union + * + * @default false + */ + allowUnrecognizedUnionMembers?: boolean; + + /** + * whether to fail when an unrecognized enum value is encountered + * + * @default false + */ + allowUnrecognizedEnumValues?: boolean; + + /** + * whether to allow data that doesn't conform to the schema. + * invalid data is passed through without transformation. + * + * when this is enabled, .parse() and .json() will always + * return `ok: true`. `.parseOrThrow()` and `.jsonOrThrow()` + * will never fail. + * + * @default false + */ + skipValidation?: boolean; + + /** + * each validation failure contains a "path" property, which is + * the breadcrumbs to the offending node in the JSON. you can supply + * a prefix that is prepended to all the errors' paths. this can be + * helpful for zurg's internal debug logging. + */ + breadcrumbsPrefix?: string[]; + + /** + * whether to send 'null' for optional properties explicitly set to 'undefined'. + */ + omitUndefined?: boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/bigint.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/bigint.ts new file mode 100644 index 000000000000..2c7c74c54a37 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/bigint.ts @@ -0,0 +1,55 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; + +export function bigint(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw === "bigint") { + return { + ok: true, + value: raw, + }; + } + if (typeof raw === "number") { + return { + ok: true, + value: BigInt(raw), + }; + } + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "bigint | number"), + }, + ], + }; + }, + json: (bigint, { breadcrumbsPrefix = [] } = {}) => { + if (typeof bigint !== "bigint") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(bigint, "bigint"), + }, + ], + }; + } + return { + ok: true, + value: bigint, + }; + }, + getType: () => SchemaType.BIGINT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/index.ts new file mode 100644 index 000000000000..13cc76e25b2a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/bigint/index.ts @@ -0,0 +1 @@ +export { bigint } from "./bigint.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/date.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/date.ts new file mode 100644 index 000000000000..f02e3367f88f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/date.ts @@ -0,0 +1,65 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; + +// https://stackoverflow.com/questions/12756159/regex-and-iso8601-formatted-datetime +const ISO_8601_REGEX = + /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; + +export function date(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "string"), + }, + ], + }; + } + if (!ISO_8601_REGEX.test(raw)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "ISO 8601 date string"), + }, + ], + }; + } + return { + ok: true, + value: new Date(raw), + }; + }, + json: (date, { breadcrumbsPrefix = [] } = {}) => { + if (date instanceof Date) { + return { + ok: true, + value: date.toISOString(), + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(date, "Date object"), + }, + ], + }; + } + }, + getType: () => SchemaType.DATE, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/index.ts new file mode 100644 index 000000000000..e22a2f16bfc9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/date/index.ts @@ -0,0 +1 @@ +export { date } from "./date.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/enum.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/enum.ts new file mode 100644 index 000000000000..ccae24bcf200 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/enum.ts @@ -0,0 +1,43 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export function enum_(values: E): Schema { + const validValues = new Set(values); + + const schemaCreator = createIdentitySchemaCreator( + SchemaType.ENUM, + (value, { allowUnrecognizedEnumValues, breadcrumbsPrefix = [] } = {}) => { + if (typeof value !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + + if (!validValues.has(value) && !allowUnrecognizedEnumValues) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "enum"), + }, + ], + }; + } + + return { + ok: true, + value: value as U, + }; + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/index.ts new file mode 100644 index 000000000000..ff3bee3bf653 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/enum/index.ts @@ -0,0 +1 @@ +export { enum_ } from "./enum.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/index.ts new file mode 100644 index 000000000000..ddb9b3c94555 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/index.ts @@ -0,0 +1,14 @@ +export * from "./bigint/index.js"; +export * from "./date/index.js"; +export * from "./enum/index.js"; +export * from "./lazy/index.js"; +export * from "./list/index.js"; +export * from "./literals/index.js"; +export * from "./object/index.js"; +export * from "./object-like/index.js"; +export * from "./primitives/index.js"; +export * from "./record/index.js"; +export * from "./schema-utils/index.js"; +export * from "./set/index.js"; +export * from "./undiscriminated-union/index.js"; +export * from "./union/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/index.ts new file mode 100644 index 000000000000..e8ca40992061 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/index.ts @@ -0,0 +1,3 @@ +export type { SchemaGetter } from "./lazy.js"; +export { lazy } from "./lazy.js"; +export { lazyObject } from "./lazyObject.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazy.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazy.ts new file mode 100644 index 000000000000..37f28871683a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazy.ts @@ -0,0 +1,32 @@ +import type { BaseSchema, Schema } from "../../Schema.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; + +export type SchemaGetter> = () => SchemaType; + +export function lazy(getter: SchemaGetter>): Schema { + const baseSchema = constructLazyBaseSchema(getter); + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function constructLazyBaseSchema( + getter: SchemaGetter>, +): BaseSchema { + return { + parse: (raw, opts) => getMemoizedSchema(getter).parse(raw, opts), + json: (parsed, opts) => getMemoizedSchema(getter).json(parsed, opts), + getType: () => getMemoizedSchema(getter).getType(), + }; +} + +type MemoizedGetter> = SchemaGetter & { __zurg_memoized?: SchemaType }; + +export function getMemoizedSchema>(getter: SchemaGetter): SchemaType { + const castedGetter = getter as MemoizedGetter; + if (castedGetter.__zurg_memoized == null) { + castedGetter.__zurg_memoized = getter(); + } + return castedGetter.__zurg_memoized; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazyObject.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazyObject.ts new file mode 100644 index 000000000000..192c90e5c83e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/lazy/lazyObject.ts @@ -0,0 +1,20 @@ +import { getObjectUtils } from "../object/index.js"; +import type { BaseObjectSchema, ObjectSchema } from "../object/types.js"; +import { getObjectLikeUtils } from "../object-like/index.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import { constructLazyBaseSchema, getMemoizedSchema, type SchemaGetter } from "./lazy.js"; + +export function lazyObject(getter: SchemaGetter>): ObjectSchema { + const baseSchema: BaseObjectSchema = { + ...constructLazyBaseSchema(getter), + _getRawProperties: () => getMemoizedSchema(getter)._getRawProperties(), + _getParsedProperties: () => getMemoizedSchema(getter)._getParsedProperties(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/index.ts new file mode 100644 index 000000000000..021f1e4df1ff --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/index.ts @@ -0,0 +1 @@ +export { list } from "./list.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/list.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/list.ts new file mode 100644 index 000000000000..4f8c10ba483a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/list/list.ts @@ -0,0 +1,73 @@ +import { type BaseSchema, type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; + +export function list(schema: Schema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => + validateAndTransformArray(raw, (item, index) => + schema.parse(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + json: (parsed, opts) => + validateAndTransformArray(parsed, (item, index) => + schema.json(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + getType: () => SchemaType.LIST, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformArray( + value: unknown, + transformItem: (item: Raw, index: number) => MaybeValid, +): MaybeValid { + if (!Array.isArray(value)) { + return { + ok: false, + errors: [ + { + message: getErrorMessageForIncorrectType(value, "list"), + path: [], + }, + ], + }; + } + + const maybeValidItems = value.map((item, index) => transformItem(item, index)); + + return maybeValidItems.reduce>( + (acc, item) => { + if (acc.ok && item.ok) { + return { + ok: true, + value: [...acc.value, item.value], + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!item.ok) { + errors.push(...item.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: [] }, + ); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/booleanLiteral.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/booleanLiteral.ts new file mode 100644 index 000000000000..db5d2c7a7313 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/booleanLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export function booleanLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.BOOLEAN_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `${literal.toString()}`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/index.ts new file mode 100644 index 000000000000..4a4ab39d91a7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/index.ts @@ -0,0 +1,2 @@ +export { booleanLiteral } from "./booleanLiteral.js"; +export { stringLiteral } from "./stringLiteral.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/stringLiteral.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/stringLiteral.ts new file mode 100644 index 000000000000..ce6e20caf8d6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/literals/stringLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export function stringLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.STRING_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `"${literal}"`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/getObjectLikeUtils.ts new file mode 100644 index 000000000000..af69acb01dc1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -0,0 +1,79 @@ +import type { BaseSchema } from "../../Schema.js"; +import { filterObject } from "../../utils/filterObject.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { isPlainObject } from "../../utils/isPlainObject.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import type { ObjectLikeSchema, ObjectLikeUtils } from "./types.js"; + +export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { + return { + withParsedProperties: (properties) => withParsedProperties(schema, properties), + }; +} + +/** + * object-like utils are defined in one file to resolve issues with circular imports + */ + +export function withParsedProperties( + objectLike: BaseSchema, + properties: { [K in keyof Properties]: Properties[K] | ((parsed: ParsedObjectShape) => Properties[K]) }, +): ObjectLikeSchema { + const objectSchema: BaseSchema = { + parse: (raw, opts) => { + const parsedObject = objectLike.parse(raw, opts); + if (!parsedObject.ok) { + return parsedObject; + } + + const additionalProperties = Object.entries(properties).reduce>( + (processed, [key, value]) => { + return { + ...processed, + [key]: typeof value === "function" ? value(parsedObject.value) : value, + }; + }, + {}, + ); + + return { + ok: true, + value: { + ...parsedObject.value, + ...(additionalProperties as Properties), + }, + }; + }, + + json: (parsed, opts) => { + if (!isPlainObject(parsed)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "object"), + }, + ], + }; + } + + // strip out added properties + const addedPropertyKeys = new Set(Object.keys(properties)); + const parsedWithoutAddedProperties = filterObject( + parsed, + Object.keys(parsed).filter((key) => !addedPropertyKeys.has(key)), + ); + + return objectLike.json(parsedWithoutAddedProperties as ParsedObjectShape, opts); + }, + + getType: () => objectLike.getType(), + }; + + return { + ...objectSchema, + ...getSchemaUtils(objectSchema), + ...getObjectLikeUtils(objectSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/index.ts new file mode 100644 index 000000000000..2451ef7d0e50 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/index.ts @@ -0,0 +1,2 @@ +export { getObjectLikeUtils, withParsedProperties } from "./getObjectLikeUtils.js"; +export type { ObjectLikeSchema, ObjectLikeUtils } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/types.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/types.ts new file mode 100644 index 000000000000..44b9669108cb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object-like/types.ts @@ -0,0 +1,13 @@ +import type { BaseSchema, Schema } from "../../Schema.js"; + +export type ObjectLikeSchema = Schema & + BaseSchema & + ObjectLikeUtils; + +export interface ObjectLikeUtils { + withParsedProperties: >( + properties: { + [K in keyof T]: T[K] | ((parsed: Parsed) => T[K]); + }, + ) => ObjectLikeSchema; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/index.ts new file mode 100644 index 000000000000..c6611aaacd3b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/index.ts @@ -0,0 +1,22 @@ +export { getObjectUtils, object } from "./object.js"; +export type { + inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, +} from "./objectWithoutOptionalProperties.js"; +export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties.js"; +export type { Property } from "./property.js"; +export { isProperty, property } from "./property.js"; +export type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObject, + inferParsedObjectFromPropertySchemas, + inferParsedPropertySchema, + inferRawKey, + inferRawObject, + inferRawObjectFromPropertySchemas, + inferRawPropertySchema, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/object.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/object.ts new file mode 100644 index 000000000000..624d4d3888d5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/object.ts @@ -0,0 +1,382 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema.js"; +import { entries } from "../../utils/entries.js"; +import { filterObject } from "../../utils/filterObject.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { isPlainObject } from "../../utils/isPlainObject.js"; +import { keys } from "../../utils/keys.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { partition } from "../../utils/partition.js"; +import { getObjectLikeUtils } from "../object-like/index.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import { isProperty } from "./property.js"; +import type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObjectFromPropertySchemas, + inferRawObjectFromPropertySchemas, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types.js"; + +interface ObjectPropertyWithRawKey { + rawKey: string; + parsedKey: string; + valueSchema: Schema; +} + +export function object>( + schemas: T, +): inferObjectSchemaFromPropertySchemas { + const baseSchema: BaseObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas + > = { + _getRawProperties: () => + Object.entries(schemas).map(([parsedKey, propertySchema]) => + isProperty(propertySchema) ? propertySchema.rawKey : parsedKey, + ) as unknown as (keyof inferRawObjectFromPropertySchemas)[], + _getParsedProperties: () => keys(schemas) as unknown as (keyof inferParsedObjectFromPropertySchemas)[], + + parse: (raw, opts) => { + const rawKeyToProperty: Record = {}; + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + const property: ObjectPropertyWithRawKey = { + rawKey, + parsedKey: parsedKey as string, + valueSchema, + }; + + rawKeyToProperty[rawKey] = property; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(rawKey); + } + } + + return validateAndTransformObject({ + value: raw, + requiredKeys, + getProperty: (rawKey) => { + const property = rawKeyToProperty[rawKey]; + if (property == null) { + return undefined; + } + return { + transformedKey: property.parsedKey, + transform: (propertyValue) => + property.valueSchema.parse(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], + }), + }; + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + json: (parsed, opts) => { + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(parsedKey as string); + } + } + + return validateAndTransformObject({ + value: parsed, + requiredKeys, + getProperty: ( + parsedKey, + ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { + const property = schemas[parsedKey as keyof T]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (property == null) { + return undefined; + } + + if (isProperty(property)) { + return { + transformedKey: property.rawKey, + transform: (propertyValue) => + property.valueSchema.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } else { + return { + transformedKey: parsedKey, + transform: (propertyValue) => + property.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + getType: () => SchemaType.OBJECT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} + +function validateAndTransformObject({ + value, + requiredKeys, + getProperty, + unrecognizedObjectKeys = "fail", + skipValidation = false, + breadcrumbsPrefix = [], +}: { + value: unknown; + requiredKeys: string[]; + getProperty: ( + preTransformedKey: string, + ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; + unrecognizedObjectKeys: "fail" | "passthrough" | "strip" | undefined; + skipValidation: boolean | undefined; + breadcrumbsPrefix: string[] | undefined; + omitUndefined: boolean | undefined; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const missingRequiredKeys = new Set(requiredKeys); + const errors: ValidationError[] = []; + const transformed: Record = {}; + + for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + const property = getProperty(preTransformedKey); + + if (property != null) { + missingRequiredKeys.delete(preTransformedKey); + + const value = property.transform(preTransformedItemValue as object); + if (value.ok) { + transformed[property.transformedKey] = value.value; + } else { + transformed[preTransformedKey] = preTransformedItemValue; + errors.push(...value.errors); + } + } else { + switch (unrecognizedObjectKeys) { + case "fail": + errors.push({ + path: [...breadcrumbsPrefix, preTransformedKey], + message: `Unexpected key "${preTransformedKey}"`, + }); + break; + case "strip": + break; + case "passthrough": + transformed[preTransformedKey] = preTransformedItemValue; + break; + } + } + } + + errors.push( + ...requiredKeys + .filter((key) => missingRequiredKeys.has(key)) + .map((key) => ({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + })), + ); + + if (errors.length === 0 || skipValidation) { + return { + ok: true, + value: transformed as Transformed, + }; + } else { + return { + ok: false, + errors, + }; + } +} + +export function getObjectUtils(schema: BaseObjectSchema): ObjectUtils { + return { + extend: (extension: ObjectSchema) => { + const baseSchema: BaseObjectSchema = { + _getParsedProperties: () => [...schema._getParsedProperties(), ...extension._getParsedProperties()], + _getRawProperties: () => [...schema._getRawProperties(), ...extension._getRawProperties()], + parse: (raw, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getRawProperties(), + value: raw, + transformBase: (rawBase) => schema.parse(rawBase, opts), + transformExtension: (rawExtension) => extension.parse(rawExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getParsedProperties(), + value: parsed, + transformBase: (parsedBase) => schema.json(parsedBase, opts), + transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + passthrough: () => { + const baseSchema: BaseObjectSchema = + { + _getParsedProperties: () => schema._getParsedProperties(), + _getRawProperties: () => schema._getRawProperties(), + parse: (raw, opts) => { + const transformed = schema.parse(raw, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(raw as any), + ...transformed.value, + }, + }; + }, + json: (parsed, opts) => { + const transformed = schema.json(parsed, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(parsed as any), + ...transformed.value, + }, + }; + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + }; +} + +function validateAndTransformExtendedObject({ + extensionKeys, + value, + transformBase, + transformExtension, + breadcrumbsPrefix = [], +}: { + extensionKeys: (keyof PreTransformedExtension)[]; + value: unknown; + transformBase: (value: object) => MaybeValid; + transformExtension: (value: object) => MaybeValid; + breadcrumbsPrefix?: string[]; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const extensionPropertiesSet = new Set(extensionKeys); + const [extensionProperties, baseProperties] = partition(keys(value), (key) => + extensionPropertiesSet.has(key as keyof PreTransformedExtension), + ); + + const transformedBase = transformBase(filterObject(value, baseProperties)); + const transformedExtension = transformExtension(filterObject(value, extensionProperties)); + + if (transformedBase.ok && transformedExtension.ok) { + return { + ok: true, + value: { + ...transformedBase.value, + ...transformedExtension.value, + }, + }; + } else { + return { + ok: false, + errors: [ + ...(transformedBase.ok ? [] : transformedBase.errors), + ...(transformedExtension.ok ? [] : transformedExtension.errors), + ], + }; + } +} + +function isSchemaRequired(schema: Schema): boolean { + return !isSchemaOptional(schema); +} + +function isSchemaOptional(schema: Schema): boolean { + switch (schema.getType()) { + case SchemaType.ANY: + case SchemaType.UNKNOWN: + case SchemaType.OPTIONAL: + case SchemaType.OPTIONAL_NULLABLE: + return true; + default: + return false; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts new file mode 100644 index 000000000000..4d39c862f0c4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts @@ -0,0 +1,23 @@ +import { object } from "./object.js"; +import type { + inferParsedPropertySchema, + inferRawObjectFromPropertySchemas, + ObjectSchema, + PropertySchemas, +} from "./types.js"; + +export function objectWithoutOptionalProperties>( + schemas: T, +): inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas { + return object(schemas) as unknown as inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas; +} + +export type inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas> = + ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas + >; + +export type inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas> = { + [K in keyof T]: inferParsedPropertySchema; +}; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/property.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/property.ts new file mode 100644 index 000000000000..d1f9f386aa64 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/property.ts @@ -0,0 +1,23 @@ +import type { Schema } from "../../Schema.js"; + +export function property( + rawKey: RawKey, + valueSchema: Schema, +): Property { + return { + rawKey, + valueSchema, + isProperty: true, + }; +} + +export interface Property { + rawKey: RawKey; + valueSchema: Schema; + isProperty: true; +} + +export function isProperty>(maybeProperty: unknown): maybeProperty is O { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return (maybeProperty as O).isProperty; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/types.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/types.ts new file mode 100644 index 000000000000..735d14fc4cee --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/object/types.ts @@ -0,0 +1,73 @@ +import type { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema.js"; +import type { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties.js"; +import type { ObjectLikeUtils } from "../object-like/index.js"; +import type { SchemaUtils } from "../schema-utils/index.js"; +import type { Property } from "./property.js"; + +export type ObjectSchema = BaseObjectSchema & + ObjectLikeUtils & + ObjectUtils & + SchemaUtils; + +export interface BaseObjectSchema extends BaseSchema { + _getRawProperties: () => (keyof Raw)[]; + _getParsedProperties: () => (keyof Parsed)[]; +} + +export interface ObjectUtils { + extend: ( + schemas: ObjectSchema, + ) => ObjectSchema; + passthrough: () => ObjectSchema; +} + +export type inferRawObject> = O extends ObjectSchema ? Raw : never; + +export type inferParsedObject> = O extends ObjectSchema + ? Parsed + : never; + +export type inferObjectSchemaFromPropertySchemas> = ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas +>; + +export type inferRawObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [ParsedKey in keyof T as inferRawKey]: inferRawPropertySchema; + }>; + +export type inferParsedObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [K in keyof T]: inferParsedPropertySchema; + }>; + +export type PropertySchemas = Record< + ParsedKeys, + Property | Schema +>; + +export type inferRawPropertySchema

| Schema> = P extends Property< + any, + infer Raw, + any +> + ? Raw + : P extends Schema + ? inferRaw

+ : never; + +export type inferParsedPropertySchema

| Schema> = P extends Property< + any, + any, + infer Parsed +> + ? Parsed + : P extends Schema + ? inferParsed

+ : never; + +export type inferRawKey< + ParsedKey extends string | number | symbol, + P extends Property | Schema, +> = P extends Property ? Raw : ParsedKey; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/any.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/any.ts new file mode 100644 index 000000000000..bc4d47fab56e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/any.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; + +export const any: () => Schema = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ + ok: true, + value, +})); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/boolean.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/boolean.ts new file mode 100644 index 000000000000..78c3c36284c7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/boolean.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export const boolean: () => Schema = createIdentitySchemaCreator( + SchemaType.BOOLEAN, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "boolean") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "boolean"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/index.ts new file mode 100644 index 000000000000..7a3ee0154829 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/index.ts @@ -0,0 +1,6 @@ +export { any } from "./any.js"; +export { boolean } from "./boolean.js"; +export { never } from "./never.js"; +export { number } from "./number.js"; +export { string } from "./string.js"; +export { unknown } from "./unknown.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/never.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/never.ts new file mode 100644 index 000000000000..91f85d74c01b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/never.ts @@ -0,0 +1,15 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; + +export const never: () => Schema = createIdentitySchemaCreator( + SchemaType.NEVER, + (_value, { breadcrumbsPrefix = [] } = {}) => ({ + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: "Expected never", + }, + ], + }), +); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/number.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/number.ts new file mode 100644 index 000000000000..6f16cd462a1b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/number.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export const number: () => Schema = createIdentitySchemaCreator( + SchemaType.NUMBER, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "number") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "number"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/string.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/string.ts new file mode 100644 index 000000000000..b29d72ae7ef1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/string.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; + +export const string: () => Schema = createIdentitySchemaCreator( + SchemaType.STRING, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "string") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/unknown.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/unknown.ts new file mode 100644 index 000000000000..04514160366f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/primitives/unknown.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; + +export const unknown: () => Schema = createIdentitySchemaCreator( + SchemaType.UNKNOWN, + (value) => ({ ok: true, value }), +); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/index.ts new file mode 100644 index 000000000000..b17997f7bf84 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/index.ts @@ -0,0 +1,2 @@ +export { record } from "./record.js"; +export type { BaseRecordSchema, RecordSchema } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/record.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/record.ts new file mode 100644 index 000000000000..a489660399b7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/record.ts @@ -0,0 +1,129 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema.js"; +import { entries } from "../../utils/entries.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { isPlainObject } from "../../utils/isPlainObject.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import type { BaseRecordSchema, RecordSchema } from "./types.js"; + +export function record( + keySchema: Schema, + valueSchema: Schema, +): RecordSchema { + const baseSchema: BaseRecordSchema = { + parse: (raw, opts) => { + return validateAndTransformRecord({ + value: raw, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.parse(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.parse(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformRecord({ + value: parsed, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.json(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.json(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.RECORD, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformRecord({ + value, + isKeyNumeric, + transformKey, + transformValue, + breadcrumbsPrefix = [], +}: { + value: unknown; + isKeyNumeric: boolean; + transformKey: (key: string | number) => MaybeValid; + transformValue: (value: unknown, key: string | number) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + return entries(value).reduce>>( + (accPromise, [stringKey, value]) => { + if (value === undefined) { + return accPromise; + } + + const acc = accPromise; + + let key: string | number = stringKey; + if (isKeyNumeric) { + const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; + if (!Number.isNaN(numberKey)) { + key = numberKey; + } + } + const transformedKey = transformKey(key); + + const transformedValue = transformValue(value, key); + + if (acc.ok && transformedKey.ok && transformedValue.ok) { + return { + ok: true, + value: { + ...acc.value, + [transformedKey.value]: transformedValue.value, + }, + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!transformedKey.ok) { + errors.push(...transformedKey.errors); + } + if (!transformedValue.ok) { + errors.push(...transformedValue.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: {} as Record }, + ); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/types.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/types.ts new file mode 100644 index 000000000000..5950b4cbde1e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/record/types.ts @@ -0,0 +1,17 @@ +import type { BaseSchema } from "../../Schema.js"; +import type { SchemaUtils } from "../schema-utils/index.js"; + +export type RecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseRecordSchema & + SchemaUtils, Record>; + +export type BaseRecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseSchema, Record>; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/JsonError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/JsonError.ts new file mode 100644 index 000000000000..daee3dc79184 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/JsonError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema.js"; +import { stringifyValidationError } from "./stringifyValidationErrors.js"; + +export class JsonError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, JsonError.prototype); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/ParseError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/ParseError.ts new file mode 100644 index 000000000000..9facf06159b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/ParseError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema.js"; +import { stringifyValidationError } from "./stringifyValidationErrors.js"; + +export class ParseError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, ParseError.prototype); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/getSchemaUtils.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/getSchemaUtils.ts new file mode 100644 index 000000000000..3ceaf4e011f0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/getSchemaUtils.ts @@ -0,0 +1,181 @@ +import { type BaseSchema, type Schema, type SchemaOptions, SchemaType } from "../../Schema.js"; +import { JsonError } from "./JsonError.js"; +import { ParseError } from "./ParseError.js"; + +export interface SchemaUtils { + nullable: () => Schema; + optional: () => Schema; + optionalNullable: () => Schema; + transform: (transformer: SchemaTransformer) => Schema; + parseOrThrow: (raw: unknown, opts?: SchemaOptions) => Parsed; + jsonOrThrow: (raw: unknown, opts?: SchemaOptions) => Raw; +} + +export interface SchemaTransformer { + transform: (parsed: Parsed) => Transformed; + untransform: (transformed: any) => Parsed; +} + +export function getSchemaUtils(schema: BaseSchema): SchemaUtils { + return { + nullable: () => nullable(schema), + optional: () => optional(schema), + optionalNullable: () => optionalNullable(schema), + transform: (transformer) => transform(schema, transformer), + parseOrThrow: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (parsed.ok) { + return parsed.value; + } + throw new ParseError(parsed.errors); + }, + jsonOrThrow: (parsed, opts) => { + const raw = schema.json(parsed, opts); + if (raw.ok) { + return raw.value; + } + throw new JsonError(raw.errors); + }, + }; +} + +/** + * schema utils are defined in one file to resolve issues with circular imports + */ + +export function nullable(schema: BaseSchema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optional( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: undefined, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (opts?.omitUndefined && parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optionalNullable( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (raw === null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed === null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL_NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function transform( + schema: BaseSchema, + transformer: SchemaTransformer, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (!parsed.ok) { + return parsed; + } + return { + ok: true, + value: transformer.transform(parsed.value), + }; + }, + json: (transformed, opts) => { + const parsed = transformer.untransform(transformed); + return schema.json(parsed, opts); + }, + getType: () => schema.getType(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/index.ts new file mode 100644 index 000000000000..efb3b0c46288 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/index.ts @@ -0,0 +1,4 @@ +export type { SchemaUtils } from "./getSchemaUtils.js"; +export { getSchemaUtils, optional, transform } from "./getSchemaUtils.js"; +export { JsonError } from "./JsonError.js"; +export { ParseError } from "./ParseError.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts new file mode 100644 index 000000000000..d36a4900c6e6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts @@ -0,0 +1,8 @@ +import type { ValidationError } from "../../Schema.js"; + +export function stringifyValidationError(error: ValidationError): string { + if (error.path.length === 0) { + return error.message; + } + return `${error.path.join(" -> ")}: ${error.message}`; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/index.ts new file mode 100644 index 000000000000..c72be55e9406 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/index.ts @@ -0,0 +1 @@ +export { set } from "./set.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/set.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/set.ts new file mode 100644 index 000000000000..2013cdb4760f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/set/set.ts @@ -0,0 +1,43 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { list } from "../list/index.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; + +export function set(schema: Schema): Schema> { + const listSchema = list(schema); + const baseSchema: BaseSchema> = { + parse: (raw, opts) => { + const parsedList = listSchema.parse(raw, opts); + if (parsedList.ok) { + return { + ok: true, + value: new Set(parsedList.value), + }; + } else { + return parsedList; + } + }, + json: (parsed, opts) => { + if (!(parsed instanceof Set)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "Set"), + }, + ], + }; + } + const jsonList = listSchema.json([...parsed], opts); + return jsonList; + }, + getType: () => SchemaType.SET, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/index.ts new file mode 100644 index 000000000000..c8318222b4f7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/index.ts @@ -0,0 +1,6 @@ +export type { + inferParsedUnidiscriminatedUnionSchema, + inferRawUnidiscriminatedUnionSchema, + UndiscriminatedUnionSchema, +} from "./types.js"; +export { undiscriminatedUnion } from "./undiscriminatedUnion.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/types.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/types.ts new file mode 100644 index 000000000000..0d5096fab913 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/types.ts @@ -0,0 +1,10 @@ +import type { inferParsed, inferRaw, Schema } from "../../Schema.js"; + +export type UndiscriminatedUnionSchema = Schema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema +>; + +export type inferRawUnidiscriminatedUnionSchema = inferRaw; + +export type inferParsedUnidiscriminatedUnionSchema = inferParsed; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts new file mode 100644 index 000000000000..07591b4d3e63 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts @@ -0,0 +1,67 @@ +import { + type BaseSchema, + type MaybeValid, + type Schema, + type SchemaOptions, + SchemaType, + type ValidationError, +} from "../../Schema.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import type { inferParsedUnidiscriminatedUnionSchema, inferRawUnidiscriminatedUnionSchema } from "./types.js"; + +export function undiscriminatedUnion, ...Schema[]]>( + schemas: Schemas, +): Schema, inferParsedUnidiscriminatedUnionSchema> { + const baseSchema: BaseSchema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema + > = { + parse: (raw, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.parse(raw, opts), + schemas, + opts, + ); + }, + json: (parsed, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.json(parsed, opts), + schemas, + opts, + ); + }, + getType: () => SchemaType.UNDISCRIMINATED_UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformUndiscriminatedUnion( + transform: (schema: Schema, opts: SchemaOptions) => MaybeValid, + schemas: Schema[], + opts: SchemaOptions | undefined, +): MaybeValid { + const errors: ValidationError[] = []; + for (const [index, schema] of schemas.entries()) { + const transformed = transform(schema, { ...opts, skipValidation: false }); + if (transformed.ok) { + return transformed; + } else { + for (const error of transformed.errors) { + errors.push({ + path: error.path, + message: `[Variant ${index}] ${error.message}`, + }); + } + } + } + + return { + ok: false, + errors, + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/discriminant.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/discriminant.ts new file mode 100644 index 000000000000..73cd62adeba5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/discriminant.ts @@ -0,0 +1,14 @@ +export function discriminant( + parsedDiscriminant: ParsedDiscriminant, + rawDiscriminant: RawDiscriminant, +): Discriminant { + return { + parsedDiscriminant, + rawDiscriminant, + }; +} + +export interface Discriminant { + parsedDiscriminant: ParsedDiscriminant; + rawDiscriminant: RawDiscriminant; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/index.ts new file mode 100644 index 000000000000..6bc29ba9ed38 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/index.ts @@ -0,0 +1,10 @@ +export type { Discriminant } from "./discriminant.js"; +export { discriminant } from "./discriminant.js"; +export type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types.js"; +export { union } from "./union.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/types.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/types.ts new file mode 100644 index 000000000000..7bfdd636d8d0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/types.ts @@ -0,0 +1,26 @@ +import type { inferParsedObject, inferRawObject, ObjectSchema } from "../object/index.js"; +import type { Discriminant } from "./discriminant.js"; + +export type UnionSubtypes = { + [K in DiscriminantValues]: ObjectSchema; +}; + +export type inferRawUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferRawObject; +}[keyof U]; + +export type inferParsedUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferParsedObject; +}[keyof U]; + +export type inferRawDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Raw + : never; + +export type inferParsedDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Parsed + : never; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/union.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/union.ts new file mode 100644 index 000000000000..509658e0eb3d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/builders/union/union.ts @@ -0,0 +1,176 @@ +import { type BaseSchema, type MaybeValid, SchemaType } from "../../Schema.js"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType.js"; +import { isPlainObject } from "../../utils/isPlainObject.js"; +import { keys } from "../../utils/keys.js"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation.js"; +import { enum_ } from "../enum/index.js"; +import type { ObjectSchema } from "../object/index.js"; +import { getObjectLikeUtils, type ObjectLikeSchema } from "../object-like/index.js"; +import { getSchemaUtils } from "../schema-utils/index.js"; +import type { Discriminant } from "./discriminant.js"; +import type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types.js"; + +export function union, U extends UnionSubtypes>( + discriminant: D, + union: U, +): ObjectLikeSchema, inferParsedUnion> { + const rawDiscriminant = + typeof discriminant === "string" ? discriminant : (discriminant.rawDiscriminant as inferRawDiscriminant); + const parsedDiscriminant = + typeof discriminant === "string" + ? discriminant + : (discriminant.parsedDiscriminant as inferParsedDiscriminant); + + const discriminantValueSchema = enum_(keys(union) as string[]); + + const baseSchema: BaseSchema, inferParsedUnion> = { + parse: (raw, opts) => { + return transformAndValidateUnion({ + value: raw, + discriminant: rawDiscriminant, + transformedDiscriminant: parsedDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.parse(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.parse(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return transformAndValidateUnion({ + value: parsed, + discriminant: parsedDiscriminant, + transformedDiscriminant: rawDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.json(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.json(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + }; +} + +function transformAndValidateUnion< + TransformedDiscriminant extends string, + TransformedDiscriminantValue extends string, + TransformedAdditionalProperties, +>({ + value, + discriminant, + transformedDiscriminant, + transformDiscriminantValue, + getAdditionalPropertiesSchema, + allowUnrecognizedUnionMembers = false, + transformAdditionalProperties, + breadcrumbsPrefix = [], +}: { + value: unknown; + discriminant: string; + transformedDiscriminant: TransformedDiscriminant; + transformDiscriminantValue: (discriminantValue: unknown) => MaybeValid; + getAdditionalPropertiesSchema: (discriminantValue: string) => ObjectSchema | undefined; + allowUnrecognizedUnionMembers: boolean | undefined; + transformAdditionalProperties: ( + additionalProperties: unknown, + additionalPropertiesSchema: ObjectSchema, + ) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid & TransformedAdditionalProperties> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const { [discriminant]: discriminantValue, ...additionalProperties } = value; + + if (discriminantValue == null) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: `Missing discriminant ("${discriminant}")`, + }, + ], + }; + } + + const transformedDiscriminantValue = transformDiscriminantValue(discriminantValue); + if (!transformedDiscriminantValue.ok) { + return { + ok: false, + errors: transformedDiscriminantValue.errors, + }; + } + + const additionalPropertiesSchema = getAdditionalPropertiesSchema(transformedDiscriminantValue.value); + + if (additionalPropertiesSchema == null) { + if (allowUnrecognizedUnionMembers) { + return { + ok: true, + value: { + [transformedDiscriminant]: transformedDiscriminantValue.value, + ...additionalProperties, + } as Record & TransformedAdditionalProperties, + }; + } else { + return { + ok: false, + errors: [ + { + path: [...breadcrumbsPrefix, discriminant], + message: "Unexpected discriminant value", + }, + ], + }; + } + } + + const transformedAdditionalProperties = transformAdditionalProperties( + additionalProperties, + additionalPropertiesSchema, + ); + if (!transformedAdditionalProperties.ok) { + return transformedAdditionalProperties; + } + + return { + ok: true, + value: { + [transformedDiscriminant]: discriminantValue, + ...transformedAdditionalProperties.value, + } as Record & TransformedAdditionalProperties, + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/index.ts new file mode 100644 index 000000000000..befac2e3bebc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./builders/index.js"; +export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/MaybePromise.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/MaybePromise.ts new file mode 100644 index 000000000000..9cd354b3418e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/MaybePromise.ts @@ -0,0 +1 @@ +export type MaybePromise = T | Promise; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts new file mode 100644 index 000000000000..59f9e658867b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts @@ -0,0 +1,9 @@ +export type addQuestionMarksToNullableProperties = { + [K in OptionalKeys]?: T[K]; +} & Pick>; + +export type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] ? K : never; +}[keyof T]; + +export type RequiredKeys = Exclude>; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/createIdentitySchemaCreator.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/createIdentitySchemaCreator.ts new file mode 100644 index 000000000000..9aa4ed5029a0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/createIdentitySchemaCreator.ts @@ -0,0 +1,21 @@ +import { getSchemaUtils } from "../builders/schema-utils/index.js"; +import type { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType } from "../Schema.js"; +import { maybeSkipValidation } from "./maybeSkipValidation.js"; + +export function createIdentitySchemaCreator( + schemaType: SchemaType, + validate: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): () => Schema { + return () => { + const baseSchema: BaseSchema = { + parse: validate, + json: validate, + getType: () => schemaType, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/entries.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/entries.ts new file mode 100644 index 000000000000..2d5c93d657ce --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/entries.ts @@ -0,0 +1,3 @@ +export function entries(object: T): [keyof T, T[keyof T]][] { + return Object.entries(object) as [keyof T, T[keyof T]][]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/filterObject.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/filterObject.ts new file mode 100644 index 000000000000..70527d10013b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/filterObject.ts @@ -0,0 +1,13 @@ +export function filterObject(obj: T, keysToInclude: K[]): Pick { + const keysToIncludeSet = new Set(keysToInclude); + return Object.entries(obj).reduce( + (acc, [key, value]) => { + if (keysToIncludeSet.has(key as K)) { + acc[key as K] = value as T[K]; + } + return acc; + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, + {} as Pick, + ); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/getErrorMessageForIncorrectType.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/getErrorMessageForIncorrectType.ts new file mode 100644 index 000000000000..1a5c31027ce9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/getErrorMessageForIncorrectType.ts @@ -0,0 +1,25 @@ +export function getErrorMessageForIncorrectType(value: unknown, expectedType: string): string { + return `Expected ${expectedType}. Received ${getTypeAsString(value)}.`; +} + +function getTypeAsString(value: unknown): string { + if (Array.isArray(value)) { + return "list"; + } + if (value === null) { + return "null"; + } + if (value instanceof BigInt) { + return "BigInt"; + } + switch (typeof value) { + case "string": + return `"${value}"`; + case "bigint": + case "number": + case "boolean": + case "undefined": + return `${value}`; + } + return typeof value; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/isPlainObject.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/isPlainObject.ts new file mode 100644 index 000000000000..db82a722c35b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/isPlainObject.ts @@ -0,0 +1,17 @@ +// borrowed from https://github.com/lodash/lodash/blob/master/isPlainObject.js +export function isPlainObject(value: unknown): value is Record { + if (typeof value !== "object" || value === null) { + return false; + } + + if (Object.getPrototypeOf(value) === null) { + return true; + } + + let proto = value; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + + return Object.getPrototypeOf(value) === proto; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/keys.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/keys.ts new file mode 100644 index 000000000000..2e0930e2d70b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/keys.ts @@ -0,0 +1,3 @@ +export function keys(object: T): (keyof T)[] { + return Object.keys(object) as (keyof T)[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/maybeSkipValidation.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/maybeSkipValidation.ts new file mode 100644 index 000000000000..f32d4525136d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/maybeSkipValidation.ts @@ -0,0 +1,38 @@ +import type { BaseSchema, MaybeValid, SchemaOptions } from "../Schema.js"; + +export function maybeSkipValidation, Raw, Parsed>(schema: S): S { + return { + ...schema, + json: transformAndMaybeSkipValidation(schema.json), + parse: transformAndMaybeSkipValidation(schema.parse), + }; +} + +function transformAndMaybeSkipValidation( + transform: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): (value: unknown, opts?: SchemaOptions) => MaybeValid { + return (value, opts): MaybeValid => { + const transformed = transform(value, opts); + const { skipValidation = false } = opts ?? {}; + if (!transformed.ok && skipValidation) { + // biome-ignore lint/suspicious/noConsole: allow console + console.warn( + [ + "Failed to validate.", + ...transformed.errors.map( + (error) => + " - " + + (error.path.length > 0 ? `${error.path.join(".")}: ${error.message}` : error.message), + ), + ].join("\n"), + ); + + return { + ok: true, + value: value as T, + }; + } else { + return transformed; + } + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/partition.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/partition.ts new file mode 100644 index 000000000000..f58d6f3d35f3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/schemas/utils/partition.ts @@ -0,0 +1,12 @@ +export function partition(items: readonly T[], predicate: (item: T) => boolean): [T[], T[]] { + const trueItems: T[] = [], + falseItems: T[] = []; + for (const item of items) { + if (predicate(item)) { + trueItems.push(item); + } else { + falseItems.push(item); + } + } + return [trueItems, falseItems]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/encodePathParam.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/encodePathParam.ts new file mode 100644 index 000000000000..19b901244218 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/encodePathParam.ts @@ -0,0 +1,18 @@ +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/exhaustive/serde-layer-zurg/src/core/url/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/index.ts new file mode 100644 index 000000000000..f2e0fa2d2221 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/index.ts @@ -0,0 +1,3 @@ +export { encodePathParam } from "./encodePathParam.js"; +export { join } from "./join.js"; +export { toQueryString } from "./qs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/join.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/join.ts new file mode 100644 index 000000000000..7ca7daef094d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/join.ts @@ -0,0 +1,79 @@ +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/exhaustive/serde-layer-zurg/src/core/url/qs.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/qs.ts new file mode 100644 index 000000000000..13e89be9d9a6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/core/url/qs.ts @@ -0,0 +1,74 @@ +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/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveError.ts new file mode 100644 index 000000000000..d2e515f5d4e2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveError.ts @@ -0,0 +1,58 @@ +// 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 SeedExhaustiveError 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/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveTimeoutError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveTimeoutError.ts new file mode 100644 index 000000000000..82a3cb8de266 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/SeedExhaustiveTimeoutError.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +export class SeedExhaustiveTimeoutError 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/exhaustive/serde-layer-zurg/src/errors/handleNonStatusCodeError.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/handleNonStatusCodeError.ts new file mode 100644 index 000000000000..deb55c602654 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/handleNonStatusCodeError.ts @@ -0,0 +1,37 @@ +// 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.SeedExhaustiveError({ + statusCode: error.statusCode, + body: error.rawBody, + rawResponse: rawResponse, + }); + case "body-is-null": + throw new errors.SeedExhaustiveError({ + statusCode: error.statusCode, + rawResponse: rawResponse, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(`Timeout exceeded when calling ${method} ${path}.`); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: error.errorMessage, + rawResponse: rawResponse, + }); + default: + throw new errors.SeedExhaustiveError({ + message: "Unknown error", + rawResponse: rawResponse, + }); + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/index.ts new file mode 100644 index 000000000000..2e2853073a94 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/errors/index.ts @@ -0,0 +1,2 @@ +export { SeedExhaustiveError } from "./SeedExhaustiveError.js"; +export { SeedExhaustiveTimeoutError } from "./SeedExhaustiveTimeoutError.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/exports.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/exports.ts new file mode 100644 index 000000000000..7b70ee14fc02 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/exports.ts @@ -0,0 +1 @@ +export * from "./core/exports.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/index.ts new file mode 100644 index 000000000000..9f0bdd34e0d5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/index.ts @@ -0,0 +1,6 @@ +export * as SeedExhaustive from "./api/index.js"; +export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; +export { SeedExhaustiveClient } from "./Client.js"; +export { SeedExhaustiveError, SeedExhaustiveTimeoutError } from "./errors/index.js"; +export * from "./exports.js"; +export * as serialization from "./serialization/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts new file mode 100644 index 000000000000..e37f244ea60c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfObjects.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnListOfObjects.Request.Raw, + SeedExhaustive.types.ObjectWithRequiredField[] +> = core.serialization.list(ObjectWithRequiredField); + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw[]; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnListOfObjects.Response.Raw, + SeedExhaustive.types.ObjectWithRequiredField[] +> = core.serialization.list(ObjectWithRequiredField); + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts new file mode 100644 index 000000000000..668b72ec3402 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnListOfPrimitives.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnListOfPrimitives.Request.Raw, + string[] +> = core.serialization.list(core.serialization.string()); + +export declare namespace Request { + export type Raw = string[]; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnListOfPrimitives.Response.Raw, + string[] +> = core.serialization.list(core.serialization.string()); + +export declare namespace Response { + export type Raw = string[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts new file mode 100644 index 000000000000..242978c11a34 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapOfPrimToObject.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnMapOfPrimToObject.Request.Raw, + Record +> = core.serialization.record(core.serialization.string(), ObjectWithRequiredField); + +export declare namespace Request { + export type Raw = Record; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnMapOfPrimToObject.Response.Raw, + Record +> = core.serialization.record(core.serialization.string(), ObjectWithRequiredField); + +export declare namespace Response { + export type Raw = Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts new file mode 100644 index 000000000000..9003c1ac3676 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnMapPrimToPrim.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnMapPrimToPrim.Request.Raw, + Record +> = core.serialization.record(core.serialization.string(), core.serialization.string()); + +export declare namespace Request { + export type Raw = Record; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnMapPrimToPrim.Response.Raw, + Record +> = core.serialization.record(core.serialization.string(), core.serialization.string()); + +export declare namespace Response { + export type Raw = Record; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts new file mode 100644 index 000000000000..b7e20374f571 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnOptional.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import type * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnOptional.Request.Raw, + SeedExhaustive.types.ObjectWithRequiredField | undefined +> = ObjectWithRequiredField.optional(); + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw | null | undefined; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnOptional.Response.Raw, + SeedExhaustive.types.ObjectWithRequiredField | undefined +> = ObjectWithRequiredField.optional(); + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw | null | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts new file mode 100644 index 000000000000..4792425484f1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfObjects.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithRequiredField } from "../../../../types/resources/object/types/ObjectWithRequiredField.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnSetOfObjects.Request.Raw, + SeedExhaustive.types.ObjectWithRequiredField[] +> = core.serialization.list(ObjectWithRequiredField); + +export declare namespace Request { + export type Raw = ObjectWithRequiredField.Raw[]; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnSetOfObjects.Response.Raw, + SeedExhaustive.types.ObjectWithRequiredField[] +> = core.serialization.list(ObjectWithRequiredField); + +export declare namespace Response { + export type Raw = ObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts new file mode 100644 index 000000000000..53931f5e393e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/getAndReturnSetOfPrimitives.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.container.getAndReturnSetOfPrimitives.Request.Raw, + Set +> = core.serialization.set(core.serialization.string()); + +export declare namespace Request { + export type Raw = string[]; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.container.getAndReturnSetOfPrimitives.Response.Raw, + Set +> = core.serialization.set(core.serialization.string()); + +export declare namespace Response { + export type Raw = string[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/index.ts new file mode 100644 index 000000000000..1972f5b270f4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/client/index.ts @@ -0,0 +1,7 @@ +export * as getAndReturnListOfObjects from "./getAndReturnListOfObjects.js"; +export * as getAndReturnListOfPrimitives from "./getAndReturnListOfPrimitives.js"; +export * as getAndReturnMapOfPrimToObject from "./getAndReturnMapOfPrimToObject.js"; +export * as getAndReturnMapPrimToPrim from "./getAndReturnMapPrimToPrim.js"; +export * as getAndReturnOptional from "./getAndReturnOptional.js"; +export * as getAndReturnSetOfObjects from "./getAndReturnSetOfObjects.js"; +export * as getAndReturnSetOfPrimitives from "./getAndReturnSetOfPrimitives.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/container/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts new file mode 100644 index 000000000000..e053119b972c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/index.ts @@ -0,0 +1,2 @@ +export * as testDelete from "./testDelete.js"; +export * as testGet from "./testGet.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts new file mode 100644 index 000000000000..b6273278aa1f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testDelete.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.boolean(); + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts new file mode 100644 index 000000000000..a642aa120d8b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/client/testGet.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/httpMethods/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/index.ts new file mode 100644 index 000000000000..cd7e640937da --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/index.ts @@ -0,0 +1,8 @@ +export * as container from "./container/index.js"; +export * as httpMethods from "./httpMethods/index.js"; +export * as object from "./object/index.js"; +export * as params from "./params/index.js"; +export * as primitive from "./primitive/index.js"; +export * as put from "./put/index.js"; +export * from "./put/types/index.js"; +export * as urls from "./urls/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts new file mode 100644 index 000000000000..60f40db3f33b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/getAndReturnNestedWithRequiredFieldAsList.ts @@ -0,0 +1,15 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { NestedObjectWithRequiredField } from "../../../../types/resources/object/types/NestedObjectWithRequiredField.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.object.getAndReturnNestedWithRequiredFieldAsList.Request.Raw, + SeedExhaustive.types.NestedObjectWithRequiredField[] +> = core.serialization.list(NestedObjectWithRequiredField); + +export declare namespace Request { + export type Raw = NestedObjectWithRequiredField.Raw[]; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/index.ts new file mode 100644 index 000000000000..d00c4c582910 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/client/index.ts @@ -0,0 +1 @@ +export * as getAndReturnNestedWithRequiredFieldAsList from "./getAndReturnNestedWithRequiredFieldAsList.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts new file mode 100644 index 000000000000..c82dda746e5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithInlinePath.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts new file mode 100644 index 000000000000..da8888318c5b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/getWithPath.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/index.ts new file mode 100644 index 000000000000..b30603cae602 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/index.ts @@ -0,0 +1,4 @@ +export * as getWithInlinePath from "./getWithInlinePath.js"; +export * as getWithPath from "./getWithPath.js"; +export * as modifyWithInlinePath from "./modifyWithInlinePath.js"; +export * as modifyWithPath from "./modifyWithPath.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts new file mode 100644 index 000000000000..b84b3f790f22 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithInlinePath.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.params.modifyWithInlinePath.Response.Raw, + string +> = core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts new file mode 100644 index 000000000000..8369d9dae207 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/client/modifyWithPath.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/params/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts new file mode 100644 index 000000000000..eb7fdb1065fb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBase64.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnBase64.Request.Raw, + string +> = core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnBase64.Response.Raw, + string +> = core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts new file mode 100644 index 000000000000..f51fb5f9e891 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnBool.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.boolean(); + +export declare namespace Request { + export type Raw = boolean; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnBool.Response.Raw, + boolean +> = core.serialization.boolean(); + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts new file mode 100644 index 000000000000..08ac416d1caf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDate.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnDate.Response.Raw, + string +> = core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts new file mode 100644 index 000000000000..a80c46b7f418 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDatetime.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnDatetime.Request.Raw, + Date +> = core.serialization.date(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnDatetime.Response.Raw, + Date +> = core.serialization.date(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts new file mode 100644 index 000000000000..bba9c504ff71 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnDouble.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnDouble.Request.Raw, + number +> = core.serialization.number(); + +export declare namespace Request { + export type Raw = number; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnDouble.Response.Raw, + number +> = core.serialization.number(); + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts new file mode 100644 index 000000000000..552fe3843ef9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnInt.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.number(); + +export declare namespace Request { + export type Raw = number; +} + +export const Response: core.serialization.Schema = + core.serialization.number(); + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts new file mode 100644 index 000000000000..191eda735e47 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnLong.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.number(); + +export declare namespace Request { + export type Raw = number; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnLong.Response.Raw, + number +> = core.serialization.number(); + +export declare namespace Response { + export type Raw = number; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts new file mode 100644 index 000000000000..f75bf59ba909 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnString.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnString.Request.Raw, + string +> = core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnString.Response.Raw, + string +> = core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts new file mode 100644 index 000000000000..64875010a017 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/getAndReturnUuid.ts @@ -0,0 +1,20 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} + +export const Response: core.serialization.Schema< + serializers.endpoints.primitive.getAndReturnUuid.Response.Raw, + string +> = core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/index.ts new file mode 100644 index 000000000000..9805ab86b129 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/client/index.ts @@ -0,0 +1,9 @@ +export * as getAndReturnBase64 from "./getAndReturnBase64.js"; +export * as getAndReturnBool from "./getAndReturnBool.js"; +export * as getAndReturnDate from "./getAndReturnDate.js"; +export * as getAndReturnDatetime from "./getAndReturnDatetime.js"; +export * as getAndReturnDouble from "./getAndReturnDouble.js"; +export * as getAndReturnInt from "./getAndReturnInt.js"; +export * as getAndReturnLong from "./getAndReturnLong.js"; +export * as getAndReturnString from "./getAndReturnString.js"; +export * as getAndReturnUuid from "./getAndReturnUuid.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/primitive/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts new file mode 100644 index 000000000000..47e249372cff --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCategory.ts @@ -0,0 +1,14 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ErrorCategory: core.serialization.Schema< + serializers.endpoints.ErrorCategory.Raw, + SeedExhaustive.endpoints.ErrorCategory +> = core.serialization.enum_(["API_ERROR", "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR"]); + +export declare namespace ErrorCategory { + export type Raw = "API_ERROR" | "AUTHENTICATION_ERROR" | "INVALID_REQUEST_ERROR"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts new file mode 100644 index 000000000000..a36b10d4b362 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/ErrorCode.ts @@ -0,0 +1,37 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ErrorCode: core.serialization.Schema< + serializers.endpoints.ErrorCode.Raw, + SeedExhaustive.endpoints.ErrorCode +> = core.serialization.enum_([ + "INTERNAL_SERVER_ERROR", + "UNAUTHORIZED", + "FORBIDDEN", + "BAD_REQUEST", + "CONFLICT", + "GONE", + "UNPROCESSABLE_ENTITY", + "NOT_IMPLEMENTED", + "BAD_GATEWAY", + "SERVICE_UNAVAILABLE", + "Unknown", +]); + +export declare namespace ErrorCode { + export type Raw = + | "INTERNAL_SERVER_ERROR" + | "UNAUTHORIZED" + | "FORBIDDEN" + | "BAD_REQUEST" + | "CONFLICT" + | "GONE" + | "UNPROCESSABLE_ENTITY" + | "NOT_IMPLEMENTED" + | "BAD_GATEWAY" + | "SERVICE_UNAVAILABLE" + | "Unknown"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/Error_.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/Error_.ts new file mode 100644 index 000000000000..59ab664d43a2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/Error_.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ErrorCategory } from "./ErrorCategory.js"; +import { ErrorCode } from "./ErrorCode.js"; + +export const Error_: core.serialization.ObjectSchema< + serializers.endpoints.Error_.Raw, + SeedExhaustive.endpoints.Error_ +> = core.serialization.object({ + category: ErrorCategory, + code: ErrorCode, + detail: core.serialization.string().optional(), + field: core.serialization.string().optional(), +}); + +export declare namespace Error_ { + export interface Raw { + category: ErrorCategory.Raw; + code: ErrorCode.Raw; + detail?: string | null; + field?: string | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts new file mode 100644 index 000000000000..17af0986c151 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/PutResponse.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { Error_ } from "./Error_.js"; + +export const PutResponse: core.serialization.ObjectSchema< + serializers.endpoints.PutResponse.Raw, + SeedExhaustive.endpoints.PutResponse +> = core.serialization.object({ + errors: core.serialization.list(Error_).optional(), +}); + +export declare namespace PutResponse { + export interface Raw { + errors?: Error_.Raw[] | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/index.ts new file mode 100644 index 000000000000..3293f66bb34c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/put/types/index.ts @@ -0,0 +1,4 @@ +export * from "./Error_.js"; +export * from "./ErrorCategory.js"; +export * from "./ErrorCode.js"; +export * from "./PutResponse.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/index.ts new file mode 100644 index 000000000000..66e101cdc2d7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/index.ts @@ -0,0 +1,4 @@ +export * as noEndingSlash from "./noEndingSlash.js"; +export * as withEndingSlash from "./withEndingSlash.js"; +export * as withMixedCase from "./withMixedCase.js"; +export * as withUnderscores from "./withUnderscores.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts new file mode 100644 index 000000000000..2b0a2cfbfa53 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/noEndingSlash.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts new file mode 100644 index 000000000000..de39ec7a2a0d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withEndingSlash.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts new file mode 100644 index 000000000000..ffedf860047a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withMixedCase.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts new file mode 100644 index 000000000000..8d308fd3e42f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/client/withUnderscores.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/endpoints/resources/urls/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts new file mode 100644 index 000000000000..5d81697cccfb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/BadObjectRequestInfo.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../api/index.js"; +import * as core from "../../../../core/index.js"; +import type * as serializers from "../../../index.js"; + +export const BadObjectRequestInfo: core.serialization.ObjectSchema< + serializers.BadObjectRequestInfo.Raw, + SeedExhaustive.BadObjectRequestInfo +> = core.serialization.object({ + message: core.serialization.string(), +}); + +export declare namespace BadObjectRequestInfo { + export interface Raw { + message: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/index.ts new file mode 100644 index 000000000000..b10afa3b7749 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/generalErrors/types/index.ts @@ -0,0 +1 @@ +export * from "./BadObjectRequestInfo.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/index.ts new file mode 100644 index 000000000000..20b614a89e2f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/index.ts @@ -0,0 +1,9 @@ +export * as endpoints from "./endpoints/index.js"; +export * as generalErrors from "./generalErrors/index.js"; +export * from "./generalErrors/types/index.js"; +export * from "./inlinedRequests/client/requests/index.js"; +export * as inlinedRequests from "./inlinedRequests/index.js"; +export * as noAuth from "./noAuth/index.js"; +export * as noReqBody from "./noReqBody/index.js"; +export * as reqWithHeaders from "./reqWithHeaders/index.js"; +export * as types from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/index.ts new file mode 100644 index 000000000000..195f9aa8a846 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/index.ts @@ -0,0 +1 @@ +export * from "./requests/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts new file mode 100644 index 000000000000..d8c20c33277a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/PostWithObjectBody.ts @@ -0,0 +1,23 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../api/index.js"; +import * as core from "../../../../../core/index.js"; +import type * as serializers from "../../../../index.js"; +import { ObjectWithOptionalField } from "../../../types/resources/object/types/ObjectWithOptionalField.js"; + +export const PostWithObjectBody: core.serialization.Schema< + serializers.PostWithObjectBody.Raw, + SeedExhaustive.PostWithObjectBody +> = core.serialization.object({ + string: core.serialization.string(), + integer: core.serialization.number(), + nestedObject: core.serialization.property("NestedObject", ObjectWithOptionalField), +}); + +export declare namespace PostWithObjectBody { + export interface Raw { + string: string; + integer: number; + NestedObject: ObjectWithOptionalField.Raw; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/index.ts new file mode 100644 index 000000000000..d62a81ce61fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/client/requests/index.ts @@ -0,0 +1 @@ +export { PostWithObjectBody } from "./PostWithObjectBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/inlinedRequests/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/index.ts new file mode 100644 index 000000000000..549edcdba7f2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/index.ts @@ -0,0 +1 @@ +export * as postWithNoAuth from "./postWithNoAuth.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/postWithNoAuth.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/postWithNoAuth.ts new file mode 100644 index 000000000000..8e7617816edf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/client/postWithNoAuth.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../core/index.js"; +import type * as serializers from "../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.boolean(); + +export declare namespace Response { + export type Raw = boolean; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noAuth/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/index.ts new file mode 100644 index 000000000000..b62616349ec6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/index.ts @@ -0,0 +1 @@ +export * as postWithNoRequestBody from "./postWithNoRequestBody.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts new file mode 100644 index 000000000000..c0cba3668202 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/client/postWithNoRequestBody.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../core/index.js"; +import type * as serializers from "../../../index.js"; + +export const Response: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Response { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/noReqBody/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts new file mode 100644 index 000000000000..70ac5077c6f2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/getWithCustomHeader.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../../../../core/index.js"; +import type * as serializers from "../../../index.js"; + +export const Request: core.serialization.Schema = + core.serialization.string(); + +export declare namespace Request { + export type Raw = string; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/index.ts new file mode 100644 index 000000000000..fb7f25afb170 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/client/index.ts @@ -0,0 +1 @@ +export * as getWithCustomHeader from "./getWithCustomHeader.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/index.ts new file mode 100644 index 000000000000..914b8c3c7214 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/reqWithHeaders/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/index.ts new file mode 100644 index 000000000000..e445af0d831e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/index.ts @@ -0,0 +1 @@ +export * from "./resources/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts new file mode 100644 index 000000000000..ab35ed9023bd --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/ObjectWithDocs.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ObjectWithDocs: core.serialization.ObjectSchema< + serializers.types.ObjectWithDocs.Raw, + SeedExhaustive.types.ObjectWithDocs +> = core.serialization.object({ + string: core.serialization.string(), +}); + +export declare namespace ObjectWithDocs { + export interface Raw { + string: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/index.ts new file mode 100644 index 000000000000..3eedfcba912c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/docs/types/index.ts @@ -0,0 +1 @@ +export * from "./ObjectWithDocs.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/WeatherReport.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/WeatherReport.ts new file mode 100644 index 000000000000..96a37eda2a46 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/WeatherReport.ts @@ -0,0 +1,14 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const WeatherReport: core.serialization.Schema< + serializers.types.WeatherReport.Raw, + SeedExhaustive.types.WeatherReport +> = core.serialization.enum_(["SUNNY", "CLOUDY", "RAINING", "SNOWING"]); + +export declare namespace WeatherReport { + export type Raw = "SUNNY" | "CLOUDY" | "RAINING" | "SNOWING"; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/index.ts new file mode 100644 index 000000000000..3b2d63911a15 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/enum/types/index.ts @@ -0,0 +1 @@ +export * from "./WeatherReport.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/index.ts new file mode 100644 index 000000000000..52175f3cee16 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/index.ts @@ -0,0 +1,8 @@ +export * as docs from "./docs/index.js"; +export * from "./docs/types/index.js"; +export * as enum_ from "./enum/index.js"; +export * from "./enum/types/index.js"; +export * as object from "./object/index.js"; +export * from "./object/types/index.js"; +export * as union from "./union/index.js"; +export * from "./union/types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/DoubleOptional.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/DoubleOptional.ts new file mode 100644 index 000000000000..e8e0fd387243 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/DoubleOptional.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { OptionalAlias } from "./OptionalAlias.js"; + +export const DoubleOptional: core.serialization.ObjectSchema< + serializers.types.DoubleOptional.Raw, + SeedExhaustive.types.DoubleOptional +> = core.serialization.object({ + optionalAlias: OptionalAlias.optional(), +}); + +export declare namespace DoubleOptional { + export interface Raw { + optionalAlias?: (OptionalAlias.Raw | undefined) | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts new file mode 100644 index 000000000000..c754d9aba654 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithOptionalField.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithOptionalField } from "./ObjectWithOptionalField.js"; + +export const NestedObjectWithOptionalField: core.serialization.ObjectSchema< + serializers.types.NestedObjectWithOptionalField.Raw, + SeedExhaustive.types.NestedObjectWithOptionalField +> = core.serialization.object({ + string: core.serialization.string().optional(), + nestedObject: core.serialization.property("NestedObject", ObjectWithOptionalField.optional()), +}); + +export declare namespace NestedObjectWithOptionalField { + export interface Raw { + string?: string | null; + NestedObject?: ObjectWithOptionalField.Raw | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts new file mode 100644 index 000000000000..cd443843bf86 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/NestedObjectWithRequiredField.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { ObjectWithOptionalField } from "./ObjectWithOptionalField.js"; + +export const NestedObjectWithRequiredField: core.serialization.ObjectSchema< + serializers.types.NestedObjectWithRequiredField.Raw, + SeedExhaustive.types.NestedObjectWithRequiredField +> = core.serialization.object({ + string: core.serialization.string(), + nestedObject: core.serialization.property("NestedObject", ObjectWithOptionalField), +}); + +export declare namespace NestedObjectWithRequiredField { + export interface Raw { + string: string; + NestedObject: ObjectWithOptionalField.Raw; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts new file mode 100644 index 000000000000..c7426a9b9847 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithMapOfMap.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ObjectWithMapOfMap: core.serialization.ObjectSchema< + serializers.types.ObjectWithMapOfMap.Raw, + SeedExhaustive.types.ObjectWithMapOfMap +> = core.serialization.object({ + map: core.serialization.record( + core.serialization.string(), + core.serialization.record(core.serialization.string(), core.serialization.string()), + ), +}); + +export declare namespace ObjectWithMapOfMap { + export interface Raw { + map: Record>; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts new file mode 100644 index 000000000000..44a200707fd8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithOptionalField.ts @@ -0,0 +1,42 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ObjectWithOptionalField: core.serialization.ObjectSchema< + serializers.types.ObjectWithOptionalField.Raw, + SeedExhaustive.types.ObjectWithOptionalField +> = core.serialization.object({ + string: core.serialization.string().optional(), + integer: core.serialization.number().optional(), + long: core.serialization.number().optional(), + double: core.serialization.number().optional(), + bool: core.serialization.boolean().optional(), + datetime: core.serialization.date().optional(), + date: core.serialization.string().optional(), + uuid: core.serialization.string().optional(), + base64: core.serialization.string().optional(), + list: core.serialization.list(core.serialization.string()).optional(), + set: core.serialization.set(core.serialization.string()).optional(), + map: core.serialization.record(core.serialization.number(), core.serialization.string()).optional(), + bigint: core.serialization.string().optional(), +}); + +export declare namespace ObjectWithOptionalField { + export interface Raw { + string?: string | null; + integer?: number | null; + long?: number | null; + double?: number | null; + bool?: boolean | null; + datetime?: string | null; + date?: string | null; + uuid?: string | null; + base64?: string | null; + list?: string[] | null; + set?: string[] | null; + map?: Record | null; + bigint?: string | null; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts new file mode 100644 index 000000000000..b6887e41337d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/ObjectWithRequiredField.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const ObjectWithRequiredField: core.serialization.ObjectSchema< + serializers.types.ObjectWithRequiredField.Raw, + SeedExhaustive.types.ObjectWithRequiredField +> = core.serialization.object({ + string: core.serialization.string(), +}); + +export declare namespace ObjectWithRequiredField { + export interface Raw { + string: string; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/OptionalAlias.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/OptionalAlias.ts new file mode 100644 index 000000000000..a7798d5904bf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/OptionalAlias.ts @@ -0,0 +1,14 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const OptionalAlias: core.serialization.Schema< + serializers.types.OptionalAlias.Raw, + SeedExhaustive.types.OptionalAlias +> = core.serialization.string().optional(); + +export declare namespace OptionalAlias { + export type Raw = string | null | undefined; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/index.ts new file mode 100644 index 000000000000..c3c65c311b82 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/object/types/index.ts @@ -0,0 +1,7 @@ +export * from "./DoubleOptional.js"; +export * from "./NestedObjectWithOptionalField.js"; +export * from "./NestedObjectWithRequiredField.js"; +export * from "./ObjectWithMapOfMap.js"; +export * from "./ObjectWithOptionalField.js"; +export * from "./ObjectWithRequiredField.js"; +export * from "./OptionalAlias.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Animal.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Animal.ts new file mode 100644 index 000000000000..83b27d0bb69d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Animal.ts @@ -0,0 +1,30 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; +import { Cat } from "./Cat.js"; +import { Dog } from "./Dog.js"; + +export const Animal: core.serialization.Schema = + core.serialization + .union("animal", { + dog: Dog, + cat: Cat, + }) + .transform({ + transform: (value) => value, + untransform: (value) => value, + }); + +export declare namespace Animal { + export type Raw = Animal.Dog | Animal.Cat; + + export interface Dog extends Dog.Raw { + animal: "dog"; + } + + export interface Cat extends Cat.Raw { + animal: "cat"; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Cat.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Cat.ts new file mode 100644 index 000000000000..16f8a2006e5c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Cat.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Cat: core.serialization.ObjectSchema = + core.serialization.object({ + name: core.serialization.string(), + likesToMeow: core.serialization.boolean(), + }); + +export declare namespace Cat { + export interface Raw { + name: string; + likesToMeow: boolean; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Dog.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Dog.ts new file mode 100644 index 000000000000..271a1f3a4afa --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/Dog.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedExhaustive from "../../../../../../api/index.js"; +import * as core from "../../../../../../core/index.js"; +import type * as serializers from "../../../../../index.js"; + +export const Dog: core.serialization.ObjectSchema = + core.serialization.object({ + name: core.serialization.string(), + likesToWoof: core.serialization.boolean(), + }); + +export declare namespace Dog { + export interface Raw { + name: string; + likesToWoof: boolean; + } +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/index.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/index.ts new file mode 100644 index 000000000000..342e90adc057 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/serialization/resources/types/resources/union/types/index.ts @@ -0,0 +1,3 @@ +export * from "./Animal.js"; +export * from "./Cat.js"; +export * from "./Dog.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/src/version.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/version.ts new file mode 100644 index 000000000000..b643a3e3ea27 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/custom.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/custom.test.ts new file mode 100644 index 000000000000..7f5e031c8396 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/custom.test.ts @@ -0,0 +1,13 @@ +/** + * 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/exhaustive/serde-layer-zurg/tests/mock-server/MockServer.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/MockServer.ts new file mode 100644 index 000000000000..954872157d52 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/MockServer.ts @@ -0,0 +1,29 @@ +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/exhaustive/serde-layer-zurg/tests/mock-server/MockServerPool.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/MockServerPool.ts new file mode 100644 index 000000000000..e1a90f7fb2e3 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/MockServerPool.ts @@ -0,0 +1,106 @@ +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/exhaustive/serde-layer-zurg/tests/mock-server/mockEndpointBuilder.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/mockEndpointBuilder.ts new file mode 100644 index 000000000000..1b0e51079e6b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/mockEndpointBuilder.ts @@ -0,0 +1,227 @@ +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/exhaustive/serde-layer-zurg/tests/mock-server/randomBaseUrl.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/randomBaseUrl.ts new file mode 100644 index 000000000000..031aa6408aca --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/randomBaseUrl.ts @@ -0,0 +1,4 @@ +export function randomBaseUrl(): string { + const randomString = Math.random().toString(36).substring(2, 15); + return `http://${randomString}.localhost`; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/setup.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/setup.ts new file mode 100644 index 000000000000..aeb3a95af7dc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/setup.ts @@ -0,0 +1,10 @@ +import { afterAll, beforeAll } from "vitest"; + +import { mockServerPool } from "./MockServerPool"; + +beforeAll(() => { + mockServerPool.listen(); +}); +afterAll(() => { + mockServerPool.close(); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withFormUrlEncoded.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withFormUrlEncoded.ts new file mode 100644 index 000000000000..e9e6ff2d9cf1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withFormUrlEncoded.ts @@ -0,0 +1,80 @@ +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/exhaustive/serde-layer-zurg/tests/mock-server/withHeaders.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withHeaders.ts new file mode 100644 index 000000000000..6599d2b4a92d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withHeaders.ts @@ -0,0 +1,70 @@ +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/exhaustive/serde-layer-zurg/tests/mock-server/withJson.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withJson.ts new file mode 100644 index 000000000000..b627638b015f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/mock-server/withJson.ts @@ -0,0 +1,158 @@ +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/exhaustive/serde-layer-zurg/tests/setup.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/setup.ts new file mode 100644 index 000000000000..a5651f81ba10 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/setup.ts @@ -0,0 +1,80 @@ +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/exhaustive/serde-layer-zurg/tests/tsconfig.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/tsconfig.json new file mode 100644 index 000000000000..a477df47920c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": null, + "rootDir": "..", + "baseUrl": "..", + "types": ["vitest/globals"] + }, + "include": ["../src", "../tests"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/auth/BasicAuth.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/auth/BasicAuth.test.ts new file mode 100644 index 000000000000..9b5123364c47 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/auth/BasicAuth.test.ts @@ -0,0 +1,92 @@ +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/exhaustive/serde-layer-zurg/tests/unit/auth/BearerToken.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/auth/BearerToken.test.ts new file mode 100644 index 000000000000..7757b87cb97e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/auth/BearerToken.test.ts @@ -0,0 +1,14 @@ +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/exhaustive/serde-layer-zurg/tests/unit/base64.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/base64.test.ts new file mode 100644 index 000000000000..939594ca277b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/base64.test.ts @@ -0,0 +1,53 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/Fetcher.test.ts new file mode 100644 index 000000000000..60df2b5e4824 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/Fetcher.test.ts @@ -0,0 +1,261 @@ +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 reader = 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 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/exhaustive/serde-layer-zurg/tests/unit/fetcher/HttpResponsePromise.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/HttpResponsePromise.test.ts new file mode 100644 index 000000000000..2ec008e581d8 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/HttpResponsePromise.test.ts @@ -0,0 +1,143 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/RawResponse.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/RawResponse.test.ts new file mode 100644 index 000000000000..375ee3f38064 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/RawResponse.test.ts @@ -0,0 +1,34 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/createRequestUrl.test.ts new file mode 100644 index 000000000000..a92f1b5e81d1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/createRequestUrl.test.ts @@ -0,0 +1,163 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/getRequestBody.test.ts new file mode 100644 index 000000000000..8a6c3a57e211 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/getRequestBody.test.ts @@ -0,0 +1,129 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/getResponseBody.test.ts new file mode 100644 index 000000000000..ad6be7fc2c9b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/getResponseBody.test.ts @@ -0,0 +1,97 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/logging.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/logging.test.ts new file mode 100644 index 000000000000..366c9b6ced61 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/logging.test.ts @@ -0,0 +1,517 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/makeRequest.test.ts new file mode 100644 index 000000000000..ea49466a55fc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/makeRequest.test.ts @@ -0,0 +1,54 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/redacting.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/redacting.test.ts new file mode 100644 index 000000000000..d599376b9bcf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/redacting.test.ts @@ -0,0 +1,1115 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/requestWithRetries.test.ts new file mode 100644 index 000000000000..d22661367f4e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/requestWithRetries.test.ts @@ -0,0 +1,230 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/signals.test.ts new file mode 100644 index 000000000000..d7b6d1e63caa --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/signals.test.ts @@ -0,0 +1,69 @@ +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/exhaustive/serde-layer-zurg/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/fetcher/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/logging/logger.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/logging/logger.test.ts new file mode 100644 index 000000000000..2e0b5fe5040c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/logging/logger.test.ts @@ -0,0 +1,454 @@ +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/exhaustive/serde-layer-zurg/tests/unit/schemas/bigint/bigint.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/bigint/bigint.test.ts new file mode 100644 index 000000000000..498f143c7283 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/bigint/bigint.test.ts @@ -0,0 +1,46 @@ +import { bigint } from "../../../../src/core/schemas/builders/bigint"; +import { itJson, itParse, itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("bigint", () => { + itSchema("converts between raw bigint and parsed bigint", bigint(), { + raw: BigInt("9007199254740992"), + parsed: BigInt("9007199254740992"), + }); + + itParse("converts between raw number and parsed bigint", bigint(), { + raw: 10, + parsed: BigInt("10"), + }); + + itParse("converts between raw number and parsed bigint", bigint(), { + raw: BigInt("10"), + parsed: BigInt("10"), + }); + + itJson("converts raw bigint to parsed bigint", bigint(), { + parsed: BigInt("10"), + raw: BigInt("10"), + }); + + itValidateParse("string", bigint(), "42", [ + { + message: 'Expected bigint | number. Received "42".', + path: [], + }, + ]); + + itValidateJson("number", bigint(), 42, [ + { + message: "Expected bigint. Received 42.", + path: [], + }, + ]); + + itValidateJson("string", bigint(), "42", [ + { + message: 'Expected bigint. Received "42".', + path: [], + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/date/date.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/date/date.test.ts new file mode 100644 index 000000000000..2790268a09c6 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/date/date.test.ts @@ -0,0 +1,31 @@ +import { date } from "../../../../src/core/schemas/builders/date"; +import { itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("date", () => { + itSchema("converts between raw ISO string and parsed Date", date(), { + raw: "2022-09-29T05:41:21.939Z", + parsed: new Date("2022-09-29T05:41:21.939Z"), + }); + + itValidateParse("non-string", date(), 42, [ + { + message: "Expected string. Received 42.", + path: [], + }, + ]); + + itValidateParse("non-ISO", date(), "hello world", [ + { + message: 'Expected ISO 8601 date string. Received "hello world".', + path: [], + }, + ]); + + itValidateJson("non-Date", date(), "hello", [ + { + message: 'Expected Date object. Received "hello".', + path: [], + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/enum/enum.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/enum/enum.test.ts new file mode 100644 index 000000000000..d1707325b29b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/enum/enum.test.ts @@ -0,0 +1,30 @@ +import { enum_ } from "../../../../src/core/schemas/builders/enum"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("enum", () => { + itSchemaIdentity(enum_(["A", "B", "C"]), "A"); + + itSchemaIdentity(enum_(["A", "B", "C"]), "D" as any, { + opts: { allowUnrecognizedEnumValues: true }, + }); + + itValidate("invalid enum", enum_(["A", "B", "C"]), "D", [ + { + message: 'Expected enum. Received "D".', + path: [], + }, + ]); + + itValidate( + "non-string", + enum_(["A", "B", "C"]), + [], + [ + { + message: "Expected string. Received list.", + path: [], + }, + ], + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazy.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazy.test.ts new file mode 100644 index 000000000000..a82ace4a08c0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazy.test.ts @@ -0,0 +1,57 @@ +import { lazy, list, object, string } from "../../../../src/core/schemas/builders"; +import type { Schema } from "../../../../src/core/schemas/Schema"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("lazy", () => { + it("doesn't run immediately", () => { + let wasRun = false; + lazy(() => { + wasRun = true; + return string(); + }); + expect(wasRun).toBe(false); + }); + + it("only runs first time", async () => { + let count = 0; + const schema = lazy(() => { + count++; + return string(); + }); + await schema.parse("hello"); + await schema.json("world"); + expect(count).toBe(1); + }); + + itSchemaIdentity( + lazy(() => object({})), + { foo: "hello" }, + { + title: "passes opts through", + opts: { unrecognizedObjectKeys: "passthrough" }, + }, + ); + + itSchemaIdentity( + lazy(() => object({ foo: string() })), + { foo: "hello" }, + ); + + // eslint-disable-next-line vi/expect-expect + it("self-referencial schema doesn't compile", () => { + () => { + // @ts-expect-error + const a = lazy(() => object({ foo: a })); + }; + }); + + // eslint-disable-next-line vi/expect-expect + it("self-referencial compiles with explicit type", () => { + () => { + interface TreeNode { + children: TreeNode[]; + } + const TreeNode: Schema = lazy(() => object({ children: list(TreeNode) })); + }; + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazyObject.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazyObject.test.ts new file mode 100644 index 000000000000..9b443671a71f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/lazyObject.test.ts @@ -0,0 +1,18 @@ +import { lazyObject, number, object, string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("lazy", () => { + itSchemaIdentity( + lazyObject(() => object({ foo: string() })), + { foo: "hello" }, + ); + + itSchemaIdentity( + lazyObject(() => object({ foo: string() })).extend(object({ bar: number() })), + { + foo: "hello", + bar: 42, + }, + { title: "returned schema has object utils" }, + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/a.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/a.ts new file mode 100644 index 000000000000..8b7d5e40cfaf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/a.ts @@ -0,0 +1,7 @@ +import { object } from "../../../../../src/core/schemas/builders/object"; +import { schemaB } from "./b"; + +// @ts-expect-error +export const schemaA = object({ + b: schemaB, +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/b.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/b.ts new file mode 100644 index 000000000000..fb219d54c8e5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/lazy/recursive/b.ts @@ -0,0 +1,8 @@ +import { object } from "../../../../../src/core/schemas/builders/object"; +import { optional } from "../../../../../src/core/schemas/builders/schema-utils"; +import { schemaA } from "./a"; + +// @ts-expect-error +export const schemaB = object({ + a: optional(schemaA), +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/list/list.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/list/list.test.ts new file mode 100644 index 000000000000..108789b7317d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/list/list.test.ts @@ -0,0 +1,41 @@ +import { list, object, property, string } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("list", () => { + itSchemaIdentity(list(string()), ["hello", "world"], { + title: "functions as identity when item type is primitive", + }); + + itSchema( + "converts objects correctly", + list( + object({ + helloWorld: property("hello_world", string()), + }), + ), + { + raw: [{ hello_world: "123" }], + parsed: [{ helloWorld: "123" }], + }, + ); + + itValidate("not a list", list(string()), 42, [ + { + path: [], + message: "Expected list. Received 42.", + }, + ]); + + itValidate( + "invalid item type", + list(string()), + [42], + [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ], + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/literals/stringLiteral.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/literals/stringLiteral.test.ts new file mode 100644 index 000000000000..fa6c88873c61 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/literals/stringLiteral.test.ts @@ -0,0 +1,21 @@ +import { stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("stringLiteral", () => { + itSchemaIdentity(stringLiteral("A"), "A"); + + itValidate("incorrect string", stringLiteral("A"), "B", [ + { + path: [], + message: 'Expected "A". Received "B".', + }, + ]); + + itValidate("non-string", stringLiteral("A"), 42, [ + { + path: [], + message: 'Expected "A". Received 42.', + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object-like/withParsedProperties.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object-like/withParsedProperties.test.ts new file mode 100644 index 000000000000..b18bc9d3e5df --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object-like/withParsedProperties.test.ts @@ -0,0 +1,57 @@ +import { object, property, string, stringLiteral } from "../../../../src/core/schemas/builders"; + +describe("withParsedProperties", () => { + it("Added properties included on parsed object", async () => { + const schema = object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }).withParsedProperties({ + printFoo: (parsed) => () => parsed.foo, + printHelloWorld: () => () => "Hello world", + helloWorld: "Hello world", + }); + + const parsed = await schema.parse({ raw_foo: "value of foo", bar: "bar" }); + if (!parsed.ok) { + throw new Error("Failed to parse"); + } + expect(parsed.value.printFoo()).toBe("value of foo"); + expect(parsed.value.printHelloWorld()).toBe("Hello world"); + expect(parsed.value.helloWorld).toBe("Hello world"); + }); + + it("Added property is removed on raw object", async () => { + const schema = object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }).withParsedProperties({ + printFoo: (parsed) => () => parsed.foo, + }); + + const original = { raw_foo: "value of foo", bar: "bar" } as const; + const parsed = await schema.parse(original); + if (!parsed.ok) { + throw new Error("Failed to parse()"); + } + + const raw = await schema.json(parsed.value); + + if (!raw.ok) { + throw new Error("Failed to json()"); + } + + expect(raw.value).toEqual(original); + }); + + describe("compile", () => { + // eslint-disable-next-line vi/expect-expect + it("doesn't compile with non-object schema", () => { + () => + object({ + foo: string(), + }) + // @ts-expect-error + .withParsedProperties(42); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/extend.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/extend.test.ts new file mode 100644 index 000000000000..b6c2920f4d3e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/extend.test.ts @@ -0,0 +1,89 @@ +import { boolean, object, property, string, stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; + +describe("extend", () => { + itSchemaIdentity( + object({ + foo: string(), + }).extend( + object({ + bar: stringLiteral("bar"), + }), + ), + { + foo: "", + bar: "bar", + } as const, + { + title: "extended properties are included in schema", + }, + ); + + itSchemaIdentity( + object({ + foo: string(), + }) + .extend( + object({ + bar: stringLiteral("bar"), + }), + ) + .extend( + object({ + baz: boolean(), + }), + ), + { + foo: "", + bar: "bar", + baz: true, + } as const, + { + title: "extensions can be extended", + }, + ); + + itSchema( + "converts nested object", + object({ + item: object({ + helloWorld: property("hello_world", string()), + }), + }).extend( + object({ + goodbye: property("goodbye_raw", string()), + }), + ), + { + raw: { item: { hello_world: "yo" }, goodbye_raw: "peace" }, + parsed: { item: { helloWorld: "yo" }, goodbye: "peace" }, + }, + ); + + itSchema( + "extensions work with raw/parsed property name conversions", + object({ + item: property("item_raw", string()), + }).extend( + object({ + goodbye: property("goodbye_raw", string()), + }), + ), + { + raw: { item_raw: "hi", goodbye_raw: "peace" }, + parsed: { item: "hi", goodbye: "peace" }, + }, + ); + + describe("compile", () => { + // eslint-disable-next-line vi/expect-expect + it("doesn't compile with non-object schema", () => { + () => + object({ + foo: string(), + }) + // @ts-expect-error + .extend([]); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/object.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/object.test.ts new file mode 100644 index 000000000000..a8d9fe0a1359 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/object.test.ts @@ -0,0 +1,255 @@ +import { any, number, object, property, string, stringLiteral, unknown } from "../../../../src/core/schemas/builders"; +import { itJson, itParse, itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("object", () => { + itSchemaIdentity( + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { + foo: "", + bar: "bar", + }, + { + title: "functions as identity when values are primitives and property() isn't used", + }, + ); + + itSchema( + "uses raw key from property()", + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { raw_foo: "foo", bar: "bar" }, + parsed: { foo: "foo", bar: "bar" }, + }, + ); + + itSchema( + "keys with unknown type can be omitted", + object({ + foo: unknown(), + }), + { + raw: {}, + parsed: {}, + }, + ); + + itSchema( + "keys with any type can be omitted", + object({ + foo: any(), + }), + { + raw: {}, + parsed: {}, + }, + ); + + describe("unrecognizedObjectKeys", () => { + describe("parse", () => { + itParse( + 'includes unknown values when unrecognizedObjectKeys === "passthrough"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "passthrough", + }, + }, + ); + + itParse( + 'strips unknown values when unrecognizedObjectKeys === "strip"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + }, + opts: { + unrecognizedObjectKeys: "strip", + }, + }, + ); + }); + + describe("json", () => { + itJson( + 'includes unknown values when unrecognizedObjectKeys === "passthrough"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "passthrough", + }, + }, + ); + + itJson( + 'strips unknown values when unrecognizedObjectKeys === "strip"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "strip", + }, + }, + ); + }); + }); + + describe("nullish properties", () => { + itSchema("missing properties are not added", object({ foo: property("raw_foo", string().optional()) }), { + raw: {}, + parsed: {}, + }); + + itSchema("undefined properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }); + + itSchema("null properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }); + + describe("extensions", () => { + itSchema( + "undefined properties are not dropped", + object({}).extend(object({ foo: property("raw_foo", string().optional()) })), + { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }, + ); + + describe("parse()", () => { + itParse( + "null properties are not dropped", + object({}).extend(object({ foo: property("raw_foo", string().optional()) })), + { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }, + ); + }); + }); + }); + + itValidate( + "missing property", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { foo: "hello" }, + [ + { + path: [], + message: 'Missing required key "bar"', + }, + ], + ); + + itValidate( + "extra property", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { foo: "hello", bar: "bar", baz: 42 }, + [ + { + path: ["baz"], + message: 'Unexpected key "baz"', + }, + ], + ); + + itValidate( + "not an object", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ], + ); + + itValidate( + "nested validation error", + object({ + foo: object({ + bar: number(), + }), + }), + { foo: { bar: "hello" } }, + [ + { + path: ["foo", "bar"], + message: 'Expected number. Received "hello".', + }, + ], + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/objectWithoutOptionalProperties.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/objectWithoutOptionalProperties.test.ts new file mode 100644 index 000000000000..efcd83afae79 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/objectWithoutOptionalProperties.test.ts @@ -0,0 +1,21 @@ +import { objectWithoutOptionalProperties, string, stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; + +describe("objectWithoutOptionalProperties", () => { + itSchema( + "all properties are required", + objectWithoutOptionalProperties({ + foo: string(), + bar: stringLiteral("bar").optional(), + }), + { + raw: { + foo: "hello", + }, + // @ts-expect-error + parsed: { + foo: "hello", + }, + }, + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/passthrough.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/passthrough.test.ts new file mode 100644 index 000000000000..c8770fca17dc --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/object/passthrough.test.ts @@ -0,0 +1,87 @@ +import { object, string, stringLiteral } from "../../../../src/core/schemas/builders"; +import { itJson, itParse, itSchema } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("passthrough", () => { + const baseSchema = object({ + foo: string(), + bar: stringLiteral("bar"), + }); + + describe("parse", () => { + itParse("includes unknown values", baseSchema.passthrough(), { + raw: { + foo: "hello", + bar: "bar", + baz: "extra", + }, + parsed: { + foo: "hello", + bar: "bar", + baz: "extra", + }, + }); + + itValidate( + "preserves schema validation", + baseSchema.passthrough(), + { + foo: 123, + bar: "bar", + baz: "extra", + }, + [ + { + path: ["foo"], + message: "Expected string. Received 123.", + }, + ], + ); + }); + + describe("json", () => { + itJson("includes unknown values", baseSchema.passthrough(), { + raw: { + foo: "hello", + bar: "bar", + + baz: "extra", + }, + parsed: { + foo: "hello", + bar: "bar", + + baz: "extra", + }, + }); + + itValidate( + "preserves schema validation", + baseSchema.passthrough(), + { + foo: "hello", + bar: "wrong", + baz: "extra", + }, + [ + { + path: ["bar"], + message: 'Expected "bar". Received "wrong".', + }, + ], + ); + }); + + itSchema("preserves schema validation in both directions", baseSchema.passthrough(), { + raw: { + foo: "hello", + bar: "bar", + extra: 42, + }, + parsed: { + foo: "hello", + bar: "bar", + extra: 42, + }, + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/any.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/any.test.ts new file mode 100644 index 000000000000..1adbbe2a8380 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/any.test.ts @@ -0,0 +1,6 @@ +import { any } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("any", () => { + itSchemaIdentity(any(), true); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/boolean.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/boolean.test.ts new file mode 100644 index 000000000000..897a8295dca7 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/boolean.test.ts @@ -0,0 +1,14 @@ +import { boolean } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("boolean", () => { + itSchemaIdentity(boolean(), true); + + itValidate("non-boolean", boolean(), {}, [ + { + path: [], + message: "Expected boolean. Received object.", + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/never.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/never.test.ts new file mode 100644 index 000000000000..1d18eba052ab --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/never.test.ts @@ -0,0 +1,54 @@ +import { never } from "../../../../src/core/schemas/builders"; + +describe("never", () => { + it("always fails to parse", () => { + const schema = never(); + const result = schema.parse("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + } + }); + + it("always fails to json", () => { + const schema = never(); + const result = schema.json("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + } + }); + + it("fails with any value including undefined", () => { + const schema = never(); + expect(schema.parse(undefined).ok).toBe(false); + expect(schema.parse(null).ok).toBe(false); + expect(schema.parse(0).ok).toBe(false); + expect(schema.parse("").ok).toBe(false); + expect(schema.parse({}).ok).toBe(false); + expect(schema.parse([]).ok).toBe(false); + }); + + it("works when called without options parameter", () => { + const schema = never(); + // This tests that the default = {} parameter works correctly + const result = schema.parse("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + expect(result.errors[0]?.path).toEqual([]); + } + }); + + it("succeeds with skipValidation", () => { + const schema = never(); + const result = schema.parse("test", { skipValidation: true }); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value).toBe("test"); + } + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/number.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/number.test.ts new file mode 100644 index 000000000000..2d01415a60ba --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/number.test.ts @@ -0,0 +1,14 @@ +import { number } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("number", () => { + itSchemaIdentity(number(), 42); + + itValidate("non-number", number(), "hello", [ + { + path: [], + message: 'Expected number. Received "hello".', + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/string.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/string.test.ts new file mode 100644 index 000000000000..57b2368784ab --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/string.test.ts @@ -0,0 +1,14 @@ +import { string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("string", () => { + itSchemaIdentity(string(), "hello"); + + itValidate("non-string", string(), 42, [ + { + path: [], + message: "Expected string. Received 42.", + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/unknown.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/unknown.test.ts new file mode 100644 index 000000000000..4d17a7dbd005 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/primitives/unknown.test.ts @@ -0,0 +1,6 @@ +import { unknown } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("unknown", () => { + itSchemaIdentity(unknown(), true); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/record/record.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/record/record.test.ts new file mode 100644 index 000000000000..e07f3e7cb00d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/record/record.test.ts @@ -0,0 +1,34 @@ +import { number, record, string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("record", () => { + itSchemaIdentity(record(string(), string()), { hello: "world" }); + itSchemaIdentity(record(number(), string()), { 42: "world" }); + + itValidate( + "non-record", + record(number(), string()), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ], + ); + + itValidate("invalid key type", record(number(), string()), { hello: "world" }, [ + { + path: ["hello (key)"], + message: 'Expected number. Received "hello".', + }, + ]); + + itValidate("invalid value type", record(string(), number()), { hello: "world" }, [ + { + path: ["hello"], + message: 'Expected number. Received "world".', + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema-utils/getSchemaUtils.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema-utils/getSchemaUtils.test.ts new file mode 100644 index 000000000000..822c3ca4e5a4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema-utils/getSchemaUtils.test.ts @@ -0,0 +1,83 @@ +import { object, string } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; + +describe("getSchemaUtils", () => { + describe("optional()", () => { + itSchema("optional fields allow original schema", string().optional(), { + raw: "hello", + parsed: "hello", + }); + + itSchema("optional fields are not required", string().optional(), { + raw: null, + parsed: undefined, + }); + }); + + describe("transform()", () => { + itSchema( + "transform and untransform run correctly", + string().transform({ + transform: (x) => `${x}X`, + untransform: (x) => (x as string).slice(0, -1), + }), + { + raw: "hello", + parsed: "helloX", + }, + ); + }); + + describe("parseOrThrow()", () => { + it("parses valid value", async () => { + const value = string().parseOrThrow("hello"); + expect(value).toBe("hello"); + }); + + it("throws on invalid value", async () => { + const value = () => object({ a: string(), b: string() }).parseOrThrow({ a: 24 }); + expect(value).toThrowError('a: Expected string. Received 24.; Missing required key "b"'); + }); + }); + + describe("jsonOrThrow()", () => { + it("serializes valid value", async () => { + const value = string().jsonOrThrow("hello"); + expect(value).toBe("hello"); + }); + + it("throws on invalid value", async () => { + const value = () => object({ a: string(), b: string() }).jsonOrThrow({ a: 24 }); + expect(value).toThrowError('a: Expected string. Received 24.; Missing required key "b"'); + }); + }); + + describe("omitUndefined", () => { + it("serializes undefined as null", async () => { + const value = object({ + a: string().optional(), + b: string().optional(), + }).jsonOrThrow({ + a: "hello", + b: undefined, + }); + expect(value).toEqual({ a: "hello", b: null }); + }); + + it("omits undefined values", async () => { + const value = object({ + a: string().optional(), + b: string().optional(), + }).jsonOrThrow( + { + a: "hello", + b: undefined, + }, + { + omitUndefined: true, + }, + ); + expect(value).toEqual({ a: "hello" }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema.test.ts new file mode 100644 index 000000000000..13842ff40157 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/schema.test.ts @@ -0,0 +1,78 @@ +import { + boolean, + discriminant, + list, + number, + object, + string, + stringLiteral, + union, +} from "../../../src/core/schemas/builders"; +import { booleanLiteral } from "../../../src/core/schemas/builders/literals/booleanLiteral"; +import { property } from "../../../src/core/schemas/builders/object/property"; +import { itSchema } from "./utils/itSchema"; + +describe("Schema", () => { + itSchema( + "large nested object", + object({ + a: string(), + b: stringLiteral("b value"), + c: property( + "raw_c", + list( + object({ + animal: union(discriminant("type", "_type"), { + dog: object({ value: boolean() }), + cat: object({ value: property("raw_cat", number()) }), + }), + }), + ), + ), + d: property("raw_d", boolean()), + e: booleanLiteral(true), + }), + { + raw: { + a: "hello", + b: "b value", + raw_c: [ + { + animal: { + _type: "dog", + value: true, + }, + }, + { + animal: { + _type: "cat", + raw_cat: 42, + }, + }, + ], + raw_d: false, + e: true, + }, + parsed: { + a: "hello", + b: "b value", + c: [ + { + animal: { + type: "dog", + value: true, + }, + }, + { + animal: { + type: "cat", + value: 42, + }, + }, + ], + d: false, + e: true, + }, + }, + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/set/set.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/set/set.test.ts new file mode 100644 index 000000000000..53a1652c8bbb --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/set/set.test.ts @@ -0,0 +1,48 @@ +import { set, string } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("set", () => { + itSchema("converts between raw list and parsed Set", set(string()), { + raw: ["A", "B"], + parsed: new Set(["A", "B"]), + }); + + itValidateParse("not a list", set(string()), 42, [ + { + path: [], + message: "Expected list. Received 42.", + }, + ]); + + itValidateJson( + "not a Set", + set(string()), + [], + [ + { + path: [], + message: "Expected Set. Received list.", + }, + ], + ); + + itValidateParse( + "invalid item type", + set(string()), + [42], + [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ], + ); + + itValidateJson("invalid item type", set(string()), new Set([42]), [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ]); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/skipValidation.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/skipValidation.test.ts new file mode 100644 index 000000000000..3283555949ab --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/skipValidation.test.ts @@ -0,0 +1,44 @@ +/* eslint-disable no-console */ +import { boolean, number, object, property, string, undiscriminatedUnion } from "../../../src/core/schemas/builders"; + +describe("skipValidation", () => { + it("allows data that doesn't conform to the schema", async () => { + const warningLogs: string[] = []; + const originalConsoleWarn = console.warn; + console.warn = (...args) => warningLogs.push(args.join(" ")); + + const schema = object({ + camelCase: property("snake_case", string()), + numberProperty: number(), + requiredProperty: boolean(), + anyPrimitive: undiscriminatedUnion([string(), number(), boolean()]), + }); + + const parsed = await schema.parse( + { + snake_case: "hello", + numberProperty: "oops", + anyPrimitive: true, + }, + { + skipValidation: true, + }, + ); + + expect(parsed).toEqual({ + ok: true, + value: { + camelCase: "hello", + numberProperty: "oops", + anyPrimitive: true, + }, + }); + + expect(warningLogs).toEqual([ + `Failed to validate. + - numberProperty: Expected number. Received "oops".`, + ]); + + console.warn = originalConsoleWarn; + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/undiscriminated-union/undiscriminatedUnion.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/undiscriminated-union/undiscriminatedUnion.test.ts new file mode 100644 index 000000000000..01dcadbba37b --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/undiscriminated-union/undiscriminatedUnion.test.ts @@ -0,0 +1,44 @@ +import { number, object, property, string, undiscriminatedUnion } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; + +describe("undiscriminatedUnion", () => { + itSchemaIdentity(undiscriminatedUnion([string(), number()]), "hello world"); + + itSchemaIdentity(undiscriminatedUnion([object({ hello: string() }), object({ goodbye: string() })]), { + goodbye: "foo", + }); + + itSchema( + "Correctly transforms", + undiscriminatedUnion([object({ hello: string() }), object({ helloWorld: property("hello_world", string()) })]), + { + raw: { hello_world: "foo " }, + parsed: { helloWorld: "foo " }, + }, + ); + + it("Returns errors for all variants", async () => { + const result = await undiscriminatedUnion([string(), number()]).parse(true); + if (result.ok) { + throw new Error("Unexpectedly passed validation"); + } + expect(result.errors).toEqual([ + { + message: "[Variant 0] Expected string. Received true.", + path: [], + }, + { + message: "[Variant 1] Expected number. Received true.", + path: [], + }, + ]); + }); + + describe("compile", () => { + // eslint-disable-next-line vi/expect-expect + it("doesn't compile with zero members", () => { + // @ts-expect-error + () => undiscriminatedUnion([]); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/union/union.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/union/union.test.ts new file mode 100644 index 000000000000..1f5d7a8fad5c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/union/union.test.ts @@ -0,0 +1,113 @@ +import { boolean, discriminant, number, object, string, union } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("union", () => { + itSchemaIdentity( + union("type", { + lion: object({ + meows: boolean(), + }), + giraffe: object({ + heightInInches: number(), + }), + }), + { type: "lion", meows: true }, + { title: "doesn't transform discriminant when it's a string" }, + ); + + itSchema( + "transforms discriminant when it's a discriminant()", + union(discriminant("type", "_type"), { + lion: object({ meows: boolean() }), + giraffe: object({ heightInInches: number() }), + }), + { + raw: { _type: "lion", meows: true }, + parsed: { type: "lion", meows: true }, + }, + ); + + describe("allowUnrecognizedUnionMembers", () => { + itSchema( + "transforms discriminant & passes through values when discriminant value is unrecognized", + union(discriminant("type", "_type"), { + lion: object({ meows: boolean() }), + giraffe: object({ heightInInches: number() }), + }), + { + // @ts-expect-error + raw: { _type: "moose", isAMoose: true }, + // @ts-expect-error + parsed: { type: "moose", isAMoose: true }, + opts: { + allowUnrecognizedUnionMembers: true, + }, + }, + ); + }); + + describe("withParsedProperties", () => { + it("Added property is included on parsed object", async () => { + const schema = union("type", { + lion: object({}), + tiger: object({ value: string() }), + }).withParsedProperties({ + printType: (parsed) => () => parsed.type, + }); + + const parsed = await schema.parse({ type: "lion" }); + if (!parsed.ok) { + throw new Error("Failed to parse"); + } + expect(parsed.value.printType()).toBe("lion"); + }); + }); + + itValidate( + "non-object", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ], + ); + + itValidate( + "missing discriminant", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + {}, + [ + { + path: [], + message: 'Missing discriminant ("type")', + }, + ], + ); + + itValidate( + "unrecognized discriminant value", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + { + type: "bear", + }, + [ + { + path: ["type"], + message: 'Expected enum. Received "bear".', + }, + ], + ); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itSchema.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itSchema.ts new file mode 100644 index 000000000000..25b13e643207 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itSchema.ts @@ -0,0 +1,78 @@ +/* eslint-disable vi/no-export */ +import type { Schema, SchemaOptions } from "../../../../src/core/schemas/Schema"; + +export function itSchemaIdentity( + schema: Schema, + value: T, + { title = "functions as identity", opts }: { title?: string; opts?: SchemaOptions } = {}, +): void { + itSchema(title, schema, { raw: value, parsed: value, opts }); +} + +export function itSchema( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + only = false, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + only?: boolean; + }, +): void { + // eslint-disable-next-line vi/valid-title + (only ? describe.only : describe)(title, () => { + itParse("parse()", schema, { raw, parsed, opts }); + itJson("json()", schema, { raw, parsed, opts }); + }); +} + +export function itParse( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + }, +): void { + // eslint-disable-next-line vi/valid-title + it(title, () => { + const maybeValid = schema.parse(raw, opts); + if (!maybeValid.ok) { + throw new Error(`Failed to parse() ${JSON.stringify(maybeValid.errors, undefined, 4)}`); + } + expect(maybeValid.value).toStrictEqual(parsed); + }); +} + +export function itJson( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + }, +): void { + // eslint-disable-next-line vi/valid-title + it(title, () => { + const maybeValid = schema.json(parsed, opts); + if (!maybeValid.ok) { + throw new Error(`Failed to json() ${JSON.stringify(maybeValid.errors, undefined, 4)}`); + } + expect(maybeValid.value).toStrictEqual(raw); + }); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itValidate.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itValidate.ts new file mode 100644 index 000000000000..60bc56c123cf --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/schemas/utils/itValidate.ts @@ -0,0 +1,56 @@ +/* eslint-disable vi/no-export */ +import type { Schema, SchemaOptions, ValidationError } from "../../../../src/core/schemas/Schema"; + +export function itValidate( + title: string, + schema: Schema, + input: unknown, + errors: ValidationError[], + opts?: SchemaOptions, +): void { + // eslint-disable-next-line vi/valid-title + describe("parse()", () => { + itValidateParse(title, schema, input, errors, opts); + }); + describe("json()", () => { + itValidateJson(title, schema, input, errors, opts); + }); +} + +export function itValidateParse( + title: string, + schema: Schema, + raw: unknown, + errors: ValidationError[], + opts?: SchemaOptions, +): void { + describe("parse", () => { + // eslint-disable-next-line vi/valid-title + it(title, async () => { + const maybeValid = await schema.parse(raw, opts); + if (maybeValid.ok) { + throw new Error("Value passed validation"); + } + expect(maybeValid.errors).toStrictEqual(errors); + }); + }); +} + +export function itValidateJson( + title: string, + schema: Schema, + parsed: unknown, + errors: ValidationError[], + opts?: SchemaOptions, +): void { + describe("json", () => { + // eslint-disable-next-line vi/valid-title + it(title, async () => { + const maybeValid = await schema.json(parsed, opts); + if (maybeValid.ok) { + throw new Error("Value passed validation"); + } + expect(maybeValid.errors).toStrictEqual(errors); + }); + }); +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/url/join.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/url/join.test.ts new file mode 100644 index 000000000000..123488f084ea --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/url/join.test.ts @@ -0,0 +1,284 @@ +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/exhaustive/serde-layer-zurg/tests/unit/url/qs.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/url/qs.test.ts new file mode 100644 index 000000000000..42cdffb9e5ea --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/unit/url/qs.test.ts @@ -0,0 +1,278 @@ +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/exhaustive/serde-layer-zurg/tests/wire/.gitkeep b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/container.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/container.test.ts new file mode 100644 index 000000000000..98b9ec0c58b9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/container.test.ts @@ -0,0 +1,170 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ContainerClient", () => { + test("getAndReturnListOfPrimitives", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = ["string", "string"]; + const rawResponseBody = ["string", "string"]; + server + .mockEndpoint() + .post("/container/list-of-primitives") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnListOfPrimitives(["string", "string"]); + expect(response).toEqual(["string", "string"]); + }); + + test("getAndReturnListOfObjects", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [{ string: "string" }, { string: "string" }]; + const rawResponseBody = [{ string: "string" }, { string: "string" }]; + server + .mockEndpoint() + .post("/container/list-of-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnListOfObjects([ + { + string: "string", + }, + { + string: "string", + }, + ]); + expect(response).toEqual([ + { + string: "string", + }, + { + string: "string", + }, + ]); + }); + + test("getAndReturnSetOfPrimitives", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = ["string"]; + const rawResponseBody = ["string"]; + server + .mockEndpoint() + .post("/container/set-of-primitives") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnSetOfPrimitives(new Set(["string"])); + expect(response).toEqual(new Set(["string"])); + }); + + test("getAndReturnSetOfObjects", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [{ string: "string" }]; + const rawResponseBody = [{ string: "string" }]; + server + .mockEndpoint() + .post("/container/set-of-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnSetOfObjects([ + { + string: "string", + }, + ]); + expect(response).toEqual([ + { + string: "string", + }, + ]); + }); + + test("getAndReturnMapPrimToPrim", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/container/map-prim-to-prim") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnMapPrimToPrim({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); + + test("getAndReturnMapOfPrimToObject", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: { string: "string" } }; + const rawResponseBody = { string: { string: "string" } }; + server + .mockEndpoint() + .post("/container/map-prim-to-object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnMapOfPrimToObject({ + string: { + string: "string", + }, + }); + expect(response).toEqual({ + string: { + string: "string", + }, + }); + }); + + test("getAndReturnOptional", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/container/opt-objects") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.container.getAndReturnOptional({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/contentType.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/contentType.test.ts new file mode 100644 index 000000000000..4bc8916f7ce5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/contentType.test.ts @@ -0,0 +1,88 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ContentTypeClient", () => { + test("postJsonPatchContentType", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + + server.mockEndpoint().post("/foo/bar").jsonBody(rawRequestBody).respondWith().statusCode(200).build(); + + const response = await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual(undefined); + }); + + test("postJsonPatchContentWithCharsetType", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + + server.mockEndpoint().post("/foo/baz").jsonBody(rawRequestBody).respondWith().statusCode(200).build(); + + const response = await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual(undefined); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/enum.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/enum.test.ts new file mode 100644 index 000000000000..5770be42a097 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/enum.test.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("EnumClient", () => { + test("getAndReturnEnum", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "SUNNY"; + const rawResponseBody = "SUNNY"; + server + .mockEndpoint() + .post("/enum") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.enum.getAndReturnEnum("SUNNY"); + expect(response).toEqual("SUNNY"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/httpMethods.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/httpMethods.test.ts new file mode 100644 index 000000000000..b9e26c032ff9 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/httpMethods.test.ts @@ -0,0 +1,212 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("HttpMethodsClient", () => { + test("testGet", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/http-methods/id").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.httpMethods.testGet("id"); + expect(response).toEqual("string"); + }); + + test("testPost", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/http-methods") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPost({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testPut", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .put("/http-methods/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPut("id", { + string: "string", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testPatch", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .patch("/http-methods/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testPatch("id", { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("testDelete", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = true; + server + .mockEndpoint() + .delete("/http-methods/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.httpMethods.testDelete("id"); + expect(response).toEqual(true); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/object.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/object.test.ts new file mode 100644 index 000000000000..685826602e5e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/object.test.ts @@ -0,0 +1,448 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ObjectClient", () => { + test("getAndReturnWithOptionalField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/object/get-and-return-with-optional-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithOptionalField({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("getAndReturnWithRequiredField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { string: "string" }; + const rawResponseBody = { string: "string" }; + server + .mockEndpoint() + .post("/object/get-and-return-with-required-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithRequiredField({ + string: "string", + }); + expect(response).toEqual({ + string: "string", + }); + }); + + test("getAndReturnWithMapOfMap", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { map: { map: { map: "map" } } }; + const rawResponseBody = { map: { map: { map: "map" } } }; + server + .mockEndpoint() + .post("/object/get-and-return-with-map-of-map") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnWithMapOfMap({ + map: { + map: { + map: "map", + }, + }, + }); + expect(response).toEqual({ + map: { + map: { + map: "map", + }, + }, + }); + }); + + test("getAndReturnNestedWithOptionalField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-optional-field") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithOptionalField({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); + + test("getAndReturnNestedWithRequiredField", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-required-field/string") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithRequiredField("string", { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); + + test("getAndReturnNestedWithRequiredFieldAsList", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = [ + { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }, + { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }, + ]; + const rawResponseBody = { + string: "string", + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + server + .mockEndpoint() + .post("/object/get-and-return-nested-with-required-field-list") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.object.getAndReturnNestedWithRequiredFieldAsList([ + { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }, + { + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }, + ]); + expect(response).toEqual({ + string: "string", + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/params.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/params.test.ts new file mode 100644 index 000000000000..be2e7ddd6591 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/params.test.ts @@ -0,0 +1,120 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("ParamsClient", () => { + test("getWithPath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/params/path/param").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.params.getWithPath("param"); + expect(response).toEqual("string"); + }); + + test("getWithInlinePath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/params/path/param").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.params.getWithInlinePath({ + param: "param", + }); + expect(response).toEqual("string"); + }); + + test("getWithQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithQuery({ + query: "query", + number: 1, + }); + expect(response).toEqual(undefined); + }); + + test("getWithAllowMultipleQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithAllowMultipleQuery({ + query: "query", + number: 1, + }); + expect(response).toEqual(undefined); + }); + + test("getWithPathAndQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params/path-query/param").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithPathAndQuery("param", { + query: "query", + }); + expect(response).toEqual(undefined); + }); + + test("getWithInlinePathAndQuery", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().get("/params/path-query/param").respondWith().statusCode(200).build(); + + const response = await client.endpoints.params.getWithInlinePathAndQuery({ + param: "param", + query: "query", + }); + expect(response).toEqual(undefined); + }); + + test("modifyWithPath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .put("/params/path/param") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.params.modifyWithPath("param", "string"); + expect(response).toEqual("string"); + }); + + test("modifyWithInlinePath", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .put("/params/path/param") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.params.modifyWithInlinePath({ + param: "param", + body: "string", + }); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/primitive.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/primitive.test.ts new file mode 100644 index 000000000000..650fc620bb1d --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/primitive.test.ts @@ -0,0 +1,168 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("PrimitiveClient", () => { + test("getAndReturnString", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + const rawResponseBody = "string"; + server + .mockEndpoint() + .post("/primitive/string") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnString("string"); + expect(response).toEqual("string"); + }); + + test("getAndReturnInt", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1; + const rawResponseBody = 1; + server + .mockEndpoint() + .post("/primitive/integer") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnInt(1); + expect(response).toEqual(1); + }); + + test("getAndReturnLong", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1000000; + const rawResponseBody = 1000000; + server + .mockEndpoint() + .post("/primitive/long") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnLong(1000000); + expect(response).toEqual(1000000); + }); + + test("getAndReturnDouble", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = 1.1; + const rawResponseBody = 1.1; + server + .mockEndpoint() + .post("/primitive/double") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDouble(1.1); + expect(response).toEqual(1.1); + }); + + test("getAndReturnBool", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = true; + const rawResponseBody = true; + server + .mockEndpoint() + .post("/primitive/boolean") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnBool(true); + expect(response).toEqual(true); + }); + + test("getAndReturnDatetime", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "2024-01-15T09:30:00Z"; + const rawResponseBody = "2024-01-15T09:30:00Z"; + server + .mockEndpoint() + .post("/primitive/datetime") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDatetime(new Date("2024-01-15T09:30:00.000Z")); + expect(response).toEqual(new Date("2024-01-15T09:30:00.000Z")); + }); + + test("getAndReturnDate", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "2023-01-15"; + const rawResponseBody = "2023-01-15"; + server + .mockEndpoint() + .post("/primitive/date") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnDate("2023-01-15"); + expect(response).toEqual("2023-01-15"); + }); + + test("getAndReturnUUID", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"; + const rawResponseBody = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"; + server + .mockEndpoint() + .post("/primitive/uuid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnUuid("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + expect(response).toEqual("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32"); + }); + + test("getAndReturnBase64", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "SGVsbG8gd29ybGQh"; + const rawResponseBody = "SGVsbG8gd29ybGQh"; + server + .mockEndpoint() + .post("/primitive/base64") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.primitive.getAndReturnBase64("SGVsbG8gd29ybGQh"); + expect(response).toEqual("SGVsbG8gd29ybGQh"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/put.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/put.test.ts new file mode 100644 index 000000000000..1481521d77b5 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/put.test.ts @@ -0,0 +1,39 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("PutClient", () => { + test("add", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + errors: [ + { category: "API_ERROR", code: "INTERNAL_SERVER_ERROR", detail: "detail", field: "field" }, + { category: "API_ERROR", code: "INTERNAL_SERVER_ERROR", detail: "detail", field: "field" }, + ], + }; + server.mockEndpoint().put("/id").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.put.add({ + id: "id", + }); + expect(response).toEqual({ + errors: [ + { + category: "API_ERROR", + code: "INTERNAL_SERVER_ERROR", + detail: "detail", + field: "field", + }, + { + category: "API_ERROR", + code: "INTERNAL_SERVER_ERROR", + detail: "detail", + field: "field", + }, + ], + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/union.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/union.test.ts new file mode 100644 index 000000000000..35b1405e89ca --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/union.test.ts @@ -0,0 +1,32 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("UnionClient", () => { + test("getAndReturnUnion", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { animal: "dog", name: "name", likesToWoof: true }; + const rawResponseBody = { animal: "dog", name: "name", likesToWoof: true }; + server + .mockEndpoint() + .post("/union") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.union.getAndReturnUnion({ + animal: "dog", + name: "name", + likesToWoof: true, + }); + expect(response).toEqual({ + animal: "dog", + name: "name", + likesToWoof: true, + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/urls.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/urls.test.ts new file mode 100644 index 000000000000..b450fbeea217 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/endpoints/urls.test.ts @@ -0,0 +1,68 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../../src/Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("UrlsClient", () => { + test("withMixedCase", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().get("/urls/MixedCase").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.endpoints.urls.withMixedCase(); + expect(response).toEqual("string"); + }); + + test("noEndingSlash", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/no-ending-slash") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.noEndingSlash(); + expect(response).toEqual("string"); + }); + + test("withEndingSlash", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/with-ending-slash/") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.withEndingSlash(); + expect(response).toEqual("string"); + }); + + test("withUnderscores", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server + .mockEndpoint() + .get("/urls/with_underscores") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.endpoints.urls.withUnderscores(); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/inlinedRequests.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/inlinedRequests.test.ts new file mode 100644 index 000000000000..61b5a64bce8f --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/inlinedRequests.test.ts @@ -0,0 +1,150 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as SeedExhaustive from "../../src/api/index"; +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("InlinedRequestsClient", () => { + test("postWithObjectBodyandResponse (1)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server + .mockEndpoint() + .post("/req-bodies/object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("postWithObjectBodyandResponse (2)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + string: "string", + integer: 1, + NestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }, + }; + const rawResponseBody = { message: "message" }; + server + .mockEndpoint() + .post("/req-bodies/object") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.inlinedRequests.postWithObjectBodyandResponse({ + string: "string", + integer: 1, + nestedObject: { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }, + }); + }).rejects.toThrow(SeedExhaustive.BadRequestBody); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noAuth.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noAuth.test.ts new file mode 100644 index 000000000000..3317cf1a69d1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noAuth.test.ts @@ -0,0 +1,48 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as SeedExhaustive from "../../src/api/index"; +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("NoAuthClient", () => { + test("postWithNoAuth (1)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { key: "value" }; + const rawResponseBody = true; + server + .mockEndpoint() + .post("/no-auth") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.noAuth.postWithNoAuth({ + key: "value", + }); + expect(response).toEqual(true); + }); + + test("postWithNoAuth (2)", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { key: "value" }; + const rawResponseBody = { message: "message" }; + server + .mockEndpoint() + .post("/no-auth") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.noAuth.postWithNoAuth({ + key: "value", + }); + }).rejects.toThrow(SeedExhaustive.BadRequestBody); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noReqBody.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noReqBody.test.ts new file mode 100644 index 000000000000..8f5f6a59a512 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/noReqBody.test.ts @@ -0,0 +1,58 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("NoReqBodyClient", () => { + test("getWithNoRequestBody", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: ["set"], + map: { "1": "map" }, + bigint: "1000000", + }; + server.mockEndpoint().get("/no-req-body").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.noReqBody.getWithNoRequestBody(); + expect(response).toEqual({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: new Date("2024-01-15T09:30:00.000Z"), + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", + }); + }); + + test("postWithNoRequestBody", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = "string"; + server.mockEndpoint().post("/no-req-body").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.noReqBody.postWithNoRequestBody(); + expect(response).toEqual("string"); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/reqWithHeaders.test.ts b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/reqWithHeaders.test.ts new file mode 100644 index 000000000000..a128d4844ba2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tests/wire/reqWithHeaders.test.ts @@ -0,0 +1,29 @@ +// This file was auto-generated by Fern from our API Definition. + +import { SeedExhaustiveClient } from "../../src/Client"; +import { mockServerPool } from "../mock-server/MockServerPool"; + +describe("ReqWithHeadersClient", () => { + test("getWithCustomHeader", async () => { + const server = mockServerPool.createServer(); + const client = new SeedExhaustiveClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = "string"; + + server + .mockEndpoint() + .post("/test-headers/custom-header") + .header("X-TEST-SERVICE-HEADER", "X-TEST-SERVICE-HEADER") + .header("X-TEST-ENDPOINT-HEADER", "X-TEST-ENDPOINT-HEADER") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .build(); + + const response = await client.reqWithHeaders.getWithCustomHeader({ + xTestServiceHeader: "X-TEST-SERVICE-HEADER", + xTestEndpointHeader: "X-TEST-ENDPOINT-HEADER", + body: "string", + }); + expect(response).toEqual(undefined); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.base.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.base.json new file mode 100644 index 000000000000..d7627675de20 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.base.json @@ -0,0 +1,18 @@ +{ + "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/exhaustive/serde-layer-zurg/tsconfig.cjs.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.cjs.json new file mode 100644 index 000000000000..5c11446f5984 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs" + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.esm.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.esm.json new file mode 100644 index 000000000000..6ce909748b2c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.esm.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm", + "verbatimModuleSyntax": true + }, + "include": ["src"], + "exclude": [] +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.json b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.json new file mode 100644 index 000000000000..d77fdf00d259 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.cjs.json" +} diff --git a/seed/ts-sdk/exhaustive/serde-layer-zurg/vitest.config.mts b/seed/ts-sdk/exhaustive/serde-layer-zurg/vitest.config.mts new file mode 100644 index 000000000000..ba2ec4f9d45a --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer-zurg/vitest.config.mts @@ -0,0 +1,28 @@ +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 76092f648563..2978336d2886 100644 --- a/seed/ts-sdk/seed.yml +++ b/seed/ts-sdk/seed.yml @@ -78,6 +78,12 @@ fixtures: - outputFolder: serde-layer customConfig: noSerdeLayer: false + - outputFolder: serde-layer-zurg + customConfig: + serializationFormat: zurg + - outputFolder: serde-layer-zod + customConfig: + serializationFormat: zod - outputFolder: retain-original-casing customConfig: retainOriginalCasing: true