diff --git a/docker/seed/Dockerfile.ts b/docker/seed/Dockerfile.ts index b7c00adf89f8..e79055947048 100644 --- a/docker/seed/Dockerfile.ts +++ b/docker/seed/Dockerfile.ts @@ -12,10 +12,10 @@ RUN corepack prepare yarn@1.22.22 RUN pnpm add -g typescript@~5.7.2 \ prettier@3.7.4 \ - oxfmt@0.27.0 \ + oxfmt@0.35.0 \ @biomejs/biome@2.4.3 \ - oxlint@1.42.0 \ - oxlint-tsgolint@0.11.4 \ + oxlint@1.50.0 \ + oxlint-tsgolint@0.14.2 \ @types/node@^18.19.70 \ webpack@^5.97.1 \ msw@2.11.2 \ diff --git a/generators/csharp/base/package.json b/generators/csharp/base/package.json index c307279871c0..56bf63ddfe6c 100644 --- a/generators/csharp/base/package.json +++ b/generators/csharp/base/package.json @@ -40,6 +40,7 @@ "@fern-fern/ir-sdk": "^62.3.0", "@types/lodash-es": "catalog:", "@types/node": "catalog:", + "eta": "^4.5.1", "lodash-es": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/generators/csharp/base/src/project/CsharpProject.ts b/generators/csharp/base/src/project/CsharpProject.ts index 72cc0409953c..6e67cbf42465 100644 --- a/generators/csharp/base/src/project/CsharpProject.ts +++ b/generators/csharp/base/src/project/CsharpProject.ts @@ -2,14 +2,16 @@ import { AbstractProject, FernGeneratorExec, File, SourceFetcher } from "@fern-a import { Generation, WithGeneration } from "@fern-api/csharp-codegen"; import { AbsoluteFilePath, join, RelativeFilePath } from "@fern-api/fs-utils"; import { loggingExeca } from "@fern-api/logging-execa"; +import { Eta } from "eta"; import { access, mkdir, readFile, unlink, writeFile } from "fs/promises"; -import { template } from "lodash-es"; import path from "path"; import { AsIsFiles } from "../AsIs.js"; import { GeneratorContext } from "../context/GeneratorContext.js"; import { findDotnetToolPath } from "../findDotNetToolPath.js"; import { CSharpFile } from "./CSharpFile.js"; +const eta = new Eta({ autoEscape: false, useWith: true, autoTrim: false }); + export const CORE_DIRECTORY_NAME = "Core"; export const PUBLIC_CORE_DIRECTORY_NAME = "Public"; /** @@ -285,22 +287,24 @@ export class CsharpProject extends AbstractProject { } const githubWorkflowTemplate = (await readFile(getAsIsFilepath(AsIsFiles.CiYaml))).toString(); - const githubWorkflow = template(githubWorkflowTemplate)({ - projectName: this.name, - libraryPath: path.posix.join(libraryPath, this.name), - libraryProjectFilePath: path.posix.join(libraryPath, this.name, `${this.name}.csproj`), - testProjectFilePath: path.posix.join( - testPath, - this.names.files.testProject, - `${this.names.files.testProject}.csproj` - ), - shouldWritePublishBlock: this.context.publishConfig != null, - nugetTokenEnvvar: - this.context.publishConfig?.apiKeyEnvironmentVariable == null || - this.context.publishConfig?.apiKeyEnvironmentVariable === "" - ? "NUGET_API_TOKEN" - : this.context.publishConfig.apiKeyEnvironmentVariable - }).replaceAll("\\{", "{"); + const githubWorkflow = eta + .renderString(githubWorkflowTemplate, { + projectName: this.name, + libraryPath: path.posix.join(libraryPath, this.name), + libraryProjectFilePath: path.posix.join(libraryPath, this.name, `${this.name}.csproj`), + testProjectFilePath: path.posix.join( + testPath, + this.names.files.testProject, + `${this.names.files.testProject}.csproj` + ), + shouldWritePublishBlock: this.context.publishConfig != null, + nugetTokenEnvvar: + this.context.publishConfig?.apiKeyEnvironmentVariable == null || + this.context.publishConfig?.apiKeyEnvironmentVariable === "" + ? "NUGET_API_TOKEN" + : this.context.publishConfig.apiKeyEnvironmentVariable + }) + .replaceAll("\\{", "{"); const ghDir = join(this.absolutePathToOutputDirectory, RelativeFilePath.of(".github/workflows")); await mkdir(ghDir, { recursive: true }); await writeFile(join(ghDir, RelativeFilePath.of("ci.yml")), githubWorkflow); @@ -436,7 +440,7 @@ dotnet_diagnostic.IDE0005.severity = error const testCsProjTemplateContents = ( await readFile(getAsIsFilepath(AsIsFiles.Test.TemplateTestCsProj)) ).toString(); - const testCsProjContents = template(testCsProjTemplateContents)({ + const testCsProjContents = eta.renderString(testCsProjTemplateContents, { projectName: this.name, testProjectName }); @@ -699,7 +703,7 @@ dotnet_diagnostic.IDE0005.severity = error } function replaceTemplate({ contents, variables }: { contents: string; variables: Record }): string { - return template(contents)(variables); + return eta.renderString(contents, variables); } function getAsIsFilepath(filename: string): string { diff --git a/generators/csharp/sdk/versions.yml b/generators/csharp/sdk/versions.yml index 97aa0e838a2d..6bc62c90c3a5 100644 --- a/generators/csharp/sdk/versions.yml +++ b/generators/csharp/sdk/versions.yml @@ -1,4 +1,15 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 2.22.3 + changelogEntry: + - summary: | + Replace lodash-es template engine with Eta for processing template files. + Eta is a modern, lightweight, TypeScript-native engine with zero dependencies + that uses the same `<% %>` / `<%= %>` syntax. This resolves crashes when + template files contain backticks (template literals). + type: fix + createdAt: "2026-02-24" + irVersion: 62 + - version: 2.22.2 changelogEntry: - summary: | diff --git a/generators/python/sdk/versions.yml b/generators/python/sdk/versions.yml index d9bad59755bf..bc146d1496d3 100644 --- a/generators/python/sdk/versions.yml +++ b/generators/python/sdk/versions.yml @@ -1,11 +1,22 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json # For unreleased changes, use unreleased.yml +- version: 4.59.2 + changelogEntry: + - summary: | + Generate an appropriate `client.py` file when `client.exported_filename` differs from `client.filename`. + Previously, the `__init__.py` would import from the exported filename (e.g., `client.py`) but + that file was never created, causing mypy `import-not-found` errors. + type: fix + createdAt: "2026-02-20" + irVersion: 65 + - version: 4.59.1 changelogEntry: - summary: Fix wire test imports to respect package_name custom config, preventing import errors when users specify custom package names type: fix createdAt: "2026-02-23" irVersion: 65 + - version: 4.59.0 changelogEntry: - summary: | diff --git a/generators/python/src/fern_python/generators/sdk/client_generator/generated_root_client.py b/generators/python/src/fern_python/generators/sdk/client_generator/generated_root_client.py index c2edaeb6960e..db9fb7d4bab9 100644 --- a/generators/python/src/fern_python/generators/sdk/client_generator/generated_root_client.py +++ b/generators/python/src/fern_python/generators/sdk/client_generator/generated_root_client.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass -from typing import List +from dataclasses import dataclass, field +from typing import List, Optional from fern_python.codegen import AST from fern_python.generators.sdk.core_utilities.client_wrapper_generator import ( @@ -11,6 +11,7 @@ class RootClient: class_reference: AST.ClassReference parameters: List[ConstructorParameter] + init_parameters: Optional[List[ConstructorParameter]] = field(default=None) @dataclass diff --git a/generators/python/src/fern_python/generators/sdk/client_generator/root_client_generator.py b/generators/python/src/fern_python/generators/sdk/client_generator/root_client_generator.py index a33c4990abe4..15308e6660fa 100644 --- a/generators/python/src/fern_python/generators/sdk/client_generator/root_client_generator.py +++ b/generators/python/src/fern_python/generators/sdk/client_generator/root_client_generator.py @@ -181,6 +181,8 @@ def __init__( # like os.getenv("..."). use_kwargs_snippets=(has_inferred_auth or is_oauth_client_credentials), base_url_example_value=base_url_example_value, + sync_init_parameters=self._get_constructor_parameters(is_async=False), + async_init_parameters=self._get_constructor_parameters(is_async=True), ) self._generated_root_client = root_client_builder.build() @@ -1565,11 +1567,19 @@ def __init__( oauth_token_override: bool = False, use_kwargs_snippets: bool = False, base_url_example_value: Optional[AST.Expression] = None, + sync_init_parameters: Optional[Sequence[ConstructorParameter]] = None, + async_init_parameters: Optional[Sequence[ConstructorParameter]] = None, ): self._module_path = module_path self._class_name = class_name self._async_class_name = async_class_name self._constructor_parameters: List[ConstructorParameter] = list(constructor_parameters) + self._sync_init_parameters: Optional[List[ConstructorParameter]] = ( + list(sync_init_parameters) if sync_init_parameters is not None else None + ) + self._async_init_parameters: Optional[List[ConstructorParameter]] = ( + list(async_init_parameters) if async_init_parameters is not None else None + ) self._oauth_token_override = oauth_token_override self._use_kwargs_snippets = use_kwargs_snippets self._base_url_example_value = base_url_example_value @@ -1726,7 +1736,15 @@ def build_default_snippet_kwargs() -> List[typing.Tuple[str, AST.Expression]]: return GeneratedRootClient( async_instantiations=async_instantiations, - async_client=RootClient(class_reference=async_class_reference, parameters=self._constructor_parameters), + async_client=RootClient( + class_reference=async_class_reference, + parameters=self._constructor_parameters, + init_parameters=self._async_init_parameters, + ), sync_instantiations=sync_instantiations, - sync_client=RootClient(class_reference=sync_class_reference, parameters=self._constructor_parameters), + sync_client=RootClient( + class_reference=sync_class_reference, + parameters=self._constructor_parameters, + init_parameters=self._sync_init_parameters, + ), ) diff --git a/generators/python/src/fern_python/generators/sdk/sdk_generator.py b/generators/python/src/fern_python/generators/sdk/sdk_generator.py index 45ffac02674a..fdef3d3e2d06 100644 --- a/generators/python/src/fern_python/generators/sdk/sdk_generator.py +++ b/generators/python/src/fern_python/generators/sdk/sdk_generator.py @@ -3,7 +3,7 @@ from typing import Literal, Optional, Sequence, Tuple, Union, cast from .client_generator.client_generator import ClientGenerator -from .client_generator.generated_root_client import GeneratedRootClient +from .client_generator.generated_root_client import GeneratedRootClient, RootClient from .client_generator.inferred_auth_token_provider_generator import InferredAuthTokenProviderGenerator from .client_generator.oauth_token_provider_generator import OAuthTokenProviderGenerator from .client_generator.raw_client_generator import RawClientGenerator @@ -250,6 +250,17 @@ def run( oauth_scheme=oauth_scheme, ) + # If exported_filename differs from filename, generate an inheritance-based wrapper + actual_filename = custom_config.client_filename or custom_config.client.filename + if custom_config.client.exported_filename != actual_filename: + self._generate_exported_client_wrapper( + context=context, + custom_config=custom_config, + project=project, + generated_root_client=generated_root_client, + generator_exec_wrapper=generator_exec_wrapper, + ) + # Since you can customize the client export, we handle it here to capture the generated # and non-generated cases. If we were to base this off exporting the class declaration # we would have to handle the case where the exported client is not generated. @@ -558,6 +569,95 @@ def _generate_root_client( project.write_source_file(source_file=raw_client_source_file, filepath=raw_client_filepath) return generated_root_client + def _generate_exported_client_wrapper( + self, + context: SdkGeneratorContext, + custom_config: SDKCustomConfig, + project: Project, + generated_root_client: GeneratedRootClient, + generator_exec_wrapper: GeneratorExecWrapper, + ) -> None: + exported_module = custom_config.client.exported_filename.removesuffix(".py") + exported_sync_class = context.get_class_name_for_exported_root_client() + exported_async_class = "Async" + exported_sync_class + + filepath = Filepath( + directories=(), + file=Filepath.FilepathPart(module_name=exported_module), + ) + source_file = context.source_file_factory.create( + project=project, filepath=filepath, generator_exec_wrapper=generator_exec_wrapper + ) + + generated_filepath = context.get_filepath_for_generated_root_client() + generated_sync_name = context.get_class_name_for_generated_root_client() + generated_async_name = "Async" + generated_sync_name + + sync_base_class_ref = AST.ClassReference( + import_=AST.ReferenceImport( + module=generated_filepath.to_module(), + named_import=generated_sync_name, + ), + qualified_name_excluding_import=(), + ) + async_base_class_ref = AST.ClassReference( + import_=AST.ReferenceImport( + module=generated_filepath.to_module(), + named_import=generated_async_name, + ), + qualified_name_excluding_import=(), + ) + + sync_class = self._create_wrapper_class_declaration( + class_name=exported_sync_class, + base_class_ref=sync_base_class_ref, + root_client=generated_root_client.sync_client, + ) + async_class = self._create_wrapper_class_declaration( + class_name=exported_async_class, + base_class_ref=async_base_class_ref, + root_client=generated_root_client.async_client, + ) + + source_file.add_class_declaration(declaration=sync_class, should_export=True) + source_file.add_class_declaration(declaration=async_class, should_export=True) + + project.write_source_file(source_file=source_file, filepath=filepath) + + @staticmethod + def _create_wrapper_class_declaration( + *, + class_name: str, + base_class_ref: AST.ClassReference, + root_client: "RootClient", + ) -> AST.ClassDeclaration: + params = root_client.init_parameters if root_client.init_parameters is not None else root_client.parameters + + named_params = [ + AST.NamedFunctionParameter( + name=param.constructor_parameter_name, + type_hint=param.type_hint, + initializer=param.initializer, + ) + for param in params + ] + + def write_super_init(writer: AST.NodeWriter) -> None: + writer.write_line("super().__init__(") + with writer.indent(): + for param in params: + writer.write_line(f"{param.constructor_parameter_name}={param.constructor_parameter_name},") + writer.write_line(")") + + return AST.ClassDeclaration( + name=class_name, + extends=[base_class_ref], + constructor=AST.ClassConstructor( + signature=AST.FunctionSignature(named_parameters=named_params), + body=AST.CodeWriter(write_super_init), + ), + ) + def _generate_subpackage_client( self, context: SdkGeneratorContext, diff --git a/generators/swift/sdk/src/generators/client/util/__test__/format-endpoint-path-for-swift.test.ts b/generators/swift/sdk/src/generators/client/util/__test__/format-endpoint-path-for-swift.test.ts index 754d205d95c6..6c96d3cdc05c 100644 --- a/generators/swift/sdk/src/generators/client/util/__test__/format-endpoint-path-for-swift.test.ts +++ b/generators/swift/sdk/src/generators/client/util/__test__/format-endpoint-path-for-swift.test.ts @@ -1,43 +1,151 @@ -import { readdirSync } from "node:fs"; -import { resolve } from "node:path"; - -import { AbsoluteFilePath } from "@fern-api/fs-utils"; -import { createSampleIr } from "@fern-api/test-utils"; -import { IntermediateRepresentation } from "@fern-fern/ir-v59-sdk/api"; import { formatEndpointPathForSwift } from "../format-endpoint-path-for-swift.js"; +import { EndpointPathInput } from "../parse-endpoint-path.js"; -const pathToTestDefinitions = resolve(__dirname, "../../../../../../../../test-definitions/fern/apis"); -const testDefinitionNames = readdirSync(pathToTestDefinitions, { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name); - -async function getIRForTestDefinition(testDefinitionName: string): Promise { - const absolutePathToWorkspace = AbsoluteFilePath.of(resolve(pathToTestDefinitions, testDefinitionName)); - return (await createSampleIr(absolutePathToWorkspace, { - version: "v59" // make sure to upgrade this when the IR version is upgraded - })) as IntermediateRepresentation; +function makeEndpoint(opts: { + head: string; + parts?: Array<{ + paramOriginalName: string; + paramCamelCase: string; + tail: string; + }>; +}): EndpointPathInput { + const parts = opts.parts ?? []; + return { + fullPath: { + head: opts.head, + parts: parts.map((p) => ({ + pathParameter: p.paramOriginalName, + tail: p.tail + })) + }, + allPathParameters: parts.map((p) => ({ + name: { + originalName: p.paramOriginalName, + camelCase: { unsafeName: p.paramCamelCase } + }, + docs: undefined + })) + }; } -describe.each(testDefinitionNames)("formatEndpointPathForSwift - %s", (testDefinitionName) => { - // This allows us to conveniently review the formatted endpoint paths for every test definition in a single location - - it("correctly formats all endpoint paths for definition", async () => { - const ir = await getIRForTestDefinition(testDefinitionName); - const endpointPathsByService = Object.fromEntries( - Object.entries(ir.services).map(([serviceName, service]) => { - return [ - serviceName, - // biome-ignore lint/suspicious/noExplicitAny: allow explicit any - service.endpoints.map((endpoint) => formatEndpointPathForSwift(endpoint as any)) - ] as const; - }) - ); - const fileContents = Object.entries(endpointPathsByService) - .map(([serviceName, paths]) => ({ serviceName, serviceContent: paths.map((p) => `"${p}"`).join("\n") })) - .map(({ serviceName, serviceContent }) => `// ${serviceName}\n${serviceContent}`) - .join("\n\n"); - await expect(fileContents).toMatchFileSnapshot( - `snapshots/formatted-endpoint-paths/${testDefinitionName}.swift` +describe("formatEndpointPathForSwift", () => { + // --- Basic path formatting --- + + it("formats a static path with no parameters", () => { + const endpoint = makeEndpoint({ head: "/users" }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/users"); + }); + + it("formats a path with a single path parameter", () => { + const endpoint = makeEndpoint({ + head: "/users/", + parts: [{ paramOriginalName: "user_id", paramCamelCase: "userId", tail: "" }] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/users/\\(userId)"); + }); + + it("formats a path with multiple path parameters", () => { + const endpoint = makeEndpoint({ + head: "/organizations/", + parts: [ + { paramOriginalName: "org_id", paramCamelCase: "orgId", tail: "/users/" }, + { paramOriginalName: "user_id", paramCamelCase: "userId", tail: "" } + ] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/organizations/\\(orgId)/users/\\(userId)"); + }); + + // --- Leading slash handling --- + + it("prepends a leading slash if missing", () => { + const endpoint = makeEndpoint({ head: "users" }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/users"); + }); + + it("prepends a leading slash when head is empty and path parameter starts the path", () => { + const endpoint = makeEndpoint({ + head: "", + parts: [{ paramOriginalName: "id", paramCamelCase: "id", tail: "" }] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/\\(id)"); + }); + + // --- Trailing slash handling --- + + it("strips a trailing slash", () => { + const endpoint = makeEndpoint({ head: "/users/" }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/users"); + }); + + it("preserves a single slash as the root path", () => { + const endpoint = makeEndpoint({ head: "/" }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/"); + }); + + it("strips trailing slash after path parameter tail", () => { + const endpoint = makeEndpoint({ + head: "/", + parts: [{ paramOriginalName: "id", paramCamelCase: "id", tail: "/details/" }] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/\\(id)/details"); + }); + + // --- Empty head handling --- + + it("handles an empty head with no parts", () => { + const endpoint = makeEndpoint({ head: "" }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/"); + }); + + // --- Missing path parameter declaration --- + + it("skips path parameters not declared in allPathParameters but keeps surrounding literals", () => { + const endpoint: EndpointPathInput = { + fullPath: { + head: "/items/", + parts: [{ pathParameter: "unknown_param", tail: "/details" }] + }, + allPathParameters: [] + }; + expect(formatEndpointPathForSwift(endpoint)).toBe("/items//details"); + }); + + // --- Consecutive path parameters --- + + it("handles consecutive path parameters with no separator", () => { + const endpoint = makeEndpoint({ + head: "/", + parts: [ + { paramOriginalName: "key", paramCamelCase: "key", tail: "" }, + { paramOriginalName: "value", paramCamelCase: "value", tail: "" } + ] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/\\(key)\\(value)"); + }); + + // --- Tail after path parameter --- + + it("handles a path parameter followed by a tail segment", () => { + const endpoint = makeEndpoint({ + head: "/", + parts: [{ paramOriginalName: "id", paramCamelCase: "id", tail: "/details" }] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe("/\\(id)/details"); + }); + + // --- Complex / deeply nested --- + + it("handles deeply nested paths with multiple segments and parameters", () => { + const endpoint = makeEndpoint({ + head: "/api/v1/", + parts: [ + { paramOriginalName: "tenant_id", paramCamelCase: "tenantId", tail: "/resources/" }, + { paramOriginalName: "resource_id", paramCamelCase: "resourceId", tail: "/actions/" }, + { paramOriginalName: "action_id", paramCamelCase: "actionId", tail: "" } + ] + }); + expect(formatEndpointPathForSwift(endpoint)).toBe( + "/api/v1/\\(tenantId)/resources/\\(resourceId)/actions/\\(actionId)" ); - }, 10_000); + }); }); diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/accept-header.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/accept-header.swift deleted file mode 100644 index 559eaaec8b6d..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/accept-header.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/container" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias-extends.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias-extends.swift deleted file mode 100644 index fdfc1a433999..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias-extends.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/extends/extended-inline-request-body" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias.swift deleted file mode 100644 index 1c6ea4ba406f..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/alias.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/\(typeID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/any-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/any-auth.swift deleted file mode 100644 index 1177dff2cbd2..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/any-auth.swift +++ /dev/null @@ -1,6 +0,0 @@ -// service_auth -"/token" - -// service_user -"/users" -"/admins" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/api-wide-base-path.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/api-wide-base-path.swift deleted file mode 100644 index aa9e6d484f14..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/api-wide-base-path.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/test/\(pathParam)/\(serviceParam)/\(endpointParam)/\(resourceParam)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/audiences.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/audiences.swift deleted file mode 100644 index ab9b633ae30d..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/audiences.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_folder-a/service -"/" - -// service_folder-d/service -"/partner-path" - -// service_foo -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/auth-environment-variables.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/auth-environment-variables.swift deleted file mode 100644 index 14c17cc35312..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/auth-environment-variables.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_service -"/apiKey" -"/apiKeyInHeader" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth-environment-variables.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth-environment-variables.swift deleted file mode 100644 index 3b814aedef10..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth-environment-variables.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_basic-auth -"/basic-auth" -"/basic-auth" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth.swift deleted file mode 100644 index 3b814aedef10..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/basic-auth.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_basic-auth -"/basic-auth" -"/basic-auth" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bearer-token-environment-variable.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bearer-token-environment-variable.swift deleted file mode 100644 index 5641b49606df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bearer-token-environment-variable.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/apiKey" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-download.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-download.swift deleted file mode 100644 index 71b18b7a33a5..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-download.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_service -"/snippet" -"/download-content/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-upload.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-upload.swift deleted file mode 100644 index 866db57aba0c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/bytes-upload.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/upload-content" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/circular-references-advanced.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/circular-references-advanced.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/circular-references.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/circular-references.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/client-side-params.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/client-side-params.swift deleted file mode 100644 index 9cd305d2a6e7..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/client-side-params.swift +++ /dev/null @@ -1,13 +0,0 @@ -// service_service -"/api/resources" -"/api/resources/\(resourceID)" -"/api/resources/search" -"/api/users" -"/api/users/\(userID)" -"/api/users" -"/api/users/\(userID)" -"/api/users/\(userID)" -"/api/connections" -"/api/connections/\(connectionID)" -"/api/clients" -"/api/clients/\(clientID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/content-type.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/content-type.swift deleted file mode 100644 index 9605144e5cee..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/content-type.swift +++ /dev/null @@ -1,6 +0,0 @@ -// service_service -"/" -"/complex/\(id)" -"/named-mixed/\(id)" -"/optional-merge-patch-test" -"/regular/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/cross-package-type-names.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/cross-package-type-names.swift deleted file mode 100644 index dcd4488c9b01..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/cross-package-type-names.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_folder-a/service -"/" - -// service_folder-d/service -"/" - -// service_foo -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-grpc-proto-exhaustive.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-grpc-proto-exhaustive.swift deleted file mode 100644 index 5293755f35cc..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-grpc-proto-exhaustive.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_dataservice -"/foo" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-grpc-proto.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-grpc-proto.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-collision.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-collision.swift deleted file mode 100644 index b9c1074f9cb7..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-collision.swift +++ /dev/null @@ -1,7 +0,0 @@ -// service_ -"/users" -"/users" - -// service_System -"/users" -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-conflict.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-conflict.swift deleted file mode 100644 index 7d3be5cd96cb..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-namespace-conflict.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_tasktest -"/hello" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-property-access.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-property-access.swift deleted file mode 100644 index 0d3f2f4184c4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-property-access.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-readonly-request.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-readonly-request.swift deleted file mode 100644 index 5c183b9e003c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-readonly-request.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/vendors/batch" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-system-collision.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-system-collision.swift deleted file mode 100644 index a17e47b64a7e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-system-collision.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_ -"/users" -"/users" -"/users/empty" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-xml-entities.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-xml-entities.swift deleted file mode 100644 index 81ea339c04f0..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/csharp-xml-entities.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/timezone" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/custom-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/custom-auth.swift deleted file mode 100644 index bf5a1c2670fb..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/custom-auth.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_custom-auth -"/custom-auth" -"/custom-auth" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/dollar-string-examples.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/dollar-string-examples.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/empty-clients.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/empty-clients.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/endpoint-security-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/endpoint-security-auth.swift deleted file mode 100644 index 57f0eb71651a..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/endpoint-security-auth.swift +++ /dev/null @@ -1,11 +0,0 @@ -// service_auth -"/token" - -// service_user -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/enum.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/enum.swift deleted file mode 100644 index c2425fda1619..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/enum.swift +++ /dev/null @@ -1,15 +0,0 @@ -// service_headers -"/headers" - -// service_inlined-request -"/inlined" - -// service_multipart-form -"/multipart" - -// service_path-param -"/path/\(operand)/\(operandOrColor)" - -// service_query-param -"/query" -"/query-list" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/error-property.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/error-property.swift deleted file mode 100644 index f7085f71c4a6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/error-property.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_property-based-error -"/property-based-error" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/errors.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/errors.swift deleted file mode 100644 index 450b59159b6b..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/errors.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_simple -"/foo1" -"/foo2" -"/foo3" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/examples.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/examples.swift deleted file mode 100644 index b948dcb09c28..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/examples.swift +++ /dev/null @@ -1,20 +0,0 @@ -// service_ -"/" -"/" - -// service_file/notification/service -"/file/notification/\(notificationID)" - -// service_file/service -"/file/\(filename)" - -// service_health/service -"/check/\(id)" -"/ping" - -// service_service -"/movie/\(movieID)" -"/movie" -"/metadata" -"/big-entity" -"/refresh-token" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/exhaustive.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/exhaustive.swift deleted file mode 100644 index 9f3e85a04c72..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/exhaustive.swift +++ /dev/null @@ -1,81 +0,0 @@ -// service_endpoints/container -"/container/list-of-primitives" -"/container/list-of-objects" -"/container/set-of-primitives" -"/container/set-of-objects" -"/container/map-prim-to-prim" -"/container/map-prim-to-object" -"/container/map-prim-to-union" -"/container/opt-objects" - -// service_endpoints/content-type -"/foo/bar" -"/foo/baz" - -// service_endpoints/enum -"/enum" - -// service_endpoints/http-methods -"/http-methods/\(id)" -"/http-methods" -"/http-methods/\(id)" -"/http-methods/\(id)" -"/http-methods/\(id)" - -// service_endpoints/object -"/object/get-and-return-with-optional-field" -"/object/get-and-return-with-required-field" -"/object/get-and-return-with-map-of-map" -"/object/get-and-return-nested-with-optional-field" -"/object/get-and-return-nested-with-required-field/\(string)" -"/object/get-and-return-nested-with-required-field-list" -"/object/get-and-return-with-datetime-like-string" - -// service_endpoints/pagination -"/pagination" - -// service_endpoints/params -"/params/path/\(param)" -"/params/path/\(param)" -"/params" -"/params" -"/params/path-query/\(param)" -"/params/path-query/\(param)" -"/params/path/\(param)" -"/params/path/\(param)" - -// service_endpoints/primitive -"/primitive/string" -"/primitive/integer" -"/primitive/long" -"/primitive/double" -"/primitive/boolean" -"/primitive/datetime" -"/primitive/date" -"/primitive/uuid" -"/primitive/base64" - -// service_endpoints/put -"/\(id)" - -// service_endpoints/union -"/union" - -// service_endpoints/urls -"/urls/MixedCase" -"/urls/no-ending-slash" -"/urls/with-ending-slash" -"/urls/with_underscores" - -// service_inlined-requests -"/req-bodies/object" - -// service_no-auth -"/no-auth" - -// service_no-req-body -"/no-req-body" -"/no-req-body" - -// service_req-with-headers -"/test-headers/custom-header" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extends.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extends.swift deleted file mode 100644 index fdfc1a433999..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extends.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/extends/extended-inline-request-body" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extra-properties.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extra-properties.swift deleted file mode 100644 index 236a6d0a182c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/extra-properties.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/user" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-download.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-download.swift deleted file mode 100644 index 5fd4ff59efa6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-download.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_service -"/snippet" -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload-openapi.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload-openapi.swift deleted file mode 100644 index 204943cd2466..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload-openapi.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_fileUploadExample -"/upload-file" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload.swift deleted file mode 100644 index eb58e64c10ed..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/file-upload.swift +++ /dev/null @@ -1,13 +0,0 @@ -// service_service -"/" -"/just-file" -"/just-file-with-query-params" -"/just-file-with-optional-query-params" -"/with-content-type" -"/with-form-encoding" -"/" -"/optional-args" -"/inline-type" -"/with-json-property" -"/snippet" -"/with-literal-enum" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/folders.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/folders.swift deleted file mode 100644 index 18d3e8643874..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/folders.swift +++ /dev/null @@ -1,15 +0,0 @@ -// service_ -"/" - -// service_a/b -"/" - -// service_a/c -"/" - -// service_folder -"/" - -// service_folder/service -"/service" -"/service" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-bytes-request.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-bytes-request.swift deleted file mode 100644 index 866db57aba0c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-bytes-request.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/upload-content" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-content-type.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-content-type.swift deleted file mode 100644 index c510a8fc29db..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-content-type.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_imdb -"/movies/create-movie" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-optional-literal-alias.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-optional-literal-alias.swift deleted file mode 100644 index b9d21429e3ee..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-optional-literal-alias.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/search" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-undiscriminated-union-wire-tests.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-undiscriminated-union-wire-tests.swift deleted file mode 100644 index 7045440065b3..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/go-undiscriminated-union-wire-tests.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/rerank" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth-environment-variable.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth-environment-variable.swift deleted file mode 100644 index 5641b49606df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth-environment-variable.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/apiKey" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth.swift deleted file mode 100644 index 5641b49606df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/header-auth.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/apiKey" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/http-head.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/http-head.swift deleted file mode 100644 index d7b065662662..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/http-head.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_user -"/users" -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/idempotency-headers.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/idempotency-headers.swift deleted file mode 100644 index 8aaa551ade08..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/idempotency-headers.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_payment -"/payment" -"/payment/\(paymentID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/imdb.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/imdb.swift deleted file mode 100644 index 6ad601b993ae..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/imdb.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_imdb -"/movies/create-movie" -"/movies/\(movieID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-explicit.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-explicit.swift deleted file mode 100644 index a972e0ade89e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-explicit.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token/refresh" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-api-key.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-api-key.swift deleted file mode 100644 index ecb7a698bf6a..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-api-key.swift +++ /dev/null @@ -1,11 +0,0 @@ -// service_auth -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-no-expiry.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-no-expiry.swift deleted file mode 100644 index a972e0ade89e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-no-expiry.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token/refresh" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-reference.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-reference.swift deleted file mode 100644 index a972e0ade89e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit-reference.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token/refresh" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit.swift deleted file mode 100644 index a972e0ade89e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inferred-auth-implicit.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token/refresh" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inline-enum-request.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inline-enum-request.swift deleted file mode 100644 index c324af05d81e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/inline-enum-request.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/convert" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-builder-extension.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-builder-extension.swift deleted file mode 100644 index 2b741f079c36..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-builder-extension.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/api/hello" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-custom-package-prefix.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-custom-package-prefix.swift deleted file mode 100644 index 6ad601b993ae..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-custom-package-prefix.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_imdb -"/movies/create-movie" -"/movies/\(movieID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-default-timeout.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-default-timeout.swift deleted file mode 100644 index a70f2a344f97..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-default-timeout.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/user" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-inline-types.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-inline-types.swift deleted file mode 100644 index 08f4fd5496cc..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-inline-types.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_ -"/root/root" -"/root/discriminated-union" -"/root/undiscriminated-union" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-nullable-named-request-types.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-nullable-named-request-types.swift deleted file mode 100644 index d59f3395ae07..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-nullable-named-request-types.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/postWithNullableNamedRequestBodyType/\(id)" -"/postWithNonNullableNamedRequestBodyType/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-nullable-query-params.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-nullable-query-params.swift deleted file mode 100644 index 7195ccca6a80..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-nullable-query-params.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/api/search" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-query-params-overloads.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-query-params-overloads.swift deleted file mode 100644 index ca0c399c5155..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-optional-query-params-overloads.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_ -"/api/users/\(userID)/insurance" -"/api/policies/search" -"/api/policies" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-output-directory.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-output-directory.swift deleted file mode 100644 index c60e1fbf9d8c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-output-directory.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/users/\(userID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-pagination-deep-cursor-path.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-pagination-deep-cursor-path.swift deleted file mode 100644 index f92257877060..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-pagination-deep-cursor-path.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_deep-cursor-path -"/" -"/" -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-path-param-key-conflict.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-path-param-key-conflict.swift deleted file mode 100644 index 0a94bc5ad7d3..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-path-param-key-conflict.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/\(key)/\(value)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-required-body-optional-headers.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-required-body-optional-headers.swift deleted file mode 100644 index 563e699fa2fc..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-required-body-optional-headers.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_ -"/api/users" -"/api/users/\(userID)" -"/api/users/with-options" -"/api/users/required-header" -"/api/users/required-query" -"/api/users" -"/api/users/inlined" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-single-property-endpoint.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-single-property-endpoint.swift deleted file mode 100644 index 1323c2721ff6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-single-property-endpoint.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_single-property -"/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-staged-builder-ordering.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-staged-builder-ordering.swift deleted file mode 100644 index 39f4f5c36b56..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-staged-builder-ordering.swift +++ /dev/null @@ -1,6 +0,0 @@ -// service_service -"/staged-builder/simple" -"/staged-builder/medium" -"/staged-builder/complex" -"/staged-builder/mixed" -"/staged-builder/parent" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-streaming-accept-header.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-streaming-accept-header.swift deleted file mode 100644 index ef38bd97ca8b..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-streaming-accept-header.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_dummy -"/generate-stream" -"/generate" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-with-property-conflict.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/java-with-property-conflict.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/license.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/license.swift deleted file mode 100644 index 9886cbe70cc1..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/license.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/literal.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/literal.swift deleted file mode 100644 index d54f3b7d7862..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/literal.swift +++ /dev/null @@ -1,14 +0,0 @@ -// service_headers -"/headers" - -// service_inlined -"/inlined" - -// service_path -"/path/\(id)" - -// service_query -"/query" - -// service_reference -"/reference" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/literals-unions.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/literals-unions.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-case.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-case.swift deleted file mode 100644 index 1c335d81aefb..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-case.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_service -"/resource/\(resourceID)" -"/resource" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-file-directory.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-file-directory.swift deleted file mode 100644 index 73d8c580f2dd..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/mixed-file-directory.swift +++ /dev/null @@ -1,11 +0,0 @@ -// service_organization -"/organizations" - -// service_user -"/users" - -// service_user/events -"/users/events" - -// service_user/events/metadata -"/users/events/metadata" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-grouping.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-grouping.swift deleted file mode 100644 index d1df6e8124e8..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-grouping.swift +++ /dev/null @@ -1,10 +0,0 @@ -// service_ -"/wallets" -"/wallets/\(walletID)" -"/wallets/\(walletID)/balance" -"/accounts" -"/transactions" -"/transactions/\(transactionID)" -"/payments" -"/payments/\(paymentID)/refund" -"/refunds" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-no-grouping.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-no-grouping.swift deleted file mode 100644 index d1df6e8124e8..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-api-environment-no-grouping.swift +++ /dev/null @@ -1,10 +0,0 @@ -// service_ -"/wallets" -"/wallets/\(walletID)" -"/wallets/\(walletID)/balance" -"/accounts" -"/transactions" -"/transactions/\(transactionID)" -"/payments" -"/payments/\(paymentID)/refund" -"/refunds" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-line-docs.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-line-docs.swift deleted file mode 100644 index d202e08630e4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-line-docs.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_user -"/users/\(userID)" -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment-no-default.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment-no-default.swift deleted file mode 100644 index 49892658921f..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment-no-default.swift +++ /dev/null @@ -1,5 +0,0 @@ -// service_ec2 -"/ec2/boot" - -// service_s3 -"/s3/presigned-url" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment.swift deleted file mode 100644 index 49892658921f..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multi-url-environment.swift +++ /dev/null @@ -1,5 +0,0 @@ -// service_ec2 -"/ec2/boot" - -// service_s3 -"/s3/presigned-url" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multiple-request-bodies.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multiple-request-bodies.swift deleted file mode 100644 index f67033910b78..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/multiple-request-bodies.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/documents/upload" -"/documents/upload" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-environment.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-environment.swift deleted file mode 100644 index 4f6bc45b92df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-environment.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_dummy -"/dummy" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-retries.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-retries.swift deleted file mode 100644 index f9d4b8d30ac1..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/no-retries.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_retries -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-allof-extends.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-allof-extends.swift deleted file mode 100644 index 6fa96e93070d..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-allof-extends.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/test" -"/test" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-optional.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-optional.swift deleted file mode 100644 index 0006a005e278..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-optional.swift +++ /dev/null @@ -1,14 +0,0 @@ -// service_nullable-optional -"/api/users/\(userID)" -"/api/users" -"/api/users/\(userID)" -"/api/users" -"/api/users/search" -"/api/profiles/complex" -"/api/profiles/complex/\(profileID)" -"/api/profiles/complex/\(profileID)" -"/api/test/deserialization" -"/api/users/filter" -"/api/users/\(userID)/notifications" -"/api/users/\(userID)/tags" -"/api/search" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-request-body.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-request-body.swift deleted file mode 100644 index 6c33721fc316..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable-request-body.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_testGroup -"/optional-request-body/\(pathParam)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable.swift deleted file mode 100644 index ac7c13872123..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/nullable.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_nullable -"/users" -"/users" -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-custom.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-custom.swift deleted file mode 100644 index 3fae7782a151..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-custom.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-default.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-default.swift deleted file mode 100644 index ecb7a698bf6a..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-default.swift +++ /dev/null @@ -1,11 +0,0 @@ -// service_auth -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-environment-variables.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-environment-variables.swift deleted file mode 100644 index 3fae7782a151..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-environment-variables.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-mandatory-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-mandatory-auth.swift deleted file mode 100644 index 804bea941476..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-mandatory-auth.swift +++ /dev/null @@ -1,9 +0,0 @@ -// service_auth -"/token" -"/token" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-nested-root.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-nested-root.swift deleted file mode 100644 index ecb7a698bf6a..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-nested-root.swift +++ /dev/null @@ -1,11 +0,0 @@ -// service_auth -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-reference.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-reference.swift deleted file mode 100644 index 31f00656a0e3..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-reference.swift +++ /dev/null @@ -1,5 +0,0 @@ -// service_auth -"/token" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-with-variables.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-with-variables.swift deleted file mode 100644 index 284a1e634e74..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials-with-variables.swift +++ /dev/null @@ -1,15 +0,0 @@ -// service_auth -"/token" -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_service -"/service/\(endpointParam)" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials.swift deleted file mode 100644 index 3fae7782a151..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/oauth-client-credentials.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_auth -"/token" -"/token" - -// service_nested-no-auth/api -"/nested-no-auth/get-something" - -// service_nested/api -"/nested/get-something" - -// service_simple -"/get-something" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/object.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/object.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/objects-with-imports.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/objects-with-imports.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/optional.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/optional.swift deleted file mode 100644 index cc9a25f1519c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/optional.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_optional -"/send-optional-body" -"/send-optional-typed-body" -"/deploy/\(actionID)/versions/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/package-yml.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/package-yml.swift deleted file mode 100644 index 2c06a04d4ded..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/package-yml.swift +++ /dev/null @@ -1,5 +0,0 @@ -// service_ -"/\(id)" - -// service_service -"/\(id)//\(nestedID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-custom.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-custom.swift deleted file mode 100644 index c4aea3693b95..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-custom.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_users -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-uri-path.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-uri-path.swift deleted file mode 100644 index e0b17fc8b9a6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination-uri-path.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_users -"/users/uri" -"/users/path" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination.swift deleted file mode 100644 index bd0831e50218..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/pagination.swift +++ /dev/null @@ -1,33 +0,0 @@ -// service_complex -"/\(index)/conversations/search" - -// service_inline-users/inline-users -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" -"/inline-users" - -// service_users -"/users" -"/users" -"/users" -"/users/top-level-cursor" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users" -"/users/optional-data" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/path-parameters.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/path-parameters.swift deleted file mode 100644 index 7e9df769e778..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/path-parameters.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_organizations -"/\(tenantID)/organizations/\(organizationID)" -"/\(tenantID)/organizations/\(organizationID)/users/\(userID)" -"/\(tenantID)/organizations/\(organizationID)/search" - -// service_user -"/\(tenantID)/user/\(userID)" -"/\(tenantID)/user" -"/\(tenantID)/user/\(userID)" -"/\(tenantID)/user/\(userID)/search" -"/\(tenantID)/user/\(userID)/metadata/v\(version)" -"/\(tenantID)/user/\(userID)/specifics/\(version)/\(thought)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/plain-text.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/plain-text.swift deleted file mode 100644 index 13748278446a..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/plain-text.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/text" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/property-access.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/property-access.swift deleted file mode 100644 index 0d3f2f4184c4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/property-access.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/users" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/public-object.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/public-object.swift deleted file mode 100644 index 2220ed0b48ba..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/public-object.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/helloworld.txt" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-backslash-escape.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-backslash-escape.swift deleted file mode 100644 index 00441012f5e7..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-backslash-escape.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/users/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-mypy-exclude.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-mypy-exclude.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-positional-single-property.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-positional-single-property.swift deleted file mode 100644 index fac19a984c14..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-positional-single-property.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/create" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-streaming-parameter-openapi.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-streaming-parameter-openapi.swift deleted file mode 100644 index 00216c32ae90..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/python-streaming-parameter-openapi.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/chat" -"/chat" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi-as-objects.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi-as-objects.swift deleted file mode 100644 index 39fde7d49de4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi-as-objects.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/user/getUsername" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi.swift deleted file mode 100644 index 39fde7d49de4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters-openapi.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/user/getUsername" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters.swift deleted file mode 100644 index 236a6d0a182c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/query-parameters.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/user" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/request-parameters.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/request-parameters.swift deleted file mode 100644 index 1eec57fa05c6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/request-parameters.swift +++ /dev/null @@ -1,5 +0,0 @@ -// service_user -"/user/username" -"/user/username-referenced" -"/user/username-optional" -"/user" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/required-nullable.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/required-nullable.swift deleted file mode 100644 index dbc9f5f67ae1..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/required-nullable.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/foo" -"/foo/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/reserved-keywords.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/reserved-keywords.swift deleted file mode 100644 index 39189524ef02..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/reserved-keywords.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_package -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/response-property.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/response-property.swift deleted file mode 100644 index 57d4be339f42..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/response-property.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_service -"/movie" -"/movie" -"/movie" -"/movie" -"/movie" -"/movie" -"/movie" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ruby-reserved-word-properties.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ruby-reserved-word-properties.swift deleted file mode 100644 index 688f79e3a46e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ruby-reserved-word-properties.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/ruby-reserved-word-properties/getFoo" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-event-examples.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-event-examples.swift deleted file mode 100644 index 85945355e75e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-event-examples.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_completions -"/stream" -"/stream-events" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-events.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-events.swift deleted file mode 100644 index 635cec4d24a6..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-sent-events.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_completions -"/stream" -"/stream-no-terminator" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-url-templating.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-url-templating.swift deleted file mode 100644 index b786dafb1509..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/server-url-templating.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_ -"/users" -"/users/\(userID)" -"/auth/token" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-api.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-api.swift deleted file mode 100644 index 00441012f5e7..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-api.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/users/\(id)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-fhir.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-fhir.swift deleted file mode 100644 index 966150d43825..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/simple-fhir.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/account/\(accountID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-default.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-default.swift deleted file mode 100644 index 4f6bc45b92df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-default.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_dummy -"/dummy" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-no-default.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-no-default.swift deleted file mode 100644 index 4f6bc45b92df..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/single-url-environment-no-default.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_dummy -"/dummy" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming-parameter.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming-parameter.swift deleted file mode 100644 index e84824763c9c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming-parameter.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_dummy -"/generate" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming.swift deleted file mode 100644 index ef38bd97ca8b..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/streaming.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_dummy -"/generate-stream" -"/generate" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/trace.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/trace.swift deleted file mode 100644 index 049c51cc334c..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/trace.swift +++ /dev/null @@ -1,54 +0,0 @@ -// service_v2 -"/" - -// service_admin -"/admin/store-test-submission-status/\(submissionID)" -"/admin/store-test-submission-status-v2/\(submissionID)" -"/admin/store-workspace-submission-status/\(submissionID)" -"/admin/store-workspace-submission-status-v2/\(submissionID)" -"/admin/store-test-trace/submission/\(submissionID)/testCase/\(testCaseID)" -"/admin/store-test-trace-v2/submission/\(submissionID)/testCase/\(testCaseID)" -"/admin/store-workspace-trace/submission/\(submissionID)" -"/admin/store-workspace-trace-v2/submission/\(submissionID)" - -// service_homepage -"/homepage-problems" -"/homepage-problems" - -// service_migration -"/migration-info/all" - -// service_playlist -"/v2/playlist/\(serviceParam)/create" -"/v2/playlist/\(serviceParam)/all" -"/v2/playlist/\(serviceParam)/\(playlistID)" -"/v2/playlist/\(serviceParam)/\(playlistID)" -"/v2/playlist/\(serviceParam)/\(playlistID)" - -// service_problem -"/problem-crud/create" -"/problem-crud/update/\(problemID)" -"/problem-crud/delete/\(problemID)" -"/problem-crud/default-starter-files" - -// service_submission -"/sessions/create-session/\(language)" -"/sessions/\(sessionID)" -"/sessions/stop/\(sessionID)" -"/sessions/execution-sessions-state" - -// service_sysprop -"/sysprop/num-warm-instances/\(language)/\(numWarmInstances)" -"/sysprop/num-warm-instances" - -// service_v2/problem -"/problems-v2/lightweight-problem-info" -"/problems-v2/problem-info" -"/problems-v2/problem-info/\(problemID)" -"/problems-v2/problem-info/\(problemID)/version/\(problemVersion)" - -// service_v2/v3/problem -"/problems-v2/lightweight-problem-info" -"/problems-v2/problem-info" -"/problems-v2/problem-info/\(problemID)" -"/problems-v2/problem-info/\(problemID)/version/\(problemVersion)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-express-casing.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-express-casing.swift deleted file mode 100644 index 6ad601b993ae..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-express-casing.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_imdb -"/movies/create-movie" -"/movies/\(movieID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-extra-properties.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-extra-properties.swift deleted file mode 100644 index ec1b793ca624..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-extra-properties.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/user" -"/user" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-inline-types.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-inline-types.swift deleted file mode 100644 index 08f4fd5496cc..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/ts-inline-types.swift +++ /dev/null @@ -1,4 +0,0 @@ -// service_ -"/root/root" -"/root/discriminated-union" -"/root/undiscriminated-union" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-union-with-response-property.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-union-with-response-property.swift deleted file mode 100644 index 0b0c4abd96ce..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-union-with-response-property.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/union" -"/unions" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-unions.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-unions.swift deleted file mode 100644 index 6fc55677c97e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/undiscriminated-unions.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_union -"/" -"/metadata" -"/metadata" -"/call" -"/duplicate" -"/nested" -"/camel-case" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions-with-local-date.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions-with-local-date.swift deleted file mode 100644 index 7ffd7cd3185d..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions-with-local-date.swift +++ /dev/null @@ -1,12 +0,0 @@ -// service_bigunion -"/\(id)" -"/" -"/many" - -// service_types -"/time/\(id)" -"/time" - -// service_union -"/\(id)" -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions.swift deleted file mode 100644 index cf0b95e1a3f7..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unions.swift +++ /dev/null @@ -1,8 +0,0 @@ -// service_bigunion -"/\(id)" -"/" -"/many" - -// service_union -"/\(id)" -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unknown.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unknown.swift deleted file mode 100644 index c783d01e16a0..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/unknown.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_unknown -"/" -"/with-object" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/url-form-encoded.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/url-form-encoded.swift deleted file mode 100644 index e29f8b794d29..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/url-form-encoded.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_ -"/submit" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/validation.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/validation.swift deleted file mode 100644 index 2ea4f4c6524e..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/validation.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_ -"/create" -"/" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/variables.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/variables.swift deleted file mode 100644 index 2fdb2a0ac3d0..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/variables.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_service -"/\(endpointParam)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version-no-default.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version-no-default.swift deleted file mode 100644 index c5b5ba55bfb4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version-no-default.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/users/\(userID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version.swift deleted file mode 100644 index c5b5ba55bfb4..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/version.swift +++ /dev/null @@ -1,2 +0,0 @@ -// service_user -"/users/\(userID)" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/webhook-audience.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/webhook-audience.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket-bearer-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket-bearer-auth.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket-inferred-auth.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket-inferred-auth.swift deleted file mode 100644 index e95dc947c264..000000000000 --- a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket-inferred-auth.swift +++ /dev/null @@ -1,3 +0,0 @@ -// service_auth -"/token" -"/token/refresh" \ No newline at end of file diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/websocket.swift deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/generators/swift/sdk/src/generators/client/util/format-endpoint-path-for-swift.ts b/generators/swift/sdk/src/generators/client/util/format-endpoint-path-for-swift.ts index 28a98b4e1fad..1badf480fa24 100644 --- a/generators/swift/sdk/src/generators/client/util/format-endpoint-path-for-swift.ts +++ b/generators/swift/sdk/src/generators/client/util/format-endpoint-path-for-swift.ts @@ -1,7 +1,6 @@ -import { FernIr } from "@fern-fern/ir-sdk"; -import { parseEndpointPath } from "./parse-endpoint-path.js"; +import { EndpointPathInput, parseEndpointPath } from "./parse-endpoint-path.js"; -export function formatEndpointPathForSwift(endpoint: FernIr.HttpEndpoint): string { +export function formatEndpointPathForSwift(endpoint: EndpointPathInput): string { let path = ""; const { pathParts } = parseEndpointPath(endpoint); pathParts.forEach((pathPart) => { diff --git a/generators/swift/sdk/src/generators/client/util/parse-endpoint-path.ts b/generators/swift/sdk/src/generators/client/util/parse-endpoint-path.ts index f401124f9509..55506cbb19af 100644 --- a/generators/swift/sdk/src/generators/client/util/parse-endpoint-path.ts +++ b/generators/swift/sdk/src/generators/client/util/parse-endpoint-path.ts @@ -1,4 +1,13 @@ -import { FernIr } from "@fern-fern/ir-sdk"; +export interface EndpointPathInput { + fullPath: { + head: string; + parts: Array<{ pathParameter: string; tail: string }>; + }; + allPathParameters: Array<{ + name: { originalName: string; camelCase: { unsafeName: string } }; + docs: string | undefined; + }>; +} type PathPart = | { @@ -15,7 +24,7 @@ export type ParseEndpointPathResult = { pathParts: PathPart[]; }; -export function parseEndpointPath(endpoint: FernIr.HttpEndpoint): ParseEndpointPathResult { +export function parseEndpointPath(endpoint: EndpointPathInput): ParseEndpointPathResult { const pathParts: PathPart[] = []; const pathParameterInfosByOriginalName = Object.fromEntries( diff --git a/generators/typescript/express/cli/Dockerfile b/generators/typescript/express/cli/Dockerfile index 65d6f3c28e7f..40544b2adf1c 100644 --- a/generators/typescript/express/cli/Dockerfile +++ b/generators/typescript/express/cli/Dockerfile @@ -11,10 +11,10 @@ ENV PATH=$PNPM_HOME:$PATH RUN npm install -g pnpm@10.20.0 --force RUN npm install -g yarn@1.22.22 --force RUN pnpm install -g prettier@3.7.4 -RUN pnpm install -g oxfmt@0.27.0 +RUN pnpm install -g oxfmt@0.35.0 RUN pnpm install -g @biomejs/biome@2.4.3 -RUN pnpm install -g oxlint@1.42.0 -RUN pnpm install -g oxlint-tsgolint@0.11.4 +RUN pnpm install -g oxlint@1.50.0 +RUN pnpm install -g oxlint-tsgolint@0.14.2 WORKDIR /tmp/cache-warm @@ -30,9 +30,9 @@ RUN echo '{ \ "express": "4.18.2", \ "@biomejs/biome": "2.4.3", \ "prettier": "3.7.4", \ - "oxfmt": "0.27.0", \ - "oxlint": "1.42.0", \ - "oxlint-tsgolint": "0.11.4", \ + "oxfmt": "0.35.0", \ + "oxlint": "1.50.0", \ + "oxlint-tsgolint": "0.14.2", \ "typescript": "5.7.2", \ "url-join": "4.0.1" \ } \ diff --git a/generators/typescript/express/versions.yml b/generators/typescript/express/versions.yml index 6a5dab3b65b2..2b891f804ddb 100644 --- a/generators/typescript/express/versions.yml +++ b/generators/typescript/express/versions.yml @@ -1,4 +1,12 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- changelogEntry: + - summary: | + Update oxfmt to 0.35.0, oxlint to 1.50.0, and oxlint-tsgolint to 0.14.2. + type: chore + irVersion: 61 + createdAt: "2026-02-24" + version: 0.19.3 + - changelogEntry: - summary: | Update @biomejs/biome from 2.3.11 to 2.4.3. diff --git a/generators/typescript/sdk/cli/Dockerfile b/generators/typescript/sdk/cli/Dockerfile index 311b5a85b729..cdabbebc8a7e 100644 --- a/generators/typescript/sdk/cli/Dockerfile +++ b/generators/typescript/sdk/cli/Dockerfile @@ -12,10 +12,10 @@ RUN npm install -g pnpm@10.20.0 --force RUN npm install -g yarn@1.22.22 --force RUN pnpm add -g typescript@~5.7.2 \ prettier@3.7.4 \ - oxfmt@0.27.0 \ + oxfmt@0.35.0 \ @biomejs/biome@2.4.3 \ - oxlint@1.42.0 \ - oxlint-tsgolint@0.11.4 \ + oxlint@1.50.0 \ + oxlint-tsgolint@0.14.2 \ @fern-api/generator-cli@0.5.3 \ @types/node@^18.19.70 \ ts-loader@^9.5.1 \ diff --git a/generators/typescript/sdk/versions.yml b/generators/typescript/sdk/versions.yml index 1689affebade..fb5d97f31d28 100644 --- a/generators/typescript/sdk/versions.yml +++ b/generators/typescript/sdk/versions.yml @@ -1,4 +1,24 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.49.3 + changelogEntry: + - summary: | + Update oxfmt to 0.35.0, oxlint to 1.50.0, and oxlint-tsgolint to 0.14.2. + type: chore + createdAt: "2026-02-24" + irVersion: 63 + +- version: 3.49.2 + changelogEntry: + - summary: | + Replace lodash-es `template` with Eta for template file processing. The lodash + templating engine crashes on backticks (template literals) because it uses + `new Function()` internally. Eta is a modern, lightweight, zero-dependency + templating engine that properly handles backticks and uses the same `<% %>` + syntax, requiring no changes to existing template files. + type: fix + createdAt: "2026-02-24" + irVersion: 63 + - version: 3.49.1 changelogEntry: - summary: | diff --git a/generators/typescript/utils/commons/package.json b/generators/typescript/utils/commons/package.json index f97e3cd51ea7..ffd14bc07967 100644 --- a/generators/typescript/utils/commons/package.json +++ b/generators/typescript/utils/commons/package.json @@ -39,6 +39,7 @@ "@fern-fern/ir-sdk": "^64.0.0", "decompress": "catalog:", "esutils": "catalog:", + "eta": "^4.5.1", "glob": "catalog:", "immer": "^10.1.1", "lodash-es": "catalog:", diff --git a/generators/typescript/utils/commons/src/typescript-project/TypescriptProject.ts b/generators/typescript/utils/commons/src/typescript-project/TypescriptProject.ts index eeea0745c010..c623e8a8052c 100644 --- a/generators/typescript/utils/commons/src/typescript-project/TypescriptProject.ts +++ b/generators/typescript/utils/commons/src/typescript-project/TypescriptProject.ts @@ -384,14 +384,14 @@ export abstract class TypescriptProject { deps["@biomejs/biome"] = "2.4.3"; } if (this.linter === "oxlint") { - deps["oxlint"] = "1.42.0"; - deps["oxlint-tsgolint"] = "0.11.4"; + deps["oxlint"] = "1.50.0"; + deps["oxlint-tsgolint"] = "0.14.2"; } if (this.formatter === "prettier") { deps["prettier"] = "3.7.4"; } if (this.formatter === "oxfmt") { - deps["oxfmt"] = "0.27.0"; + deps["oxfmt"] = "0.35.0"; } return deps; } diff --git a/generators/typescript/utils/commons/src/writeTemplateFiles.ts b/generators/typescript/utils/commons/src/writeTemplateFiles.ts index 8e0523811107..860b3e1eb85b 100644 --- a/generators/typescript/utils/commons/src/writeTemplateFiles.ts +++ b/generators/typescript/utils/commons/src/writeTemplateFiles.ts @@ -1,7 +1,9 @@ +import { Eta } from "eta"; import * as fs from "fs/promises"; -import { template } from "lodash-es"; import * as path from "path"; +const eta = new Eta({ autoEscape: false, useWith: true, autoTrim: false }); + export async function writeTemplateFiles(directory: string, templateVariables: Record): Promise { const templateFiles = await findTemplateFiles(directory); @@ -36,8 +38,7 @@ async function processTemplateFile( templateVariables: Record ): Promise { const templateContent = await fs.readFile(templateFilePath, "utf8"); - const compiledTemplate = template(templateContent); - const content = compiledTemplate(templateVariables); + const content = eta.renderString(templateContent, templateVariables); const outputFilePath = templateFilePath.replace(/\.template\./, "."); await fs.writeFile(outputFilePath, content, "utf8"); await fs.unlink(templateFilePath); diff --git a/generators/typescript/utils/core-utilities/src/core/form-data-utils/FormDataWrapper.template.ts b/generators/typescript/utils/core-utilities/src/core/form-data-utils/FormDataWrapper.template.ts index 2477eb175ff6..f4bf09b0cee7 100644 --- a/generators/typescript/utils/core-utilities/src/core/form-data-utils/FormDataWrapper.template.ts +++ b/generators/typescript/utils/core-utilities/src/core/form-data-utils/FormDataWrapper.template.ts @@ -300,7 +300,7 @@ async function streamToBuffer(stream: unknown): Promise { } throw new Error( - "Unsupported stream type: " + typeof stream + ". Expected Node.js Readable stream or Web ReadableStream.", + `Unsupported stream type: ${typeof stream}. Expected Node.js Readable stream or Web ReadableStream.`, ); } @@ -335,4 +335,4 @@ async function convertToBlob(value: unknown, contentType?: string): Promise "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } @@ -125,27 +125,27 @@ expect.extend({ if (pass) { return { - message: () => "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } @@ -167,4 +167,4 @@ declare global { } export {}; -<% } %> \ No newline at end of file +<% } %> diff --git a/generators/typescript/utils/core-utilities/tests/unit/form-data-utils/formDataWrapper.test.template.ts b/generators/typescript/utils/core-utilities/tests/unit/form-data-utils/formDataWrapper.test.template.ts index 40f4328314b7..58475212e182 100644 --- a/generators/typescript/utils/core-utilities/tests/unit/form-data-utils/formDataWrapper.test.template.ts +++ b/generators/typescript/utils/core-utilities/tests/unit/form-data-utils/formDataWrapper.test.template.ts @@ -93,7 +93,7 @@ describe("CrossPlatformFormData", () => { for await (const chunk of request.body) { data += decoder.decode(chunk); } - expect(data).toContain("Content-Disposition: form-data; name=\"file\"; filename=\"" + expectedFileName + "\""); + expect(data).toContain(`Content-Disposition: form-data; name="file"; filename="${expectedFileName}"`); }); }); @@ -503,4 +503,4 @@ describe("FormDataWrapper", () => { }); }); }); -<% } %> \ No newline at end of file +<% } %> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ec6bc29c400..fac80de358f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -973,6 +973,9 @@ importers: '@types/node': specifier: 'catalog:' version: 18.15.3 + eta: + specifier: ^4.5.1 + version: 4.5.1 lodash-es: specifier: 'catalog:' version: 4.17.23 @@ -3963,6 +3966,9 @@ importers: esutils: specifier: 'catalog:' version: 2.0.3 + eta: + specifier: ^4.5.1 + version: 4.5.1 glob: specifier: 'catalog:' version: 11.1.0 @@ -12140,6 +12146,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eta@4.5.1: + resolution: {integrity: sha512-EaNCGm+8XEIU7YNcc+THptWAO5NfKBHHARxt+wxZljj9bTr/+arRoOm9/MpGt4n6xn9fLnPFRSoLD0WFYGFUxQ==} + engines: {node: '>=20'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -18991,6 +19001,8 @@ snapshots: esutils@2.0.3: {} + eta@4.5.1: {} + etag@1.8.1: {} event-target-shim@5.0.1: {} diff --git a/seed/python-sdk/examples/client-filename/.fernignore b/seed/python-sdk/examples/client-filename/.fernignore deleted file mode 100644 index 158580aa1f4f..000000000000 --- a/seed/python-sdk/examples/client-filename/.fernignore +++ /dev/null @@ -1 +0,0 @@ -src/seed/client.py \ No newline at end of file diff --git a/seed/python-sdk/examples/client-filename/src/seed/client.py b/seed/python-sdk/examples/client-filename/src/seed/client.py new file mode 100644 index 000000000000..0e59bcdc68e1 --- /dev/null +++ b/seed/python-sdk/examples/client-filename/src/seed/client.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx +from .base_client import AsyncBaseSeedExhaustive, BaseSeedExhaustive +from .core.logging import LogConfig, Logger +from .environment import SeedExhaustiveEnvironment + + +class SeedExhaustive(BaseSeedExhaustive): + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: typing.Optional[SeedExhaustiveEnvironment] = None, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + ): + super().__init__( + base_url=base_url, + environment=environment, + token=token, + headers=headers, + timeout=timeout, + follow_redirects=follow_redirects, + httpx_client=httpx_client, + logging=logging, + ) + + +class AsyncSeedExhaustive(AsyncBaseSeedExhaustive): + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: typing.Optional[SeedExhaustiveEnvironment] = None, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + ): + super().__init__( + base_url=base_url, + environment=environment, + token=token, + headers=headers, + timeout=timeout, + follow_redirects=follow_redirects, + httpx_client=httpx_client, + logging=logging, + ) diff --git a/seed/python-sdk/seed.yml b/seed/python-sdk/seed.yml index bdaae9b5694d..4f479f8fa0f7 100644 --- a/seed/python-sdk/seed.yml +++ b/seed/python-sdk/seed.yml @@ -424,7 +424,6 @@ scripts: allowedFailures: - any-auth - endpoint-security-auth - - examples:client-filename - examples:legacy-wire-tests - exhaustive:additional_init_exports - exhaustive:deps_with_min_python_version diff --git a/seed/ts-express/pagination-uri-path/core/schemas/builders/list/list.ts b/seed/ts-express/pagination-uri-path/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/builders/list/list.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/pagination-uri-path/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/pagination-uri-path/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/pagination-uri-path/core/schemas/builders/object/object.ts b/seed/ts-express/pagination-uri-path/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/builders/object/object.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/pagination-uri-path/core/schemas/builders/record/record.ts b/seed/ts-express/pagination-uri-path/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/builders/record/record.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/pagination-uri-path/core/schemas/builders/union/union.ts b/seed/ts-express/pagination-uri-path/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/builders/union/union.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/pagination-uri-path/core/schemas/utils/isPlainObject.ts b/seed/ts-express/pagination-uri-path/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/pagination-uri-path/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/pagination-uri-path/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/path-parameters/core/schemas/builders/list/list.ts b/seed/ts-express/path-parameters/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/path-parameters/core/schemas/builders/list/list.ts +++ b/seed/ts-express/path-parameters/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/path-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/path-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/path-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/path-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/path-parameters/core/schemas/builders/object/object.ts b/seed/ts-express/path-parameters/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/path-parameters/core/schemas/builders/object/object.ts +++ b/seed/ts-express/path-parameters/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/path-parameters/core/schemas/builders/record/record.ts b/seed/ts-express/path-parameters/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/path-parameters/core/schemas/builders/record/record.ts +++ b/seed/ts-express/path-parameters/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/path-parameters/core/schemas/builders/union/union.ts b/seed/ts-express/path-parameters/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/path-parameters/core/schemas/builders/union/union.ts +++ b/seed/ts-express/path-parameters/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/path-parameters/core/schemas/utils/isPlainObject.ts b/seed/ts-express/path-parameters/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/path-parameters/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/path-parameters/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/property-access/core/schemas/builders/list/list.ts b/seed/ts-express/property-access/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/property-access/core/schemas/builders/list/list.ts +++ b/seed/ts-express/property-access/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/property-access/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/property-access/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/property-access/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/property-access/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/property-access/core/schemas/builders/object/object.ts b/seed/ts-express/property-access/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/property-access/core/schemas/builders/object/object.ts +++ b/seed/ts-express/property-access/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/property-access/core/schemas/builders/record/record.ts b/seed/ts-express/property-access/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/property-access/core/schemas/builders/record/record.ts +++ b/seed/ts-express/property-access/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/property-access/core/schemas/builders/union/union.ts b/seed/ts-express/property-access/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/property-access/core/schemas/builders/union/union.ts +++ b/seed/ts-express/property-access/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/property-access/core/schemas/utils/isPlainObject.ts b/seed/ts-express/property-access/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/property-access/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/property-access/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/list/list.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/list/list.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object/object.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object/object.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/record/record.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/record/record.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/union/union.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/union/union.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/utils/isPlainObject.ts b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/query-parameters-openapi-as-objects/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/builders/list/list.ts b/seed/ts-express/query-parameters-openapi/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/builders/list/list.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/query-parameters-openapi/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/builders/object/object.ts b/seed/ts-express/query-parameters-openapi/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/builders/object/object.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/builders/record/record.ts b/seed/ts-express/query-parameters-openapi/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/builders/record/record.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/builders/union/union.ts b/seed/ts-express/query-parameters-openapi/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/builders/union/union.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/query-parameters-openapi/core/schemas/utils/isPlainObject.ts b/seed/ts-express/query-parameters-openapi/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/query-parameters-openapi/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/query-parameters-openapi/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/query-parameters/core/schemas/builders/list/list.ts b/seed/ts-express/query-parameters/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/query-parameters/core/schemas/builders/list/list.ts +++ b/seed/ts-express/query-parameters/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/query-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/query-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/query-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/query-parameters/core/schemas/builders/object/object.ts b/seed/ts-express/query-parameters/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/query-parameters/core/schemas/builders/object/object.ts +++ b/seed/ts-express/query-parameters/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/query-parameters/core/schemas/builders/record/record.ts b/seed/ts-express/query-parameters/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/query-parameters/core/schemas/builders/record/record.ts +++ b/seed/ts-express/query-parameters/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/query-parameters/core/schemas/builders/union/union.ts b/seed/ts-express/query-parameters/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/query-parameters/core/schemas/builders/union/union.ts +++ b/seed/ts-express/query-parameters/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/query-parameters/core/schemas/utils/isPlainObject.ts b/seed/ts-express/query-parameters/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/query-parameters/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/query-parameters/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/request-parameters/core/schemas/builders/list/list.ts b/seed/ts-express/request-parameters/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/request-parameters/core/schemas/builders/list/list.ts +++ b/seed/ts-express/request-parameters/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/request-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/request-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/request-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/request-parameters/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/request-parameters/core/schemas/builders/object/object.ts b/seed/ts-express/request-parameters/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/request-parameters/core/schemas/builders/object/object.ts +++ b/seed/ts-express/request-parameters/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/request-parameters/core/schemas/builders/record/record.ts b/seed/ts-express/request-parameters/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/request-parameters/core/schemas/builders/record/record.ts +++ b/seed/ts-express/request-parameters/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/request-parameters/core/schemas/builders/union/union.ts b/seed/ts-express/request-parameters/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/request-parameters/core/schemas/builders/union/union.ts +++ b/seed/ts-express/request-parameters/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/request-parameters/core/schemas/utils/isPlainObject.ts b/seed/ts-express/request-parameters/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/request-parameters/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/request-parameters/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-express/required-nullable/core/schemas/builders/list/list.ts b/seed/ts-express/required-nullable/core/schemas/builders/list/list.ts index 7c8fd6e87db9..191922f17453 100644 --- a/seed/ts-express/required-nullable/core/schemas/builders/list/list.ts +++ b/seed/ts-express/required-nullable/core/schemas/builders/list/list.ts @@ -44,30 +44,20 @@ function validateAndTransformArray( }; } - const maybeValidItems = value.map((item, index) => transformItem(item, index)); + const result: Parsed[] = []; + const errors: ValidationError[] = []; - 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); - } + for (let i = 0; i < value.length; i++) { + const item = transformItem(value[i], i); + if (item.ok) { + result.push(item.value); + } else { + errors.push(...item.errors); + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: [] }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/required-nullable/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/required-nullable/core/schemas/builders/object-like/getObjectLikeUtils.ts index ed96cf771cbb..9f2777f6f2d9 100644 --- a/seed/ts-express/required-nullable/core/schemas/builders/object-like/getObjectLikeUtils.ts +++ b/seed/ts-express/required-nullable/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -5,6 +5,9 @@ import { isPlainObject } from "../../utils/isPlainObject"; import { getSchemaUtils } from "../schema-utils/index"; import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { return { withParsedProperties: (properties) => withParsedProperties(schema, properties), @@ -26,15 +29,14 @@ export function withParsedProperties>( - (processed, [key, value]) => { - return { - ...processed, - [key]: typeof value === "function" ? value(parsedObject.value) : value, - }; - }, - {}, - ); + const additionalProperties: Record = {}; + for (const key in properties) { + if (_hasOwn.call(properties, key)) { + const value = properties[key as keyof Properties]; + additionalProperties[key] = + typeof value === "function" ? (value as Function)(parsedObject.value) : value; + } + } return { ok: true, diff --git a/seed/ts-express/required-nullable/core/schemas/builders/object/object.ts b/seed/ts-express/required-nullable/core/schemas/builders/object/object.ts index 024a96e54a56..eb48a1116e66 100644 --- a/seed/ts-express/required-nullable/core/schemas/builders/object/object.ts +++ b/seed/ts-express/required-nullable/core/schemas/builders/object/object.ts @@ -19,6 +19,9 @@ import type { PropertySchemas, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + interface ObjectPropertyWithRawKey { rawKey: string; parsedKey: string; @@ -28,79 +31,128 @@ interface ObjectPropertyWithRawKey { 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)[], + // All property metadata is lazily computed on first use. + // This keeps schema construction free of iteration, which matters when + // many schemas are defined but only some are exercised at runtime. + // Required-key computation is also deferred because lazy() schemas may + // not be resolved at construction time. - parse: (raw, opts) => { - const rawKeyToProperty: Record = {}; - const requiredKeys: string[] = []; + let _rawKeyToProperty: Record | undefined; + function getRawKeyToProperty(): Record { + if (_rawKeyToProperty == null) { + _rawKeyToProperty = {}; for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { - const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); const valueSchema: Schema = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.valueSchema : schemaOrObjectProperty; - const property: ObjectPropertyWithRawKey = { + _rawKeyToProperty[rawKey] = { rawKey, parsedKey: parsedKey as string, valueSchema, }; + } + } + return _rawKeyToProperty; + } - rawKeyToProperty[rawKey] = property; + let _parseRequiredKeys: string[] | undefined; + let _jsonRequiredKeys: string[] | undefined; + let _parseRequiredKeysSet: Set | undefined; + let _jsonRequiredKeysSet: Set | undefined; + function getParseRequiredKeys(): string[] { + if (_parseRequiredKeys == null) { + _parseRequiredKeys = []; + _jsonRequiredKeys = []; + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.rawKey + : (parsedKey as string); + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; if (isSchemaRequired(valueSchema)) { - requiredKeys.push(rawKey); + _parseRequiredKeys.push(rawKey); + _jsonRequiredKeys.push(parsedKey as string); } } + _parseRequiredKeysSet = new Set(_parseRequiredKeys); + _jsonRequiredKeysSet = new Set(_jsonRequiredKeys); + } + return _parseRequiredKeys; + } + + function getJsonRequiredKeys(): string[] { + if (_jsonRequiredKeys == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeys!; + } + function getParseRequiredKeysSet(): Set { + if (_parseRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _parseRequiredKeysSet!; + } + + function getJsonRequiredKeysSet(): Set { + if (_jsonRequiredKeysSet == null) { + getParseRequiredKeys(); + } + return _jsonRequiredKeysSet!; + } + + 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 breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: raw, - requiredKeys, + requiredKeys: getParseRequiredKeys(), + requiredKeysSet: getParseRequiredKeysSet(), getProperty: (rawKey) => { - const property = rawKeyToProperty[rawKey]; + const property = getRawKeyToProperty()[rawKey]; if (property == null) { return undefined; } return { transformedKey: property.parsedKey, - transform: (propertyValue) => - property.valueSchema.parse(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, rawKey]; + return property.valueSchema.parse(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + 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); - } - } - + const breadcrumbsPrefix = opts?.breadcrumbsPrefix ?? []; return validateAndTransformObject({ value: parsed, - requiredKeys, + requiredKeys: getJsonRequiredKeys(), + requiredKeysSet: getJsonRequiredKeysSet(), getProperty: ( parsedKey, ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { @@ -114,26 +166,30 @@ export function object - property.valueSchema.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.valueSchema.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } else { return { transformedKey: parsedKey, - transform: (propertyValue) => - property.json(propertyValue, { + transform: (propertyValue) => { + const childBreadcrumbs = [...breadcrumbsPrefix, parsedKey]; + return property.json(propertyValue, { ...opts, - breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], - }), + breadcrumbsPrefix: childBreadcrumbs, + }); + }, }; } }, unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, skipValidation: opts?.skipValidation, - breadcrumbsPrefix: opts?.breadcrumbsPrefix, + breadcrumbsPrefix, omitUndefined: opts?.omitUndefined, }); }, @@ -152,6 +208,7 @@ export function object({ value, requiredKeys, + requiredKeysSet, getProperty, unrecognizedObjectKeys = "fail", skipValidation = false, @@ -159,6 +216,7 @@ function validateAndTransformObject({ }: { value: unknown; requiredKeys: string[]; + requiredKeysSet: Set; getProperty: ( preTransformedKey: string, ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; @@ -179,15 +237,23 @@ function validateAndTransformObject({ }; } - const missingRequiredKeys = new Set(requiredKeys); + // Track which required keys have been seen. + // Use a counter instead of copying the Set to avoid per-call allocation. + let missingRequiredCount = requiredKeys.length; const errors: ValidationError[] = []; const transformed: Record = {}; - for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + for (const preTransformedKey in value) { + if (!_hasOwn.call(value, preTransformedKey)) { + continue; + } + const preTransformedItemValue = value[preTransformedKey]; const property = getProperty(preTransformedKey); if (property != null) { - missingRequiredKeys.delete(preTransformedKey); + if (missingRequiredCount > 0 && requiredKeysSet.has(preTransformedKey)) { + missingRequiredCount--; + } const value = property.transform(preTransformedItemValue as object); if (value.ok) { @@ -213,14 +279,16 @@ function validateAndTransformObject({ } } - errors.push( - ...requiredKeys - .filter((key) => missingRequiredKeys.has(key)) - .map((key) => ({ - path: breadcrumbsPrefix, - message: `Missing required key "${key}"`, - })), - ); + if (missingRequiredCount > 0) { + for (const key of requiredKeys) { + if (!(key in (value as Record))) { + errors.push({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + }); + } + } + } if (errors.length === 0 || skipValidation) { return { diff --git a/seed/ts-express/required-nullable/core/schemas/builders/record/record.ts b/seed/ts-express/required-nullable/core/schemas/builders/record/record.ts index ba5307a6a45c..f11dfae0ec67 100644 --- a/seed/ts-express/required-nullable/core/schemas/builders/record/record.ts +++ b/seed/ts-express/required-nullable/core/schemas/builders/record/record.ts @@ -1,11 +1,13 @@ import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; -import { entries } from "../../utils/entries"; import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; import { isPlainObject } from "../../utils/isPlainObject"; import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; import { getSchemaUtils } from "../schema-utils/index"; import type { BaseRecordSchema, RecordSchema } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function record( keySchema: Schema, valueSchema: Schema, @@ -79,51 +81,42 @@ function validateAndTransformRecord>>( - (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 result = {} as Record; + const errors: ValidationError[] = []; - const transformedValue = transformValue(value, key); + for (const stringKey in value) { + if (!_hasOwn.call(value, stringKey)) { + continue; + } + const entryValue = (value as Record)[stringKey]; + if (entryValue === undefined) { + continue; + } - if (acc.ok && transformedKey.ok && transformedValue.ok) { - return { - ok: true, - value: { - ...acc.value, - [transformedKey.value]: transformedValue.value, - }, - }; + 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(entryValue, key); - const errors: ValidationError[] = []; - if (!acc.ok) { - errors.push(...acc.errors); - } + if (transformedKey.ok && transformedValue.ok) { + result[transformedKey.value] = transformedValue.value; + } else { if (!transformedKey.ok) { errors.push(...transformedKey.errors); } if (!transformedValue.ok) { errors.push(...transformedValue.errors); } + } + } - return { - ok: false, - errors, - }; - }, - { ok: true, value: {} as Record }, - ); + if (errors.length === 0) { + return { ok: true, value: result }; + } + return { ok: false, errors }; } diff --git a/seed/ts-express/required-nullable/core/schemas/builders/union/union.ts b/seed/ts-express/required-nullable/core/schemas/builders/union/union.ts index 7da4271a27c0..b324fa2de27e 100644 --- a/seed/ts-express/required-nullable/core/schemas/builders/union/union.ts +++ b/seed/ts-express/required-nullable/core/schemas/builders/union/union.ts @@ -16,6 +16,9 @@ import type { UnionSubtypes, } from "./types"; +// eslint-disable-next-line @typescript-eslint/unbound-method +const _hasOwn = Object.prototype.hasOwnProperty; + export function union, U extends UnionSubtypes>( discriminant: D, union: U, @@ -112,7 +115,13 @@ function transformAndValidateUnion< }; } - const { [discriminant]: discriminantValue, ...additionalProperties } = value; + const discriminantValue = value[discriminant]; + const additionalProperties: Record = {}; + for (const key in value) { + if (_hasOwn.call(value, key) && key !== discriminant) { + additionalProperties[key] = value[key]; + } + } if (discriminantValue == null) { return { diff --git a/seed/ts-express/required-nullable/core/schemas/utils/isPlainObject.ts b/seed/ts-express/required-nullable/core/schemas/utils/isPlainObject.ts index db82a722c35b..32a17e05f01e 100644 --- a/seed/ts-express/required-nullable/core/schemas/utils/isPlainObject.ts +++ b/seed/ts-express/required-nullable/core/schemas/utils/isPlainObject.ts @@ -4,14 +4,11 @@ export function isPlainObject(value: unknown): value is Record return false; } - if (Object.getPrototypeOf(value) === null) { + const proto = Object.getPrototypeOf(value); + if (proto === null) { return true; } - let proto = value; - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - - return Object.getPrototypeOf(value) === proto; + // Check that the prototype chain has exactly one level (i.e., proto is Object.prototype) + return Object.getPrototypeOf(proto) === null; } diff --git a/seed/ts-sdk/file-upload/form-data-node16/tests/unit/form-data-utils/formDataWrapper.test.ts b/seed/ts-sdk/file-upload/form-data-node16/tests/unit/form-data-utils/formDataWrapper.test.ts index 28c9f9bba385..0822f91a5f75 100644 --- a/seed/ts-sdk/file-upload/form-data-node16/tests/unit/form-data-utils/formDataWrapper.test.ts +++ b/seed/ts-sdk/file-upload/form-data-node16/tests/unit/form-data-utils/formDataWrapper.test.ts @@ -92,7 +92,7 @@ describe("CrossPlatformFormData", () => { for await (const chunk of request.body) { data += decoder.decode(chunk); } - expect(data).toContain(`Content-Disposition: form-data; name=\"file\"; filename=\"${expectedFileName}\"`); + expect(data).toContain(`Content-Disposition: form-data; name="file"; filename="${expectedFileName}"`); }); }); diff --git a/seed/ts-sdk/simple-api/no-linter-and-formatter/tests/setup.ts b/seed/ts-sdk/simple-api/no-linter-and-formatter/tests/setup.ts index f29a177d96be..e2a8edf51c0b 100644 --- a/seed/ts-sdk/simple-api/no-linter-and-formatter/tests/setup.ts +++ b/seed/ts-sdk/simple-api/no-linter-and-formatter/tests/setup.ts @@ -53,29 +53,30 @@ expect.extend({ if (pass) { return { - message: () => "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } }, }); + diff --git a/seed/ts-sdk/simple-api/no-scripts/tests/setup.ts b/seed/ts-sdk/simple-api/no-scripts/tests/setup.ts index f29a177d96be..e2a8edf51c0b 100644 --- a/seed/ts-sdk/simple-api/no-scripts/tests/setup.ts +++ b/seed/ts-sdk/simple-api/no-scripts/tests/setup.ts @@ -53,29 +53,30 @@ expect.extend({ if (pass) { return { - message: () => "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } }, }); + diff --git a/seed/ts-sdk/simple-api/use-oxc/package.json b/seed/ts-sdk/simple-api/use-oxc/package.json index bf74e9ec7ab2..bc3d507709c3 100644 --- a/seed/ts-sdk/simple-api/use-oxc/package.json +++ b/seed/ts-sdk/simple-api/use-oxc/package.json @@ -56,9 +56,9 @@ "devDependencies": { "@types/node": "^18.19.70", "msw": "2.11.2", - "oxfmt": "0.27.0", - "oxlint": "1.42.0", - "oxlint-tsgolint": "0.11.4", + "oxfmt": "0.35.0", + "oxlint": "1.50.0", + "oxlint-tsgolint": "0.14.2", "ts-loader": "^9.5.1", "typescript": "~5.7.2", "vitest": "^3.2.4", diff --git a/seed/ts-sdk/simple-api/use-oxc/tests/setup.ts b/seed/ts-sdk/simple-api/use-oxc/tests/setup.ts index 201f5fbc968f..a5651f81ba10 100644 --- a/seed/ts-sdk/simple-api/use-oxc/tests/setup.ts +++ b/seed/ts-sdk/simple-api/use-oxc/tests/setup.ts @@ -52,37 +52,27 @@ expect.extend({ if (pass) { return { - message: () => - "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } diff --git a/seed/ts-sdk/simple-api/use-oxfmt/package.json b/seed/ts-sdk/simple-api/use-oxfmt/package.json index e6b03dcc5e21..f91becae25da 100644 --- a/seed/ts-sdk/simple-api/use-oxfmt/package.json +++ b/seed/ts-sdk/simple-api/use-oxfmt/package.json @@ -57,7 +57,7 @@ "@biomejs/biome": "2.4.3", "@types/node": "^18.19.70", "msw": "2.11.2", - "oxfmt": "0.27.0", + "oxfmt": "0.35.0", "ts-loader": "^9.5.1", "typescript": "~5.7.2", "vitest": "^3.2.4", diff --git a/seed/ts-sdk/simple-api/use-oxfmt/tests/setup.ts b/seed/ts-sdk/simple-api/use-oxfmt/tests/setup.ts index 3bd2e38ca2ca..a5651f81ba10 100644 --- a/seed/ts-sdk/simple-api/use-oxfmt/tests/setup.ts +++ b/seed/ts-sdk/simple-api/use-oxfmt/tests/setup.ts @@ -52,8 +52,7 @@ expect.extend({ if (pass) { return { - message: () => - `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, + message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, pass: true, }; } else { @@ -66,23 +65,14 @@ expect.extend({ if (mismatchedHeaders.length > 0) { const mismatches = mismatchedHeaders.map( ({ key, expected, actual }) => - key + - ": expected " + - this.utils.printExpected(expected) + - " but got " + - this.utils.printReceived(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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } diff --git a/seed/ts-sdk/simple-api/use-oxlint/package.json b/seed/ts-sdk/simple-api/use-oxlint/package.json index 0cb6e0cdf84d..4864d72d6560 100644 --- a/seed/ts-sdk/simple-api/use-oxlint/package.json +++ b/seed/ts-sdk/simple-api/use-oxlint/package.json @@ -54,8 +54,8 @@ "@types/node": "^18.19.70", "typescript": "~5.7.2", "@biomejs/biome": "2.4.3", - "oxlint": "1.42.0", - "oxlint-tsgolint": "0.11.4" + "oxlint": "1.50.0", + "oxlint-tsgolint": "0.14.2" }, "browser": { "fs": false, diff --git a/seed/ts-sdk/simple-api/use-oxlint/tests/setup.ts b/seed/ts-sdk/simple-api/use-oxlint/tests/setup.ts index 201f5fbc968f..a5651f81ba10 100644 --- a/seed/ts-sdk/simple-api/use-oxlint/tests/setup.ts +++ b/seed/ts-sdk/simple-api/use-oxlint/tests/setup.ts @@ -52,37 +52,27 @@ expect.extend({ if (pass) { return { - message: () => - "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } diff --git a/seed/ts-sdk/simple-api/use-prettier-no-linter/tests/setup.ts b/seed/ts-sdk/simple-api/use-prettier-no-linter/tests/setup.ts index 201f5fbc968f..a5651f81ba10 100644 --- a/seed/ts-sdk/simple-api/use-prettier-no-linter/tests/setup.ts +++ b/seed/ts-sdk/simple-api/use-prettier-no-linter/tests/setup.ts @@ -52,37 +52,27 @@ expect.extend({ if (pass) { return { - message: () => - "expected " + actualType + " not to contain " + this.utils.printExpected(expectedHeaders), + 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(", "))); + 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), + `${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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; } diff --git a/seed/ts-sdk/simple-api/use-prettier/tests/setup.ts b/seed/ts-sdk/simple-api/use-prettier/tests/setup.ts index 3bd2e38ca2ca..a5651f81ba10 100644 --- a/seed/ts-sdk/simple-api/use-prettier/tests/setup.ts +++ b/seed/ts-sdk/simple-api/use-prettier/tests/setup.ts @@ -52,8 +52,7 @@ expect.extend({ if (pass) { return { - message: () => - `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, + message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, pass: true, }; } else { @@ -66,23 +65,14 @@ expect.extend({ if (mismatchedHeaders.length > 0) { const mismatches = mismatchedHeaders.map( ({ key, expected, actual }) => - key + - ": expected " + - this.utils.printExpected(expected) + - " but got " + - this.utils.printReceived(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"), + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, pass: false, }; }