From 16da9638cf5ac71831c05cf065af19ec72bc83cb Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Wed, 13 Nov 2024 19:21:00 -0500 Subject: [PATCH 01/14] started openapi parser --- packages/parsers/openapi/3.1/openapi.node.ts | 55 +++ .../shared/composable/availability.node.ts | 33 ++ .../shared/interfaces/api.node.interface.ts | 26 ++ .../interfaces/container.node.interface.ts | 5 + .../shared/interfaces/fdr.stage.interface.ts | 5 + .../interfaces/primitive.node.interface.ts | 3 + .../openapi/shared/nodes/endpoint.node.ts | 0 .../openapi/shared/nodes/string.node.ts | 43 ++ .../openapi/shared/nodes/webhook.node.ts | 0 .../openapi/shared/nodes/websocket.node.ts | 0 .../openapi/shared/stages/fdr/api.stage.ts | 40 ++ packages/parsers/package.json | 34 ++ pnpm-lock.yaml | 426 +++++++++--------- 13 files changed, 459 insertions(+), 211 deletions(-) create mode 100644 packages/parsers/openapi/3.1/openapi.node.ts create mode 100644 packages/parsers/openapi/shared/composable/availability.node.ts create mode 100644 packages/parsers/openapi/shared/interfaces/api.node.interface.ts create mode 100644 packages/parsers/openapi/shared/interfaces/container.node.interface.ts create mode 100644 packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts create mode 100644 packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts create mode 100644 packages/parsers/openapi/shared/nodes/endpoint.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/string.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/webhook.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/websocket.node.ts create mode 100644 packages/parsers/openapi/shared/stages/fdr/api.stage.ts create mode 100644 packages/parsers/package.json diff --git a/packages/parsers/openapi/3.1/openapi.node.ts b/packages/parsers/openapi/3.1/openapi.node.ts new file mode 100644 index 0000000000..a4a95fa1a5 --- /dev/null +++ b/packages/parsers/openapi/3.1/openapi.node.ts @@ -0,0 +1,55 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { v4 } from "uuid"; +import { ApiNode, ApiNodeContext } from "../shared/interfaces/api.node.interface"; + +export class OpenApi3_1Node implements ApiNode { + name = "OpenApi3_1"; + context: ApiNodeContext; + qualifiedId: string = "OpenApi3_1"; + + constructor( + context: ApiNodeContext, + readonly preProcessedInput: OpenAPIV3_1.Document, + ) { + this.context = context; + this.accessPath = [this]; + } + accessPath: ApiNode[]; + outputFdrShape = (): FdrAPI.api.latest.ApiDefinition => { + const { endpoints, websockets, webhooks } = new FdrApiStage( + this.context, + this.preProcessedInput.paths, + this.accessPath, + ).outputFdrShape(); + + const types = new FdrTypesStage( + this.context, + this.preProcessedInput.components?.schemas, + this.accessPath, + ).outputFdrShape(); + + const auths = new FdrAuthsStage( + this.context, + this.preProcessedInput.components?.securitySchemes, + this.accessPath, + ).outputFdrShape(); + + const globalHeaders = new FdrGlobalHeadersStage( + this.context, + this.preProcessedInput.components?.headers, + this.accessPath, + ).outputFdrShape(); + + return { + id: v4(), + endpoints, + websockets, + webhooks, + types, + subpackages: {}, + auths, + globalHeaders, + }; + }; +} diff --git a/packages/parsers/openapi/shared/composable/availability.node.ts b/packages/parsers/openapi/shared/composable/availability.node.ts new file mode 100644 index 0000000000..aab9c1d2f2 --- /dev/null +++ b/packages/parsers/openapi/shared/composable/availability.node.ts @@ -0,0 +1,33 @@ +import { ApiNode, ApiNodeContext, ComposableApiNode } from "../interfaces/api.node.interface"; + +export class AvailabilityNode & { availability: string }> + implements + ComposableApiNode< + InputNode, + ReturnType & { + availability: string; + } + > +{ + name = "availability"; + preProcessedInput: InputNode["preProcessedInput"]; + qualifiedId: string; + + constructor( + readonly context: ApiNodeContext, + readonly inputNode: InputNode, + readonly accessPath: ApiNode[], + ) { + this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; + this.preProcessedInput = this.inputNode.preProcessedInput; + } + + outputFdrShape(): ReturnType & { + availability: string; + } { + return { + ...this.inputNode.outputFdrShape(), + availability: this.inputNode.availability, + }; + } +} diff --git a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts new file mode 100644 index 0000000000..4c48ce4ce1 --- /dev/null +++ b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts @@ -0,0 +1,26 @@ +import { Logger } from "@playwright/test"; + +export interface ApiNodeContext { + orgId: string; + apiId: string; + logger: Logger; +} + +export interface ApiNode { + context: ApiNodeContext; + preProcessedInput: InputShape; + + accessPath: ApiNode[]; + name: string; + + qualifiedId: string; + + outputFdrShape: () => FdrShape; +} + +export interface ComposableApiNode, FdrShape> + extends ApiNode { + inputNode: InputNode; + + outputFdrShape: () => ReturnType & FdrShape; +} diff --git a/packages/parsers/openapi/shared/interfaces/container.node.interface.ts b/packages/parsers/openapi/shared/interfaces/container.node.interface.ts new file mode 100644 index 0000000000..a58ddec4e8 --- /dev/null +++ b/packages/parsers/openapi/shared/interfaces/container.node.interface.ts @@ -0,0 +1,5 @@ +import { ApiNode } from "./api.node.interface"; + +export interface ContainerNode extends ApiNode { + innerNodeInputShape: InnerNodeInputShape; +} diff --git a/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts b/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts new file mode 100644 index 0000000000..dfe05b9d07 --- /dev/null +++ b/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts @@ -0,0 +1,5 @@ +import { ApiNode } from "./api.node.interface"; + +export interface FdrStage extends ApiNode { + stageName: string; +} diff --git a/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts b/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts new file mode 100644 index 0000000000..d661752822 --- /dev/null +++ b/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts @@ -0,0 +1,3 @@ +import { ApiNode } from "./api.node.interface"; + +export interface PrimitiveNode extends ApiNode {} diff --git a/packages/parsers/openapi/shared/nodes/endpoint.node.ts b/packages/parsers/openapi/shared/nodes/endpoint.node.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/shared/nodes/string.node.ts b/packages/parsers/openapi/shared/nodes/string.node.ts new file mode 100644 index 0000000000..94143acfc7 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/string.node.ts @@ -0,0 +1,43 @@ +import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; +import { PrimitiveNode } from "../interfaces/primitive.node.interface"; + +type FdrStringShape = { + value: string; + format?: "uuid" | "email"; + pattern?: RegExp; +}; + +type RedocStringNode = { + stringValue: string; + format?: "uuid" | "email"; + pattern?: RegExp; +}; + +export class StringNode implements PrimitiveNode { + name = "String"; + qualifiedId: string; + + private readonly stringValue: string; + private readonly format?: "uuid" | "email"; + private readonly pattern?: RegExp; + + constructor( + readonly context: ApiNodeContext, + readonly preProcessedInput: RedocStringNode, + readonly accessPath: ApiNode[], + ) { + this.qualifiedId = accessPath.map((node) => node.qualifiedId).join("."); + + this.stringValue = preProcessedInput.stringValue; + this.format = preProcessedInput.format; + this.pattern = preProcessedInput.pattern; + } + + outputFdrShape(): FdrStringShape { + return { + value: this.stringValue, + ...(this.format ? {} : { format: this.format }), + ...(this.pattern ? {} : { pattern: this.pattern }), + }; + } +} diff --git a/packages/parsers/openapi/shared/nodes/webhook.node.ts b/packages/parsers/openapi/shared/nodes/webhook.node.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/shared/nodes/websocket.node.ts b/packages/parsers/openapi/shared/nodes/websocket.node.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts new file mode 100644 index 0000000000..01005812cd --- /dev/null +++ b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts @@ -0,0 +1,40 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { ApiNode, ApiNodeContext } from "../../interfaces/api.node.interface"; +import { FdrStage } from "../../interfaces/fdr.stage.interface"; + +type ApiProcessingStageOutput = { + endpoints: FdrAPI.api.latest.ApiDefinition["endpoints"]; + websockets: FdrAPI.api.latest.ApiDefinition["websockets"]; + webhooks: FdrAPI.api.latest.ApiDefinition["webhooks"]; +}; + +export class ApiProcessingStage implements FdrStage { + stageName = "ApiProcessingStage"; + name = "ApiProcessingStage"; + qualifiedId: string; + + endpoints: ApiProcessingStageOutput["endpoints"] = {}; + websockets: ApiProcessingStageOutput["websockets"] = {}; + webhooks: ApiProcessingStageOutput["webhooks"] = {}; + + constructor( + readonly context: ApiNodeContext, + readonly preProcessedInput: OpenAPIV3_1.Document["paths"], + readonly accessPath: ApiNode[], + ) { + this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; + this.accessPath.push(this); + + for (const [path, pathItem] of Object.entries(this.preProcessedInput)) { + } + } + + outputFdrShape = (): ApiProcessingStageOutput => { + return { + endpoints: {}, + webhooks: {}, + websockets: {}, + }; + }; +} diff --git a/packages/parsers/package.json b/packages/parsers/package.json new file mode 100644 index 0000000000..b297d76e61 --- /dev/null +++ b/packages/parsers/package.json @@ -0,0 +1,34 @@ +{ + "name": "@fern-ui/parsers", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "https://github.com/fern-api/fern-platform.git", + "directory": "packages/parsers" + }, + "private": true, + "sideEffects": false, + "scripts": { + "test": "vitest --run --passWithNoTests --globals", + "lint:eslint": "eslint --max-warnings 0 . --ignore-path=../../../.eslintignore", + "lint:eslint:fix": "pnpm lint:eslint --fix", + "lint:style": "stylelint 'src/**/*.scss' --allow-empty-input --max-warnings 0", + "lint:style:fix": "pnpm lint:style --fix", + "format": "prettier --write --ignore-unknown --ignore-path ../../../shared/.prettierignore \"**\"", + "format:check": "prettier --check --ignore-unknown --ignore-path ../../../shared/.prettierignore \"**\"", + "organize-imports": "organize-imports-cli tsconfig.json", + "depcheck": "depcheck", + "docs:dev": "NODE_OPTIONS='--inspect' next dev", + "docs:build": "next build", + "docs:start": "next start", + "lint": "pnpm lint:eslint && pnpm lint:style" + }, + "dependencies": { + "@fern-api/fdr-sdk": "workspace:*", + "openapi-types": "^12.1.3", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/uuid": "^9.0.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52be8c6a8c..f59ab8ad8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,13 +49,13 @@ importers: version: 1.47.1 '@storybook/nextjs': specifier: 8.4.1 - version: 8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(next@14.2.15(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + version: 8.4.1(@swc/core@1.5.7)(next@14.2.15(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)) '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@types/express': specifier: ^4.17.13 version: 4.17.21 @@ -112,7 +112,7 @@ importers: version: 4.6.2(eslint@8.57.0) eslint-plugin-tailwindcss: specifier: ^3.13.1 - version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) eslint-plugin-vitest: specifier: ^0.3.26 version: 0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) @@ -172,16 +172,16 @@ importers: version: 13.1.0(postcss@8.4.31)(stylelint@16.5.0(typescript@5.4.3)) stylelint-config-tailwindcss: specifier: ^0.0.7 - version: 0.0.7(stylelint@16.5.0(typescript@5.4.3))(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.0.7(stylelint@16.5.0(typescript@5.4.3))(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) stylelint-scss: specifier: ^6.0.0 version: 6.3.0(stylelint@16.5.0(typescript@5.4.3)) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + version: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) tsx: specifier: ^4.7.1 version: 4.9.3 @@ -193,7 +193,7 @@ importers: version: 5.4.3 typescript-plugin-css-modules: specifier: ^5.1.0 - version: 5.1.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))(typescript@5.4.3) + version: 5.1.0(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))(typescript@5.4.3) vitest: specifier: ^2.1.4 version: 2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) @@ -250,7 +250,7 @@ importers: version: 3.0.3 tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.5.7(@swc/helpers@0.5.5))(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2) + version: 8.3.5(@swc/core@1.5.7)(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2) typescript: specifier: 4.9.5 version: 4.9.5 @@ -807,10 +807,10 @@ importers: version: 4.6.2(eslint@8.57.0) eslint-plugin-tailwindcss: specifier: ^3.13.1 - version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3))) + version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) eslint-plugin-vitest: specifier: ^0.3.26 - version: 0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@22.5.5)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) + version: 0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) devDependencies: prettier: specifier: ^3.3.2 @@ -954,7 +954,7 @@ importers: version: 3.3.2 tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.5.7(@swc/helpers@0.5.5))(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2) + version: 8.3.5(@swc/core@1.5.7)(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2) typescript: specifier: 4.9.5 version: 4.9.5 @@ -965,6 +965,22 @@ importers: specifier: ^17.4.1 version: 17.7.2 + packages/parsers: + dependencies: + '@fern-api/fdr-sdk': + specifier: workspace:* + version: link:../fdr-sdk + openapi-types: + specifier: ^12.1.3 + version: 12.1.3 + uuid: + specifier: ^9.0.0 + version: 9.0.1 + devDependencies: + '@types/uuid': + specifier: ^9.0.1 + version: 9.0.8 + packages/scripts: dependencies: chalk: @@ -1040,7 +1056,7 @@ importers: version: 8.57.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) organize-imports-cli: specifier: ^0.10.0 version: 0.10.0 @@ -1317,7 +1333,7 @@ importers: version: 8.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2)) '@storybook/nextjs': specifier: ^8.4.1 - version: 8.4.1(@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + version: 8.4.1(@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(@swc/core@1.5.7)(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) '@storybook/react': specifier: ^8.4.1 version: 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) @@ -1326,13 +1342,13 @@ importers: version: 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@testing-library/jest-dom': specifier: ^6.4.2 - version: 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) + version: 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) '@testing-library/react': specifier: ^14.2.1 version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1416,7 +1432,7 @@ importers: version: 16.5.0(typescript@5.4.3) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) ts-essentials: specifier: ^10.0.1 version: 10.0.1(typescript@5.4.3) @@ -1471,7 +1487,7 @@ importers: version: link:../../configs '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3))) '@types/hast': specifier: ^3.0.4 version: 3.0.4 @@ -1513,7 +1529,7 @@ importers: version: 16.5.0(typescript@5.4.3) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)) typescript: specifier: 5.4.3 version: 5.4.3 @@ -1625,13 +1641,13 @@ importers: version: 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@testing-library/jest-dom': specifier: ^6.4.2 - version: 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) + version: 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)) '@testing-library/react': specifier: ^14.2.1 version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1691,7 +1707,7 @@ importers: version: 16.5.0(typescript@5.4.3) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) typescript: specifier: 5.4.3 version: 5.4.3 @@ -1855,10 +1871,10 @@ importers: version: 14.2.9 '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@types/node': specifier: ^18.7.18 version: 18.19.33 @@ -1900,7 +1916,7 @@ importers: version: 16.5.0(typescript@5.4.3) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) typescript: specifier: 5.4.3 version: 5.4.3 @@ -2005,7 +2021,7 @@ importers: version: 2.3.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3))) + version: 1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3))) zod: specifier: ^3.23.8 version: 3.23.8 @@ -2015,7 +2031,7 @@ importers: version: 1.78.2(@tanstack/react-router@1.78.2(@tanstack/router-generator@1.78.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/router-vite-plugin': specifier: ^1.78.2 - version: 1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + version: 1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7)) '@types/lodash': specifier: ^4.17.4 version: 4.17.4 @@ -2057,7 +2073,7 @@ importers: version: 8.4.31 tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3)) typescript: specifier: ^5.2.2 version: 5.4.3 @@ -2595,10 +2611,10 @@ importers: version: 14.2.9 '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))) '@types/node': specifier: ^18.7.18 version: 18.19.33 @@ -2640,7 +2656,7 @@ importers: version: 16.5.0(typescript@5.4.3) tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + version: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) typescript: specifier: 5.4.3 version: 5.4.3 @@ -2913,7 +2929,7 @@ importers: version: 2.1.2 ts-node: specifier: ^10.9.1 - version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + version: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) typescript: specifier: 5.4.3 version: 5.4.3 @@ -3031,7 +3047,7 @@ importers: version: 1.52.1(esbuild@0.20.2) ts-node: specifier: ^10.4.0 - version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@4.9.5) + version: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@4.9.5) tsconfig-paths: specifier: ^3.9.0 version: 3.15.0 @@ -12853,6 +12869,9 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -19077,7 +19096,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -19091,7 +19110,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.12)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + jest-config: 29.7.0(@types/node@20.12.12)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -19783,7 +19802,7 @@ snapshots: dependencies: playwright: 1.47.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2))': dependencies: ansi-html-community: 0.0.8 core-js-pure: 3.37.0 @@ -19793,12 +19812,12 @@ snapshots: react-refresh: 0.14.2 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) optionalDependencies: type-fest: 4.21.0 webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7))': dependencies: ansi-html-community: 0.0.8 core-js-pure: 3.37.0 @@ -19808,7 +19827,7 @@ snapshots: react-refresh: 0.14.2 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) optionalDependencies: type-fest: 4.21.0 webpack-hot-middleware: 2.26.1 @@ -21489,7 +21508,7 @@ snapshots: ts-dedent: 2.2.0 vite: 5.4.10(@types/node@18.19.33)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) - '@storybook/builder-webpack5@8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': + '@storybook/builder-webpack5@8.4.1(@swc/core@1.5.7)(esbuild@0.20.2)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': dependencies: '@storybook/core-webpack': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@types/node': 22.5.5 @@ -21498,23 +21517,23 @@ snapshots: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.3.1 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) es-module-lexer: 1.5.2 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) - html-webpack-plugin: 5.6.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) + html-webpack-plugin: 5.6.0(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) magic-string: 0.30.12 path-browserify: 1.0.1 process: 0.11.10 semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) - style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) - terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7)(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) ts-dedent: 2.2.0 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) - webpack-dev-middleware: 6.1.3(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) + webpack-dev-middleware: 6.1.3(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -21526,7 +21545,7 @@ snapshots: - uglify-js - webpack-cli - '@storybook/builder-webpack5@8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': + '@storybook/builder-webpack5@8.4.1(@swc/core@1.5.7)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': dependencies: '@storybook/core-webpack': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@types/node': 22.5.5 @@ -21535,23 +21554,23 @@ snapshots: case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.3.1 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7)) es-module-lexer: 1.5.2 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) - html-webpack-plugin: 5.6.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)) + html-webpack-plugin: 5.6.0(webpack@5.94.0(@swc/core@1.5.7)) magic-string: 0.30.12 path-browserify: 1.0.1 process: 0.11.10 semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) - style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) - terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7)) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7)(webpack@5.94.0(@swc/core@1.5.7)) ts-dedent: 2.2.0 url: 0.11.3 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) - webpack-dev-middleware: 6.1.3(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + webpack: 5.94.0(@swc/core@1.5.7) + webpack-dev-middleware: 6.1.3(webpack@5.94.0(@swc/core@1.5.7)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -21649,7 +21668,7 @@ snapshots: dependencies: storybook: 8.4.1(prettier@3.3.2) - '@storybook/nextjs@8.4.1(@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2))': + '@storybook/nextjs@8.4.1(@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(@swc/core@1.5.7)(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) @@ -21664,31 +21683,31 @@ snapshots: '@babel/preset-react': 7.24.1(@babel/core@7.26.0) '@babel/preset-typescript': 7.24.1(@babel/core@7.26.0) '@babel/runtime': 7.24.5 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) - '@storybook/builder-webpack5': 8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) - '@storybook/preset-react-webpack': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) + '@storybook/builder-webpack5': 8.4.1(@swc/core@1.5.7)(esbuild@0.20.2)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) + '@storybook/preset-react-webpack': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7)(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) '@storybook/react': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) '@storybook/test': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@types/node': 22.5.5 '@types/semver': 7.5.8 - babel-loader: 9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) - css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + babel-loader: 9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) + css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) find-up: 5.0.0 image-size: 1.1.1 loader-utils: 3.2.1 next: '@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)' - node-polyfill-webpack-plugin: 2.0.1(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + node-polyfill-webpack-plugin: 2.0.1(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) pnp-webpack-plugin: 1.7.0(typescript@5.4.3) postcss: 8.4.31 - postcss-loader: 8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + postcss-loader: 8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-refresh: 0.14.2 resolve-url-loader: 5.0.0 - sass-loader: 13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + sass-loader: 13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) - style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1) ts-dedent: 2.2.0 tsconfig-paths: 4.2.0 @@ -21696,7 +21715,7 @@ snapshots: optionalDependencies: sharp: 0.33.3 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -21716,7 +21735,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/nextjs@8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(next@14.2.15(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)))': + '@storybook/nextjs@8.4.1(@swc/core@1.5.7)(next@14.2.15(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)(storybook@8.4.1(prettier@3.3.2))(type-fest@4.21.0)(typescript@5.4.3)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) @@ -21731,31 +21750,31 @@ snapshots: '@babel/preset-react': 7.24.1(@babel/core@7.26.0) '@babel/preset-typescript': 7.24.1(@babel/core@7.26.0) '@babel/runtime': 7.24.5 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) - '@storybook/builder-webpack5': 8.4.1(@swc/core@1.5.7(@swc/helpers@0.5.5))(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) - '@storybook/preset-react-webpack': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7(@swc/helpers@0.5.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.13(react-refresh@0.14.2)(type-fest@4.21.0)(webpack-hot-middleware@2.26.1)(webpack@5.94.0(@swc/core@1.5.7)) + '@storybook/builder-webpack5': 8.4.1(@swc/core@1.5.7)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) + '@storybook/preset-react-webpack': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) '@storybook/react': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) '@storybook/test': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@types/node': 22.5.5 '@types/semver': 7.5.8 - babel-loader: 9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) - css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + babel-loader: 9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7)) + css-loader: 6.11.0(webpack@5.94.0(@swc/core@1.5.7)) find-up: 5.0.0 image-size: 1.1.1 loader-utils: 3.2.1 next: 14.2.15(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0) - node-polyfill-webpack-plugin: 2.0.1(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + node-polyfill-webpack-plugin: 2.0.1(webpack@5.94.0(@swc/core@1.5.7)) pnp-webpack-plugin: 1.7.0(typescript@5.4.3) postcss: 8.4.31 - postcss-loader: 8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + postcss-loader: 8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-refresh: 0.14.2 resolve-url-loader: 5.0.0 - sass-loader: 13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + sass-loader: 13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7)) semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) - style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + style-loader: 3.3.4(webpack@5.94.0(@swc/core@1.5.7)) styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1) ts-dedent: 2.2.0 tsconfig-paths: 4.2.0 @@ -21763,7 +21782,7 @@ snapshots: optionalDependencies: sharp: 0.33.3 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -21783,11 +21802,11 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': + '@storybook/preset-react-webpack@8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7)(esbuild@0.20.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': dependencies: '@storybook/core-webpack': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@storybook/react': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) '@types/node': 22.5.5 '@types/semver': 7.5.8 find-up: 5.0.0 @@ -21799,7 +21818,7 @@ snapshots: semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) tsconfig-paths: 4.2.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: @@ -21810,11 +21829,11 @@ snapshots: - uglify-js - webpack-cli - '@storybook/preset-react-webpack@8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7(@swc/helpers@0.5.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': + '@storybook/preset-react-webpack@8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(@swc/core@1.5.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3)': dependencies: '@storybook/core-webpack': 8.4.1(storybook@8.4.1(prettier@3.3.2)) '@storybook/react': 8.4.1(@storybook/test@8.4.1(storybook@8.4.1(prettier@3.3.2)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.1(prettier@3.3.2))(typescript@5.4.3) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)) '@types/node': 22.5.5 '@types/semver': 7.5.8 find-up: 5.0.0 @@ -21826,7 +21845,7 @@ snapshots: semver: 7.6.2 storybook: 8.4.1(prettier@3.3.2) tsconfig-paths: 4.2.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) optionalDependencies: typescript: 5.4.3 transitivePeerDependencies: @@ -21858,7 +21877,7 @@ snapshots: dependencies: storybook: 8.4.1(prettier@3.3.2) - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2))': dependencies: debug: 4.3.7 endent: 2.1.0 @@ -21868,11 +21887,11 @@ snapshots: react-docgen-typescript: 2.2.2(typescript@5.4.3) tslib: 2.8.0 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) transitivePeerDependencies: - supports-color - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7))': dependencies: debug: 4.3.7 endent: 2.1.0 @@ -21882,7 +21901,7 @@ snapshots: react-docgen-typescript: 2.2.2(typescript@5.4.3) tslib: 2.8.0 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) transitivePeerDependencies: - supports-color @@ -22013,26 +22032,26 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/forms@0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)))': + '@tailwindcss/forms@0.5.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)))': dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) - '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)))': + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) - '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)))': + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)) '@tanem/svg-injector@10.1.68': dependencies: @@ -22094,7 +22113,7 @@ snapshots: tsx: 4.19.2 zod: 3.23.8 - '@tanstack/router-plugin@1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)))': + '@tanstack/router-plugin@1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7))': dependencies: '@babel/core': 7.26.0 '@babel/generator': 7.26.2 @@ -22116,14 +22135,14 @@ snapshots: zod: 3.23.8 optionalDependencies: vite: 5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) transitivePeerDependencies: - supports-color - webpack-sources - '@tanstack/router-vite-plugin@1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)))': + '@tanstack/router-vite-plugin@1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7))': dependencies: - '@tanstack/router-plugin': 1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + '@tanstack/router-plugin': 1.78.2(vite@5.4.10(@types/node@20.12.12)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))(webpack-sources@3.2.3)(webpack@5.94.0(@swc/core@1.5.7)) transitivePeerDependencies: - '@rsbuild/core' - supports-color @@ -22157,7 +22176,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))': + '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)))(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.24.5 @@ -22170,7 +22189,7 @@ snapshots: optionalDependencies: '@jest/globals': 29.7.0 '@types/jest': 29.5.12 - jest: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + jest: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) vitest: 2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) '@testing-library/jest-dom@6.5.0': @@ -24562,19 +24581,19 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + babel-loader@9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: '@babel/core': 7.26.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - babel-loader@9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + babel-loader@9.1.3(@babel/core@7.26.0)(webpack@5.94.0(@swc/core@1.5.7)): dependencies: '@babel/core': 7.26.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) babel-plugin-istanbul@6.1.1: dependencies: @@ -25348,13 +25367,13 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 - create-jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + create-jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + jest-config: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -25401,7 +25420,7 @@ snapshots: dependencies: hyphenate-style-name: 1.0.5 - css-loader@6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + css-loader@6.11.0(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: icss-utils: 5.1.0(postcss@8.4.31) postcss: 8.4.31 @@ -25412,9 +25431,9 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.2 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - css-loader@6.11.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + css-loader@6.11.0(webpack@5.94.0(@swc/core@1.5.7)): dependencies: icss-utils: 5.1.0(postcss@8.4.31) postcss: 8.4.31 @@ -25425,7 +25444,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.2 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) css-select@4.3.0: dependencies: @@ -26345,8 +26364,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -26385,13 +26404,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.7 enhanced-resolve: 5.16.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -26413,14 +26432,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -26471,7 +26490,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -26481,7 +26500,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -26586,17 +26605,11 @@ snapshots: - supports-color - typescript - eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))): + eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))): dependencies: fast-glob: 3.3.2 postcss: 8.4.31 - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) - - eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3))): - dependencies: - fast-glob: 3.3.2 - postcss: 8.4.31 - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@18.19.33)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)): dependencies: @@ -26609,17 +26622,6 @@ snapshots: - supports-color - typescript - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3)(vitest@2.1.4(@edge-runtime/vm@3.2.0)(@types/node@22.5.5)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0)): - dependencies: - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.3) - eslint: 8.57.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0)(typescript@5.4.3) - vitest: 2.1.4(@edge-runtime/vm@3.2.0)(@types/node@22.5.5)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) - transitivePeerDependencies: - - supports-color - - typescript - eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -27119,7 +27121,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: '@babel/code-frame': 7.26.2 chalk: 4.1.2 @@ -27134,9 +27136,9 @@ snapshots: semver: 7.6.2 tapable: 2.2.1 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)): dependencies: '@babel/code-frame': 7.26.2 chalk: 4.1.2 @@ -27151,7 +27153,7 @@ snapshots: semver: 7.6.2 tapable: 2.2.1 typescript: 5.4.3 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) form-data@2.5.1: dependencies: @@ -27743,7 +27745,7 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + html-webpack-plugin@5.6.0(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -27751,9 +27753,9 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - html-webpack-plugin@5.6.0(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + html-webpack-plugin@5.6.0(webpack@5.94.0(@swc/core@1.5.7)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -27761,7 +27763,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) htmlparser2@6.1.0: dependencies: @@ -28337,16 +28339,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + jest-cli@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + create-jest: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + jest-config: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -28356,7 +28358,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + jest-config@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: '@babel/core': 7.24.5 '@jest/test-sequencer': 29.7.0 @@ -28382,12 +28384,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 18.19.33 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.12.12)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + jest-config@29.7.0(@types/node@20.12.12)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: '@babel/core': 7.24.5 '@jest/test-sequencer': 29.7.0 @@ -28413,7 +28415,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.12.12 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -28639,12 +28641,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + jest@29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + jest-cli: 29.7.0(@types/node@18.19.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -30145,7 +30147,7 @@ snapshots: node-int64@0.4.0: {} - node-polyfill-webpack-plugin@2.0.1(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + node-polyfill-webpack-plugin@2.0.1(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: assert: 2.1.0 browserify-zlib: 0.2.0 @@ -30172,9 +30174,9 @@ snapshots: url: 0.11.3 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - node-polyfill-webpack-plugin@2.0.1(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + node-polyfill-webpack-plugin@2.0.1(webpack@5.94.0(@swc/core@1.5.7)): dependencies: assert: 2.1.0 browserify-zlib: 0.2.0 @@ -30201,7 +30203,7 @@ snapshots: url: 0.11.3 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) node-releases@2.0.14: {} @@ -30358,6 +30360,8 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openapi-types@12.1.3: {} + opener@1.5.2: {} optimism@0.18.0: @@ -30742,37 +30746,37 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.31 - postcss-load-config@3.1.4(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + postcss-load-config@3.1.4(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.4.31 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) - postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: lilconfig: 3.1.1 yaml: 2.4.2 optionalDependencies: postcss: 8.4.31 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3) - postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3)): + postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3)): dependencies: lilconfig: 3.1.1 yaml: 2.4.2 optionalDependencies: postcss: 8.4.31 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3) - postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)): + postcss-load-config@4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)): dependencies: lilconfig: 3.1.1 yaml: 2.4.2 optionalDependencies: postcss: 8.4.31 - ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3) postcss-load-config@6.0.1(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(yaml@2.4.2): dependencies: @@ -30783,25 +30787,25 @@ snapshots: tsx: 4.19.2 yaml: 2.4.2 - postcss-loader@8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + postcss-loader@8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: cosmiconfig: 9.0.0(typescript@5.4.3) jiti: 1.21.0 postcss: 8.4.31 semver: 7.6.2 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + postcss-loader@8.1.1(postcss@8.4.31)(typescript@5.4.3)(webpack@5.94.0(@swc/core@1.5.7)): dependencies: cosmiconfig: 9.0.0(typescript@5.4.3) jiti: 1.21.0 postcss: 8.4.31 semver: 7.6.2 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) transitivePeerDependencies: - typescript @@ -31914,17 +31918,17 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + sass-loader@13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: neo-async: 2.6.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) optionalDependencies: sass: 1.77.0 - sass-loader@13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + sass-loader@13.3.3(sass@1.77.0)(webpack@5.94.0(@swc/core@1.5.7)): dependencies: neo-async: 2.6.2 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) optionalDependencies: sass: 1.77.0 @@ -32443,13 +32447,13 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 4.1.0 - style-loader@3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + style-loader@3.3.4(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - style-loader@3.3.4(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + style-loader@3.3.4(webpack@5.94.0(@swc/core@1.5.7)): dependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) style-to-js@1.1.3: dependencies: @@ -32520,10 +32524,10 @@ snapshots: stylelint: 16.5.0(typescript@5.4.3) stylelint-config-recommended: 14.0.0(stylelint@16.5.0(typescript@5.4.3)) - stylelint-config-tailwindcss@0.0.7(stylelint@16.5.0(typescript@5.4.3))(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))): + stylelint-config-tailwindcss@0.0.7(stylelint@16.5.0(typescript@5.4.3))(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))): dependencies: stylelint: 16.5.0(typescript@5.4.3) - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) stylelint-scss@6.3.0(stylelint@16.5.0(typescript@5.4.3)): dependencies: @@ -32668,11 +32672,11 @@ snapshots: dependencies: '@babel/runtime': 7.24.5 - tailwindcss-animate@1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3))): dependencies: - tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3)) - tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)): + tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -32691,7 +32695,7 @@ snapshots: postcss: 8.4.31 postcss-import: 15.1.0(postcss@8.4.31) postcss-js: 4.0.1(postcss@8.4.31) - postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) postcss-nested: 6.0.1(postcss@8.4.31) postcss-selector-parser: 6.0.16 resolve: 1.22.8 @@ -32699,7 +32703,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3)): + tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -32718,7 +32722,7 @@ snapshots: postcss: 8.4.31 postcss-import: 15.1.0(postcss@8.4.31) postcss-js: 4.0.1(postcss@8.4.31) - postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3)) + postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3)) postcss-nested: 6.0.1(postcss@8.4.31) postcss-selector-parser: 6.0.16 resolve: 1.22.8 @@ -32726,7 +32730,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)): + tailwindcss@3.4.3(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -32745,7 +32749,7 @@ snapshots: postcss: 8.4.31 postcss-import: 15.1.0(postcss@8.4.31) postcss-js: 4.0.1(postcss@8.4.31) - postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3)) + postcss-load-config: 4.0.2(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3)) postcss-nested: 6.0.1(postcss@8.4.31) postcss-selector-parser: 6.0.16 resolve: 1.22.8 @@ -32792,26 +32796,26 @@ snapshots: dependencies: memoizerific: 1.11.3 - terser-webpack-plugin@5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + terser-webpack-plugin@5.3.10(@swc/core@1.5.7)(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.5) esbuild: 0.20.2 - terser-webpack-plugin@5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + terser-webpack-plugin@5.3.10(@swc/core@1.5.7)(webpack@5.94.0(@swc/core@1.5.7)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.0 - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.5) @@ -33011,7 +33015,7 @@ snapshots: '@ts-morph/common': 0.20.0 code-block-writer: 12.0.0 - ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@4.9.5): + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@4.9.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -33031,7 +33035,7 @@ snapshots: optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.5) - ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3): + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -33051,7 +33055,7 @@ snapshots: optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.5) - ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@20.12.12)(typescript@5.4.3): + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.12.12)(typescript@5.4.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -33072,7 +33076,7 @@ snapshots: '@swc/core': 1.5.7(@swc/helpers@0.5.5) optional: true - ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@22.5.5)(typescript@5.4.3): + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@22.5.5)(typescript@5.4.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -33137,7 +33141,7 @@ snapshots: tslib@2.8.0: {} - tsup@8.3.5(@swc/core@1.5.7(@swc/helpers@0.5.5))(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2): + tsup@8.3.5(@swc/core@1.5.7)(jiti@1.21.0)(postcss@8.4.31)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.2): dependencies: bundle-require: 5.0.0(esbuild@0.20.2) cac: 6.7.14 @@ -33279,7 +33283,7 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript-plugin-css-modules@5.1.0(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3))(typescript@5.4.3): + typescript-plugin-css-modules@5.1.0(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3))(typescript@5.4.3): dependencies: '@types/postcss-modules-local-by-default': 4.0.2 '@types/postcss-modules-scope': 3.0.4 @@ -33288,7 +33292,7 @@ snapshots: less: 4.2.0 lodash.camelcase: 4.3.0 postcss: 8.4.31 - postcss-load-config: 3.1.4(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.5))(@types/node@18.19.33)(typescript@5.4.3)) + postcss-load-config: 3.1.4(postcss@8.4.31)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.33)(typescript@5.4.3)) postcss-modules-extract-imports: 3.1.0(postcss@8.4.31) postcss-modules-local-by-default: 4.0.5(postcss@8.4.31) postcss-modules-scope: 3.2.0(postcss@8.4.31) @@ -33875,7 +33879,7 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@6.1.3(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)): + webpack-dev-middleware@6.1.3(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -33883,9 +33887,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.2.0 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2) + webpack: 5.94.0(@swc/core@1.5.7)(esbuild@0.20.2) - webpack-dev-middleware@6.1.3(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))): + webpack-dev-middleware@6.1.3(webpack@5.94.0(@swc/core@1.5.7)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -33893,7 +33897,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.2.0 optionalDependencies: - webpack: 5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)) + webpack: 5.94.0(@swc/core@1.5.7) webpack-hot-middleware@2.26.1: dependencies: @@ -33907,7 +33911,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5)): + webpack@5.94.0(@swc/core@1.5.7): dependencies: '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 @@ -33929,7 +33933,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7)(webpack@5.94.0(@swc/core@1.5.7)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -33937,7 +33941,7 @@ snapshots: - esbuild - uglify-js - webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2): + webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2): dependencies: '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 @@ -33959,7 +33963,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.20.2)) + terser-webpack-plugin: 5.3.10(@swc/core@1.5.7)(esbuild@0.20.2)(webpack@5.94.0(@swc/core@1.5.7)(esbuild@0.20.2)) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: From 4437b3ecb2351980d43d8d06863a3be90ecee723 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Wed, 13 Nov 2024 19:25:29 -0500 Subject: [PATCH 02/14] composable api node working --- .../openapi/shared/composable/availability.node.ts | 10 ++++++---- .../openapi/shared/interfaces/api.node.interface.ts | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/parsers/openapi/shared/composable/availability.node.ts b/packages/parsers/openapi/shared/composable/availability.node.ts index aab9c1d2f2..86c6cf91c0 100644 --- a/packages/parsers/openapi/shared/composable/availability.node.ts +++ b/packages/parsers/openapi/shared/composable/availability.node.ts @@ -1,10 +1,11 @@ import { ApiNode, ApiNodeContext, ComposableApiNode } from "../interfaces/api.node.interface"; -export class AvailabilityNode & { availability: string }> +export class AvailabilityNode & { availability: string }> implements ComposableApiNode< + T, InputNode, - ReturnType & { + T & { availability: string; } > @@ -22,11 +23,12 @@ export class AvailabilityNode & { ava this.preProcessedInput = this.inputNode.preProcessedInput; } - outputFdrShape(): ReturnType & { + outputFdrShape(): T & { availability: string; } { + const baseShape = this.inputNode.outputFdrShape(); return { - ...this.inputNode.outputFdrShape(), + ...baseShape, availability: this.inputNode.availability, }; } diff --git a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts index 4c48ce4ce1..ce7dc0b031 100644 --- a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts +++ b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts @@ -18,9 +18,9 @@ export interface ApiNode { outputFdrShape: () => FdrShape; } -export interface ComposableApiNode, FdrShape> +export interface ComposableApiNode, FdrShape> extends ApiNode { inputNode: InputNode; - outputFdrShape: () => ReturnType & FdrShape; + outputFdrShape: () => T & FdrShape; } From 66c2d2974867ffde9284185f31af44bdbafdf8da Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Wed, 13 Nov 2024 19:26:06 -0500 Subject: [PATCH 03/14] small addition --- packages/parsers/openapi/3.1/openapi.node.ts | 1 + packages/parsers/openapi/shared/stages/fdr/api.stage.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/parsers/openapi/3.1/openapi.node.ts b/packages/parsers/openapi/3.1/openapi.node.ts index a4a95fa1a5..ed6beb893d 100644 --- a/packages/parsers/openapi/3.1/openapi.node.ts +++ b/packages/parsers/openapi/3.1/openapi.node.ts @@ -2,6 +2,7 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; import { OpenAPIV3_1 } from "openapi-types"; import { v4 } from "uuid"; import { ApiNode, ApiNodeContext } from "../shared/interfaces/api.node.interface"; +import { FdrApiStage } from "../shared/stages/fdr/api.stage"; export class OpenApi3_1Node implements ApiNode { name = "OpenApi3_1"; diff --git a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts index 01005812cd..ff48b63437 100644 --- a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts +++ b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts @@ -9,7 +9,7 @@ type ApiProcessingStageOutput = { webhooks: FdrAPI.api.latest.ApiDefinition["webhooks"]; }; -export class ApiProcessingStage implements FdrStage { +export class FdrApiStage implements FdrStage { stageName = "ApiProcessingStage"; name = "ApiProcessingStage"; qualifiedId: string; From 1332df3e22f981a1083a77fdca5dfc23673882f0 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Thu, 14 Nov 2024 16:58:25 -0500 Subject: [PATCH 04/14] demo uts --- packages/parsers/__test__/demo.test.ts | 148 ++++++ packages/parsers/openapi/3.1/openapi.node.ts | 102 ++-- packages/parsers/openapi/demo.ts | 226 +++++++++ .../shared/composable/availability.node.ts | 454 ++++++++++++++++-- .../shared/interfaces/api.node.interface.ts | 17 +- .../shared/interfaces/fdr.stage.interface.ts | 4 +- .../openapi/shared/nodes/authScheme.node.ts | 24 + .../openapi/shared/nodes/endpoint.node.ts | 37 ++ .../parsers/openapi/shared/nodes/path.node.ts | 24 + .../openapi/shared/nodes/pathPart.node.ts | 29 ++ .../openapi/shared/nodes/string.node.ts | 72 +-- .../openapi/shared/stages/fdr/api.stage.ts | 100 ++-- 12 files changed, 1074 insertions(+), 163 deletions(-) create mode 100644 packages/parsers/__test__/demo.test.ts create mode 100644 packages/parsers/openapi/demo.ts create mode 100644 packages/parsers/openapi/shared/nodes/authScheme.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/path.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/pathPart.node.ts diff --git a/packages/parsers/__test__/demo.test.ts b/packages/parsers/__test__/demo.test.ts new file mode 100644 index 0000000000..a9e49ae30e --- /dev/null +++ b/packages/parsers/__test__/demo.test.ts @@ -0,0 +1,148 @@ +import { expect } from "vitest"; + +import { ComponentsNode } from "../openapi/demo"; + +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { describe, it } from "vitest"; +import { ApiNodeContext, ErrorCollector } from "../openapi/shared/interfaces/api.node.interface"; + +describe("ComponentsNode", () => { + describe("outputFdrShape", () => { + it("should convert OpenAPI components to FDR type definitions", () => { + const context: ApiNodeContext = { + orgId: "test-org", + apiId: "test-api", + // @ts-expect-error logger is not required + logger: console, + errorCollector: new ErrorCollector(), + }; + + const input: OpenAPIV3_1.ComponentsObject = { + schemas: { + Person: { + // @ts-expect-error availability is not an out of the box OpenApi Property + "x-fern-availability": "generally-available", + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }, + }; + + const componentsNode = new ComponentsNode(context, input, []); + const result = componentsNode.outputFdrShape(); + + expect(result).toHaveLength(1); + + const personType = result[0]; + expect(personType?.name).toBe("Person"); + expect(personType?.availability).toBe(FdrAPI.Availability.GenerallyAvailable); + expect(context.errorCollector.errors).toHaveLength(0); + }); + + it("should handle invalid availability value", () => { + const context: ApiNodeContext = { + orgId: "test-org", + apiId: "test-api", + // @ts-expect-error logger is not required + logger: console, + errorCollector: new ErrorCollector(), + }; + + const input: OpenAPIV3_1.ComponentsObject = { + schemas: { + Person: { + // @ts-expect-error availability is not an out of the box OpenApi Property + "x-fern-availability": "deep", + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }, + }; + + const componentsNode = new ComponentsNode(context, input, []); + const result = componentsNode.outputFdrShape(); + + expect(result).toHaveLength(1); + const personType = result[0]; + expect(personType?.name).toBe("Person"); + expect(personType?.availability).toBeUndefined(); + expect(context.errorCollector.errors).toHaveLength(1); + }); + + it("should handle non-string availability value", () => { + const context: ApiNodeContext = { + orgId: "test-org", + apiId: "test-api", + // @ts-expect-error logger is not required + logger: console, + errorCollector: new ErrorCollector(), + }; + + const input: OpenAPIV3_1.ComponentsObject = { + schemas: { + Person: { + // @ts-expect-error availability is not an out of the box OpenApi Property + "x-fern-availability": true, + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }, + }; + + const componentsNode = new ComponentsNode(context, input, []); + const result = componentsNode.outputFdrShape(); + + expect(result).toHaveLength(1); + const personType = result[0]; + expect(personType?.name).toBe("Person"); + expect(personType?.availability).toBeUndefined(); + expect(context.errorCollector.errors).toHaveLength(1); + }); + + it("should handle deprecated flag", () => { + const context: ApiNodeContext = { + orgId: "test-org", + apiId: "test-api", + // @ts-expect-error logger is not required + logger: console, + errorCollector: new ErrorCollector(), + }; + + const input: OpenAPIV3_1.ComponentsObject = { + schemas: { + Person: { + deprecated: true, + type: "object", + properties: { + name: { + type: "string", + }, + }, + }, + }, + }; + + const componentsNode = new ComponentsNode(context, input, []); + const result = componentsNode.outputFdrShape(); + + expect(result).toHaveLength(1); + const personType = result[0]; + expect(personType?.name).toBe("Person"); + expect(personType?.availability).toBe("Deprecated"); + expect(context.errorCollector.errors).toHaveLength(0); + }); + }); +}); diff --git a/packages/parsers/openapi/3.1/openapi.node.ts b/packages/parsers/openapi/3.1/openapi.node.ts index ed6beb893d..6b6f8cb914 100644 --- a/packages/parsers/openapi/3.1/openapi.node.ts +++ b/packages/parsers/openapi/3.1/openapi.node.ts @@ -1,56 +1,58 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; -import { v4 } from "uuid"; -import { ApiNode, ApiNodeContext } from "../shared/interfaces/api.node.interface"; -import { FdrApiStage } from "../shared/stages/fdr/api.stage"; +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { OpenAPIV3_1 } from "openapi-types"; +// import { v4 } from "uuid"; +// import { ApiNode, ApiNodeContext } from "../shared/interfaces/api.node.interface"; +// import { FdrApiStage } from "../shared/stages/fdr/api.stage"; -export class OpenApi3_1Node implements ApiNode { - name = "OpenApi3_1"; - context: ApiNodeContext; - qualifiedId: string = "OpenApi3_1"; +// export class OpenApi3_1Node implements ApiNode { +// name = "OpenApi3_1"; +// context: ApiNodeContext; +// qualifiedId: string = "OpenApi3_1"; - constructor( - context: ApiNodeContext, - readonly preProcessedInput: OpenAPIV3_1.Document, - ) { - this.context = context; - this.accessPath = [this]; - } - accessPath: ApiNode[]; - outputFdrShape = (): FdrAPI.api.latest.ApiDefinition => { - const { endpoints, websockets, webhooks } = new FdrApiStage( - this.context, - this.preProcessedInput.paths, - this.accessPath, - ).outputFdrShape(); +// constructor( +// context: ApiNodeContext, +// readonly preProcessedInput: OpenAPIV3_1.Document, +// ) { +// this.context = context; +// this.accessPath = [this]; +// } +// accessPath: ApiNode[]; - const types = new FdrTypesStage( - this.context, - this.preProcessedInput.components?.schemas, - this.accessPath, - ).outputFdrShape(); +// validate +// outputFdrShape = (): FdrAPI.api.latest.ApiDefinition => { +// const { endpoints, websockets, webhooks } = new FdrApiStage( +// this.context, +// this.preProcessedInput.paths, +// this.accessPath, +// ).outputFdrShape(); - const auths = new FdrAuthsStage( - this.context, - this.preProcessedInput.components?.securitySchemes, - this.accessPath, - ).outputFdrShape(); +// const types = new FdrTypesStage( +// this.context, +// this.preProcessedInput.components?.schemas, +// this.accessPath, +// ).outputFdrShape(); - const globalHeaders = new FdrGlobalHeadersStage( - this.context, - this.preProcessedInput.components?.headers, - this.accessPath, - ).outputFdrShape(); +// const auths = new FdrAuthsStage( +// this.context, +// this.preProcessedInput.components?.securitySchemes, +// this.accessPath, +// ).outputFdrShape(); - return { - id: v4(), - endpoints, - websockets, - webhooks, - types, - subpackages: {}, - auths, - globalHeaders, - }; - }; -} +// const globalHeaders = new FdrGlobalHeadersStage( +// this.context, +// this.preProcessedInput.components?.headers, +// this.accessPath, +// ).outputFdrShape(); + +// return { +// id: v4(), +// endpoints, +// websockets, +// webhooks, +// types, +// subpackages: {}, +// auths, +// globalHeaders, +// }; +// }; +// } diff --git a/packages/parsers/openapi/demo.ts b/packages/parsers/openapi/demo.ts new file mode 100644 index 0000000000..2852bf0cd7 --- /dev/null +++ b/packages/parsers/openapi/demo.ts @@ -0,0 +1,226 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { ApiNode, ApiNodeContext } from "./shared/interfaces/api.node.interface"; +import { FdrStage } from "./shared/interfaces/fdr.stage.interface"; + +export class AvailabilityNode implements ApiNode { + id: string; + availability: FdrAPI.Availability | undefined; + + constructor( + readonly context: ApiNodeContext, + readonly input: unknown, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.AvailabilityNode`; + if (input && typeof input === "object" && "x-fern-availability" in input) { + if (typeof input["x-fern-availability"] !== "string") { + context.errorCollector.addError(`Availability is not a string for ${this.id}`); + return; + } + this.availability = this.convertAvailability(input["x-fern-availability"]); + if (!this.availability) { + context.errorCollector.addError(`Availability is not defined for ${this.id}`); + } + return; + } + + if (input && typeof input === "object" && "deprecated" in input && typeof input.deprecated === "boolean") { + this.availability = input.deprecated ? FdrAPI.Availability.Deprecated : undefined; + if (!this.availability) { + context.errorCollector.addError(`Availability is not defined for ${this.id}`); + } + return; + } + } + + convertAvailability = (availability: string): FdrAPI.Availability | undefined => { + switch (availability) { + case "beta": + return FdrAPI.Availability.Beta; + case "deprecated": + return FdrAPI.Availability.Deprecated; + case "development": + return FdrAPI.Availability.InDevelopment; + case "pre-release": + return FdrAPI.Availability.PreRelease; + case "stabel": + return FdrAPI.Availability.Stable; + case "generally-available": + return FdrAPI.Availability.GenerallyAvailable; + default: + return undefined; + } + }; + + outputFdrShape = (): FdrAPI.Availability | undefined => { + return this.availability; + }; +} + +export class DemoStringNode implements ApiNode { + id: string; + + regex: string | undefined; + format: string | undefined; + default: string | undefined; + minLength: number | undefined; + maxLength: number | undefined; + + constructor( + readonly context: ApiNodeContext, + readonly input: OpenAPIV3_1.SchemaObject, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.DemoStringNode`; + this.regex = input.pattern; + // this.format = input.format; + this.default = input.default; + this.minLength = input.minLength; + this.maxLength = input.maxLength; + } + + outputFdrShape = (): FdrAPI.api.latest.PrimitiveType => { + return { + type: "string", + regex: this.regex, + minLength: this.minLength, + maxLength: this.maxLength, + default: this.default, + }; + }; +} + +export class DemoTypeShapeStage implements FdrStage { + id: string; + + stringNode: DemoStringNode | undefined; + properties: DemoPropertyNode[] = []; + constructor( + readonly context: ApiNodeContext, + readonly input: OpenAPIV3_1.SchemaObject, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeShapeStage`; + + // This should be decomposed into container and primitive nodes, but this is a bit too much for now + if (input.type === "object") { + this.properties = Object.entries(input.properties ?? {}).map(([name, schema]) => { + return new DemoPropertyNode(name, this.context, schema, this.accessPath); + }); + } else if (input.type === "string") { + this.stringNode = new DemoStringNode(this.context, this.input, this.accessPath); + } + } + + outputFdrShape = (): FdrAPI.api.latest.TypeShape => { + // we include this because this is non-exhaustive for now, for demo purposes + if (this.stringNode) { + return { + type: "alias", + value: { + type: "primitive", + value: this.stringNode.outputFdrShape(), + }, + }; + } else { + return { + type: "object", + properties: this.properties.map((property) => property.outputFdrShape()), + extends: [], + extraProperties: undefined, + }; + } + }; +} + +export class DemoPropertyNode + implements ApiNode +{ + id: string; + + propertyShape: DemoTypeShapeStage | undefined = undefined; + availability: AvailabilityNode | undefined = undefined; + + constructor( + readonly name: string, + readonly context: ApiNodeContext, + readonly input: OpenAPIV3_1.SchemaObject, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.DemoPropertyNode`; + if (input.type === "string") { + this.propertyShape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); + } + this.availability = new AvailabilityNode(this.context, this.input, this.accessPath); + } + + outputFdrShape = (): FdrAPI.api.latest.ObjectProperty => { + // we include this because this is non-exhaustive for now, for demo purposes + if (!this.propertyShape) { + throw new Error("Property shape is undefined"); + } + + return { + key: FdrAPI.PropertyKey(this.name), + valueShape: this.propertyShape.outputFdrShape(), + description: this.input.description, + availability: this.availability?.outputFdrShape(), + }; + }; +} + +export class DemoSchemaNode implements ApiNode { + id: string; + + shape: DemoTypeShapeStage | undefined = undefined; + availability: AvailabilityNode; + + constructor( + readonly name: string, + readonly context: ApiNodeContext, + readonly input: OpenAPIV3_1.SchemaObject, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDefinitionNode`; + if (this.input.type === "object") { + this.shape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); + } + this.availability = new AvailabilityNode(this.context, this.input, this.accessPath); + } + + outputFdrShape = (): FdrAPI.api.latest.TypeDefinition => { + // we include this because this is non-exhaustive for now, for demo purposes + if (!this.shape) { + throw new Error("Shape is undefined"); + } + + return { + name: this.name, + shape: this.shape.outputFdrShape(), + description: this.input.description, + availability: this.availability.outputFdrShape(), + }; + }; +} + +export class ComponentsNode implements ApiNode { + id: string; + + schemas: DemoSchemaNode[] = []; + + constructor( + readonly context: ApiNodeContext, + readonly input: OpenAPIV3_1.ComponentsObject, + readonly accessPath: ApiNode[], + ) { + this.id = `${accessPath.map((node) => node.id).join(".")}.ComponentsNode`; + this.schemas = Object.entries(this.input.schemas ?? {}).map(([name, schema]) => { + return new DemoSchemaNode(name, this.context, schema, this.accessPath); + }); + } + + outputFdrShape = (): FdrAPI.api.latest.TypeDefinition[] => { + return this.schemas.map((schema) => schema.outputFdrShape()); + }; +} diff --git a/packages/parsers/openapi/shared/composable/availability.node.ts b/packages/parsers/openapi/shared/composable/availability.node.ts index 86c6cf91c0..59ed03e726 100644 --- a/packages/parsers/openapi/shared/composable/availability.node.ts +++ b/packages/parsers/openapi/shared/composable/availability.node.ts @@ -1,35 +1,419 @@ -import { ApiNode, ApiNodeContext, ComposableApiNode } from "../interfaces/api.node.interface"; - -export class AvailabilityNode & { availability: string }> - implements - ComposableApiNode< - T, - InputNode, - T & { - availability: string; - } - > -{ - name = "availability"; - preProcessedInput: InputNode["preProcessedInput"]; - qualifiedId: string; - - constructor( - readonly context: ApiNodeContext, - readonly inputNode: InputNode, - readonly accessPath: ApiNode[], - ) { - this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; - this.preProcessedInput = this.inputNode.preProcessedInput; - } - - outputFdrShape(): T & { - availability: string; - } { - const baseShape = this.inputNode.outputFdrShape(); - return { - ...baseShape, - availability: this.inputNode.availability, - }; - } -} +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { FernRegistry } from "@fern-api/fdr-sdk/src/client/generated"; +// import { OpenAPIV3_1 } from "openapi-types"; +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; +// import { FdrStage } from "../interfaces/fdr.stage.interface"; +// import { FdrApiStage } from "../stages/fdr/api.stage"; + +// // export class AvailabilityNode & { "x-fern-availability": string }> +// // implements +// // ComposableApiNode< +// // T, +// // InputNode, +// // T & { +// // availability: string; +// // } +// // > +// // { +// // name = "availability"; +// // preProcessedInput: InputNode["preProcessedInput"]; +// // qualifiedId: string; + +// // constructor( +// // readonly context: ApiNodeContext, +// // readonly inputNode: InputNode, +// // readonly accessPath: ApiNode[], +// // ) { +// // this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// // this.preProcessedInput = this.inputNode.preProcessedInput; +// // } + +// // outputFdrShape(): T & { +// // availability: string; +// // } { +// // const baseShape = this.inputNode.outputFdrShape(); +// // return { +// // ...baseShape, +// // availability: this.inputNode["x-fern-availability"], +// // }; +// // } +// // } + +// export class TypeNode implements ApiNode { +// name = "type"; +// qualifiedId: string; + +// reference?: string; + +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = `${accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// this.reference = "$ref" in preProcessedInput ? preProcessedInput.$ref : undefined; +// } +// input: OpenAPIV3_1.SchemaObject; +// id: string; +// parse: () => +// | { ok: true } +// | { +// ok: false; +// // implements +// // ComposableApiNode< +// // T, +// // InputNode, +// // T & { +// // availability: string; +// // } +// // > +// // { +// // name = "availability"; +// // preProcessedInput: InputNode["preProcessedInput"]; +// // qualifiedId: string; +// // constructor( +// // readonly context: ApiNodeContext, +// // readonly inputNode: InputNode, +// // readonly accessPath: ApiNode[], +// // ) { +// // this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// // this.preProcessedInput = this.inputNode.preProcessedInput; +// // } +// // outputFdrShape(): T & { +// // availability: string; +// // } { +// // const baseShape = this.inputNode.outputFdrShape(); +// // return { +// // ...baseShape, +// // availability: this.inputNode["x-fern-availability"], +// // }; +// // } +// // } +// error: string; +// }; + +// outputFdrShape = (): FdrAPI.TypeId => { +// // This is not correct, but it's a placeholder for now +// return this.reference ? FdrAPI.TypeId(this.reference) : FdrAPI.TypeId("unknown"); +// }; +// } + +// export class ContainerDiscriminationStage implements FdrApiStage { +// id: string; +// outputFdrShape: () => FdrAPI.TypeId; +// } + +// export class PrimitiveNode implements ApiNode { +// context: ApiNodeContext; +// input: OpenAPIV3_1.SchemaObject; +// accessPath: ApiNode[]; +// id: string; +// outputFdrShape: () => FdrAPI.TypeId; +// } + +// export class PropertyNode implements ApiNode { +// id: string; +// constructor( +// readonly name: string, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly context: ApiNodeContext, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.PropertyNode:${name}`; + +// this.input. +// } +// outputFdrShape: () => FdrAPI.api.latest.ObjectProperty; +// } + +// export class ExtraPropertyNode implements ApiNode { +// context: ApiNodeContext; +// input: OpenAPIV3_1.SchemaObject; +// accessPath: ApiNode[]; +// id: string; +// parse: () => +// | { ok: true } +// | { +// ok: false; +// error: string; +// }; +// outputFdrShape: () => FdrAPI.api.latest.TypeReference; +// name = "extra-property"; +// } + +// export class SchemaNode +// implements ApiNode +// { +// id: string; + +// arrayItem: SchemaNode | undefined; +// extensions: TypeNode[] = []; +// schemasToMerge: SchemaNode[] = []; +// properties: PropertyNode[] = []; +// extraProperties: ExtraPropertyNode | undefined; + +// constructor( +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly context: ApiNodeContext, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.SchemaNode:${input.title ?? input.$schema ?? "unknown"}`; +// if (this.input.type === "array") { +// this.arrayItem = new SchemaNode(this.input.items, this.context, this.accessPath); +// } else { +// if (this.input.allOf && this.input.allOf.length > 0) { +// this.input.allOf.forEach((schema) => { +// // make this a guard +// if (typeof schema === "object" && "$ref" in schema) { +// this.extensions.push(new TypeNode(this.context, schema, this.accessPath)); +// } else { +// this.schemasToMerge.push(new SchemaNode(schema, this.context, this.accessPath)); +// } +// }); +// } + +// if (this.input.properties) { +// this.properties = Object.entries(this.input.properties).map(([name, property]) => { +// return new PropertyNode(name, property, this.context, this.accessPath); +// }); +// } +// } +// } + +// outputFdrShape = (): FdrAPI.api.latest.ObjectType => { +// return { +// extends: this.extensions.map((extension) => extension.outputFdrShape()), +// properties: this.properties.map((property) => property.outputFdrShape()), +// extraProperties: this.extraProperties?.outputFdrShape(), +// }; +// }; +// } + +// export class AvailabilityNode> +// implements ApiNode { + +// id: string; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: InputNode, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.AvailabilityNode:${input.id}`; +// } + +// convertAvailability = (availability: string): FernRegistry.Availability | undefined => { +// switch (availability) { +// case "beta": +// return FernRegistry.Availability.Beta; +// case "deprecated": +// return FernRegistry.Availability.Deprecated; +// case "development": +// return FernRegistry.Availability.InDevelopment; +// case "pre-release": +// return FernRegistry.Availability.PreRelease; +// case "stabel": +// return FernRegistry.Availability.Stable; +// case "generally-available": +// return FernRegistry.Availability.GenerallyAvailable; +// default: +// return undefined; +// } +// }; + +// outputFdrShape = (): FernRegistry.Availability | undefined => { +// return "x-fern-availability" in this.input ? this.convertAvailability(this.input["x-fern-availability"] as string) : undefined; +// }; +// } + +// export class DemoTypeStage implements FdrStage { +// id: string; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeStage:${input.title ?? input.$schema ?? "unknown"}`; + +// switch (input.type) { +// case "object": +// return new DemoSchemaNode(this.context, this.input, this.accessPath); +// case "array": +// return new DemoListNode(this.context, this.input, this.accessPath); +// default: +// return new TypeNode(this.context, this.input, this.accessPath); +// } +// } +// outputFdrShape: () => FdrAPI.api.latest.TypeShape { + +// }; +// } + +// export class DemoStringNode implements ApiNode { +// id: string; + +// regex: string | undefined; +// format: string | undefined; +// default: string | undefined; +// minLength: number | undefined; +// maxLength: number | undefined; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoStringNode`; +// this.regex = input.pattern; +// // this.format = input.format; +// this.default = input.default; +// this.minLength = input.minLength; +// this.maxLength = input.maxLength; +// } + +// outputFdrShape = (): FdrAPI.api.latest.PrimitiveType => { +// return { +// type: "string", +// regex: this.regex, +// minLength: this.minLength, +// maxLength: this.maxLength, +// default: this.default, +// } +// }; +// } + +// export class DemoTypeShapeStage implements FdrStage { +// id: string; + +// stringNode: DemoStringNode | undefined; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeShapeStage`; + +// if (input.type === "string") { +// this.stringNode = new DemoStringNode(this.context, this.input, this.accessPath); +// } +// } + +// outputFdrShape = (): FdrAPI.api.latest.TypeShape => { +// // we include this because this is non-exhaustive for now, for demo purposes +// if (!this.stringNode) { +// throw new Error("String node is undefined"); +// } + +// return { +// type: "alias", +// value: { +// type: "primitive", +// value: this.stringNode.outputFdrShape(), +// }, +// } +// }; +// } + +// export class DemoPropertyNode implements ApiNode { +// id: string; + +// propertyShape: DemoTypeShapeStage | undefined = undefined; +// availability: AvailabilityNode | undefined = undefined; + +// constructor( +// readonly name: string, +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoPropertyNode`; + +// if (input.type === "string") { +// this.propertyShape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); +// } +// this.availability = new AvailabilityNode(this.context, this, this.accessPath); +// } + +// outputFdrShape = (): FdrAPI.api.latest.ObjectProperty => { +// // we include this because this is non-exhaustive for now, for demo purposes +// if (!this.propertyShape) { +// throw new Error("Property shape is undefined"); +// } + +// return { +// key: FdrAPI.PropertyKey(this.name), +// valueShape: this.propertyShape.outputFdrShape(), +// description: this.input.description, +// availability: this.availability?.outputFdrShape(), +// } +// }; +// } + +// export class DemoTypeDiscriminationStage implements FdrApiStage { +// id: string; + +// properties: DemoPropertyNode[] = []; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDiscriminationStage`; + +// if (input.type === "object" && input.properties) { +// this.properties = Object.entries(input.properties).map(([name, property]) => { +// return new DemoPropertyNode(name, property, this.context, this.accessPath); +// }); +// } +// } + +// outputFdrShape = (): FdrAPI.api.latest.TypeShape => { + +// }; +// } + +// export class DemoSchemaNode implements ApiNode { +// id: string; + +// constructor( +// readonly name: string, +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.SchemaObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDefinitionNode`; +// this.shape = new DemoTypeDiscriminationStage(this.context, this.input, this.accessPath); +// } + +// outputFdrShape = (): FdrAPI.api.latest.TypeDefinition => { +// return { +// name: this.name, +// shape: +// } +// }; +// } + +// export class ComponentsNode implements ApiNode { +// id: string; + +// schemas: DemoSchemaNode[] = []; + +// constructor( +// readonly context: ApiNodeContext, +// readonly input: OpenAPIV3_1.ComponentsObject, +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${accessPath.map((node) => node.id).join(".")}.ComponentsNode`; +// this.schemas = Object.entries(this.input.schemas ?? {}).map(([name, schema]) => { +// return new DemoSchemaNode(name, this.context, schema, this.accessPath); +// }); +// } + +// outputFdrShape = (): FdrAPI.api.latest.TypeDefinition[] => { +// return this.schemas.map((schema) => schema.outputFdrShape()); +// }; + +// } diff --git a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts index ce7dc0b031..b257982f08 100644 --- a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts +++ b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts @@ -1,25 +1,32 @@ import { Logger } from "@playwright/test"; +export class ErrorCollector { + errors: string[] = []; + + addError(error: string): void { + this.errors.push(error); + } +} + export interface ApiNodeContext { orgId: string; apiId: string; logger: Logger; + errorCollector: ErrorCollector; } export interface ApiNode { context: ApiNodeContext; - preProcessedInput: InputShape; + input: InputShape; accessPath: ApiNode[]; - name: string; - - qualifiedId: string; + id: string; outputFdrShape: () => FdrShape; } export interface ComposableApiNode, FdrShape> - extends ApiNode { + extends ApiNode { inputNode: InputNode; outputFdrShape: () => T & FdrShape; diff --git a/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts b/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts index dfe05b9d07..1f54110787 100644 --- a/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts +++ b/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts @@ -1,5 +1,3 @@ import { ApiNode } from "./api.node.interface"; -export interface FdrStage extends ApiNode { - stageName: string; -} +export interface FdrStage extends ApiNode {} diff --git a/packages/parsers/openapi/shared/nodes/authScheme.node.ts b/packages/parsers/openapi/shared/nodes/authScheme.node.ts new file mode 100644 index 0000000000..ab37d93c50 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/authScheme.node.ts @@ -0,0 +1,24 @@ +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { OpenAPIV3, OpenAPIV3_1 } from "openapi-types"; +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; + +// export class AuthSchemeIdsNode +// implements ApiNode +// { +// name = "authSchemeIds"; + +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: OpenAPIV3_1.SecurityRequirementObject[], +// readonly accessPath: ApiNode[], +// ) { +// this.id = `${this.accessPath.map((node) => node.id).join(".")}.AuthSchemeIdsNode`; +// this.accessPath.push(this); +// } +// input: OpenAPIV3.SecurityRequirementObject[]; +// id: string; + +// outputFdrShape = (): FdrAPI.api.latest.AuthSchemeId[] => { +// return this.preProcessedInput.map((authScheme) => FdrAPI.AuthSchemeId(authScheme[0])); +// }; +// } diff --git a/packages/parsers/openapi/shared/nodes/endpoint.node.ts b/packages/parsers/openapi/shared/nodes/endpoint.node.ts index e69de29bb2..a180d25db4 100644 --- a/packages/parsers/openapi/shared/nodes/endpoint.node.ts +++ b/packages/parsers/openapi/shared/nodes/endpoint.node.ts @@ -0,0 +1,37 @@ +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { OpenAPIV3_1 } from "openapi-types"; +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; +// import { PathNode } from "./path.node"; +// import { AuthSchemeIdsNode } from "./authScheme.node"; + +// export class EndpointNode implements ApiNode { +// name = "endpoint"; +// qualifiedId: string; + +// constructor( +// private readonly id: string, +// private readonly method: FdrAPI.HttpMethod, +// private readonly path: string, +// readonly context: ApiNodeContext, +// readonly preProcessedInput: OpenAPIV3_1.OperationObject, +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// this.accessPath.push(this); +// } + +// outputFdrShape = (): FdrAPI.api.latest.EndpointDefinition => { +// return { +// id: FdrAPI.EndpointId(this.id), +// method: this.method, +// path: new PathNode(this.context, this.path, this.accessPath).outputFdrShape(), +// auth: this.preProcessedInput.security +// ? new AuthSchemeIdsNode(this.context, this.preProcessedInput.security, this.accessPath).outputFdrShape() +// : undefined, +// defaultEnvironment: undefined, +// environments: undefined, +// pathParameters: undefined, +// queryParameters: undefined, +// }; +// }; +// } diff --git a/packages/parsers/openapi/shared/nodes/path.node.ts b/packages/parsers/openapi/shared/nodes/path.node.ts new file mode 100644 index 0000000000..c94fd38d3b --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/path.node.ts @@ -0,0 +1,24 @@ +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; + +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { PathPartNode } from "./pathPart.node"; + +// export class PathNode implements ApiNode { +// name = "path"; +// qualifiedId: string; + +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: string, +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// this.accessPath.push(this); +// } + +// outputFdrShape = (): FdrAPI.api.latest.PathPart[] => { +// return this.preProcessedInput +// .split("/") +// .map((part) => new PathPartNode(this.context, part, this.accessPath).outputFdrShape()); +// }; +// } diff --git a/packages/parsers/openapi/shared/nodes/pathPart.node.ts b/packages/parsers/openapi/shared/nodes/pathPart.node.ts new file mode 100644 index 0000000000..04929767e0 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/pathPart.node.ts @@ -0,0 +1,29 @@ +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; + +// import { FdrAPI } from "@fern-api/fdr-sdk"; + +// export class PathPartNode implements ApiNode { +// name = "pathPart"; +// qualifiedId: string; + +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: string, +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// this.accessPath.push(this); +// } + +// outputFdrShape = (): FdrAPI.api.latest.PathPart => { +// return this.preProcessedInput.startsWith("{") && this.preProcessedInput.endsWith("}") +// ? { +// type: "pathParameter", +// value: FdrAPI.PropertyKey(this.preProcessedInput.slice(1, -1)), +// } +// : { +// type: "literal", +// value: this.preProcessedInput, +// }; +// }; +// } diff --git a/packages/parsers/openapi/shared/nodes/string.node.ts b/packages/parsers/openapi/shared/nodes/string.node.ts index 94143acfc7..dc42a4b12b 100644 --- a/packages/parsers/openapi/shared/nodes/string.node.ts +++ b/packages/parsers/openapi/shared/nodes/string.node.ts @@ -1,43 +1,43 @@ -import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; -import { PrimitiveNode } from "../interfaces/primitive.node.interface"; +// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; +// import { PrimitiveNode } from "../interfaces/primitive.node.interface"; -type FdrStringShape = { - value: string; - format?: "uuid" | "email"; - pattern?: RegExp; -}; +// type FdrStringShape = { +// value: string; +// format?: "uuid" | "email"; +// pattern?: RegExp; +// }; -type RedocStringNode = { - stringValue: string; - format?: "uuid" | "email"; - pattern?: RegExp; -}; +// type RedocStringNode = { +// stringValue: string; +// format?: "uuid" | "email"; +// pattern?: RegExp; +// }; -export class StringNode implements PrimitiveNode { - name = "String"; - qualifiedId: string; +// export class StringNode implements PrimitiveNode { +// name = "String"; +// qualifiedId: string; - private readonly stringValue: string; - private readonly format?: "uuid" | "email"; - private readonly pattern?: RegExp; +// private readonly stringValue: string; +// private readonly format?: "uuid" | "email"; +// private readonly pattern?: RegExp; - constructor( - readonly context: ApiNodeContext, - readonly preProcessedInput: RedocStringNode, - readonly accessPath: ApiNode[], - ) { - this.qualifiedId = accessPath.map((node) => node.qualifiedId).join("."); +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: RedocStringNode, +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = accessPath.map((node) => node.qualifiedId).join("."); - this.stringValue = preProcessedInput.stringValue; - this.format = preProcessedInput.format; - this.pattern = preProcessedInput.pattern; - } +// this.stringValue = preProcessedInput.stringValue; +// this.format = preProcessedInput.format; +// this.pattern = preProcessedInput.pattern; +// } - outputFdrShape(): FdrStringShape { - return { - value: this.stringValue, - ...(this.format ? {} : { format: this.format }), - ...(this.pattern ? {} : { pattern: this.pattern }), - }; - } -} +// outputFdrShape(): FdrStringShape { +// return { +// value: this.stringValue, +// ...(this.format ? {} : { format: this.format }), +// ...(this.pattern ? {} : { pattern: this.pattern }), +// }; +// } +// } diff --git a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts index ff48b63437..db66e05db9 100644 --- a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts +++ b/packages/parsers/openapi/shared/stages/fdr/api.stage.ts @@ -1,40 +1,72 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; -import { ApiNode, ApiNodeContext } from "../../interfaces/api.node.interface"; -import { FdrStage } from "../../interfaces/fdr.stage.interface"; +// import { FdrAPI } from "@fern-api/fdr-sdk"; +// import { OpenAPIV3_1 } from "openapi-types"; +// import { v4 } from "uuid"; +// import { ApiNode, ApiNodeContext } from "../../interfaces/api.node.interface"; +// import { FdrStage } from "../../interfaces/fdr.stage.interface"; +// import { EndpointNode, Method } from "../../nodes/endpoint.node"; -type ApiProcessingStageOutput = { - endpoints: FdrAPI.api.latest.ApiDefinition["endpoints"]; - websockets: FdrAPI.api.latest.ApiDefinition["websockets"]; - webhooks: FdrAPI.api.latest.ApiDefinition["webhooks"]; -}; +// type ApiProcessingStageOutput = { +// endpoints: FdrAPI.api.latest.ApiDefinition["endpoints"]; +// websockets: FdrAPI.api.latest.ApiDefinition["websockets"]; +// webhooks: FdrAPI.api.latest.ApiDefinition["webhooks"]; +// }; -export class FdrApiStage implements FdrStage { - stageName = "ApiProcessingStage"; - name = "ApiProcessingStage"; - qualifiedId: string; +// export class FdrApiStage implements FdrStage { +// stageName = "ApiProcessingStage"; +// name = "ApiProcessingStage"; +// qualifiedId: string; - endpoints: ApiProcessingStageOutput["endpoints"] = {}; - websockets: ApiProcessingStageOutput["websockets"] = {}; - webhooks: ApiProcessingStageOutput["webhooks"] = {}; +// endpoints: ApiProcessingStageOutput["endpoints"] = {}; +// websockets: ApiProcessingStageOutput["websockets"] = {}; +// webhooks: ApiProcessingStageOutput["webhooks"] = {}; - constructor( - readonly context: ApiNodeContext, - readonly preProcessedInput: OpenAPIV3_1.Document["paths"], - readonly accessPath: ApiNode[], - ) { - this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; - this.accessPath.push(this); +// private processEndpoint(method: Method, path: string, pathItem: OpenAPIV3_1.PathItemObject) { +// const id = v4(); +// this.endpoints[FdrAPI.EndpointId(id)] = new EndpointNode( +// id, +// method, +// path, +// this.context, +// pathItem, +// this.accessPath, +// ).outputFdrShape(); +// } - for (const [path, pathItem] of Object.entries(this.preProcessedInput)) { - } - } +// constructor( +// readonly context: ApiNodeContext, +// readonly preProcessedInput: OpenAPIV3_1.Document["paths"], +// readonly accessPath: ApiNode[], +// ) { +// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; +// this.accessPath.push(this); - outputFdrShape = (): ApiProcessingStageOutput => { - return { - endpoints: {}, - webhooks: {}, - websockets: {}, - }; - }; -} +// if (this.preProcessedInput != null) { +// for (const [path, pathItem] of Object.entries(this.preProcessedInput)) { +// // handle $ref +// if (pathItem != null) { +// if (pathItem.get != null) { +// this.processEndpoint("GET", path, pathItem.get); +// } +// if (pathItem.post != null) { +// this.processEndpoint("POST", path, pathItem.post); +// } +// if (pathItem.put != null) { +// this.processEndpoint("PUT", path, pathItem.put); +// } +// if (pathItem.delete != null) { +// this.processEndpoint("DELETE", path, pathItem.delete); +// } +// // Right now, we only support the 4 methods above, head, options, and trace... are not supported +// } +// } +// } +// } + +// outputFdrShape = (): ApiProcessingStageOutput => { +// return { +// endpoints: {}, +// webhooks: {}, +// websockets: {}, +// }; +// }; +// } From 3913b36edc03b0ad1e7b02f7cb92b3b1e71db7d6 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Thu, 14 Nov 2024 17:04:49 -0500 Subject: [PATCH 05/14] added zod --- packages/parsers/openapi/demo.ts | 34 +++++++++++++++++++++++--------- packages/parsers/package.json | 3 ++- pnpm-lock.yaml | 3 +++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/parsers/openapi/demo.ts b/packages/parsers/openapi/demo.ts index 2852bf0cd7..6902c2b7d3 100644 --- a/packages/parsers/openapi/demo.ts +++ b/packages/parsers/openapi/demo.ts @@ -1,5 +1,6 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; import { OpenAPIV3_1 } from "openapi-types"; +import { z } from "zod"; import { ApiNode, ApiNodeContext } from "./shared/interfaces/api.node.interface"; import { FdrStage } from "./shared/interfaces/fdr.stage.interface"; @@ -7,6 +8,21 @@ export class AvailabilityNode implements ApiNode node.id).join(".")}.AvailabilityNode`; if (input && typeof input === "object" && "x-fern-availability" in input) { - if (typeof input["x-fern-availability"] !== "string") { - context.errorCollector.addError(`Availability is not a string for ${this.id}`); - return; - } - this.availability = this.convertAvailability(input["x-fern-availability"]); - if (!this.availability) { + const result = this["x-fern-availability-shape"].safeParse(input); + if (result.success) { + this.availability = this.convertAvailability(result.data["x-fern-availability"]); + } else { context.errorCollector.addError(`Availability is not defined for ${this.id}`); } return; } if (input && typeof input === "object" && "deprecated" in input && typeof input.deprecated === "boolean") { - this.availability = input.deprecated ? FdrAPI.Availability.Deprecated : undefined; - if (!this.availability) { + const result = this.deprecatedShape.safeParse(input); + if (result.success) { + this.availability = result.data.deprecated ? FdrAPI.Availability.Deprecated : undefined; + } else { context.errorCollector.addError(`Availability is not defined for ${this.id}`); } return; @@ -44,7 +60,7 @@ export class AvailabilityNode implements ApiNode Date: Fri, 15 Nov 2024 12:55:23 -0500 Subject: [PATCH 06/14] add primitive and objects --- packages/parsers/__test__/demo.test.ts | 2 +- .../{openapi.node.ts => openApi3_1.node.ts} | 0 .../parsers/openapi/base.node.interface.ts | 49 ++++++++ packages/parsers/openapi/demo.ts | 37 +++--- .../shared/interfaces/api.node.interface.ts | 33 ------ .../interfaces/container.node.interface.ts | 5 - .../shared/interfaces/fdr.stage.interface.ts | 3 - .../interfaces/primitive.node.interface.ts | 3 - .../openapi/shared/nodes/object.node.ts | 40 +++++++ .../shared/nodes/objectProperty.node.ts | 47 ++++++++ .../openapi/shared/nodes/pathPart.node.ts | 29 ----- .../shared/nodes/primitives/enum.node.ts | 39 +++++++ .../shared/nodes/primitives/number.node.ts | 60 ++++++++++ .../nodes/primitives/number/float.node.ts | 59 ++++++++++ .../nodes/primitives/number/integer.node.ts | 58 ++++++++++ .../shared/nodes/primitives/string.node.ts | 105 ++++++++++++++++++ .../nodes/primitives/types/fdr.types.ts | 15 +++ .../nodes/primitives/types/format.types.ts | 48 ++++++++ .../openapi/shared/nodes/schema.node.ts | 39 +++++++ .../openapi/shared/nodes/string.node.ts | 43 ------- .../shared/nodes/typeReference.node.ts | 55 +++++++++ .../openapi/shared/nodes/typeShape.node.ts | 45 ++++++++ .../parsers/openapi/shared/openapi.types.ts | 3 + .../{nodes => temporary}/authScheme.node.ts | 0 .../availability.node.ts | 0 .../{nodes => temporary}/endpoint.node.ts | 0 .../shared/{nodes => temporary}/path.node.ts | 0 .../openapi/shared/temporary/pathPart.node.ts | 29 +++++ .../{ => temporary}/stages/fdr/api.stage.ts | 0 .../{nodes => temporary}/webhook.node.ts | 0 .../{nodes => temporary}/websocket.node.ts | 0 packages/parsers/package.json | 1 + pnpm-lock.yaml | 3 + 33 files changed, 712 insertions(+), 138 deletions(-) rename packages/parsers/openapi/3.1/{openapi.node.ts => openApi3_1.node.ts} (100%) create mode 100644 packages/parsers/openapi/base.node.interface.ts delete mode 100644 packages/parsers/openapi/shared/interfaces/api.node.interface.ts delete mode 100644 packages/parsers/openapi/shared/interfaces/container.node.interface.ts delete mode 100644 packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts delete mode 100644 packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts create mode 100644 packages/parsers/openapi/shared/nodes/object.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/objectProperty.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/pathPart.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/enum.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/number.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/string.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts create mode 100644 packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts create mode 100644 packages/parsers/openapi/shared/nodes/schema.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/string.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/typeReference.node.ts create mode 100644 packages/parsers/openapi/shared/nodes/typeShape.node.ts create mode 100644 packages/parsers/openapi/shared/openapi.types.ts rename packages/parsers/openapi/shared/{nodes => temporary}/authScheme.node.ts (100%) rename packages/parsers/openapi/shared/{composable => temporary}/availability.node.ts (100%) rename packages/parsers/openapi/shared/{nodes => temporary}/endpoint.node.ts (100%) rename packages/parsers/openapi/shared/{nodes => temporary}/path.node.ts (100%) create mode 100644 packages/parsers/openapi/shared/temporary/pathPart.node.ts rename packages/parsers/openapi/shared/{ => temporary}/stages/fdr/api.stage.ts (100%) rename packages/parsers/openapi/shared/{nodes => temporary}/webhook.node.ts (100%) rename packages/parsers/openapi/shared/{nodes => temporary}/websocket.node.ts (100%) diff --git a/packages/parsers/__test__/demo.test.ts b/packages/parsers/__test__/demo.test.ts index a9e49ae30e..8fea9bca9d 100644 --- a/packages/parsers/__test__/demo.test.ts +++ b/packages/parsers/__test__/demo.test.ts @@ -5,7 +5,7 @@ import { ComponentsNode } from "../openapi/demo"; import { FdrAPI } from "@fern-api/fdr-sdk"; import { OpenAPIV3_1 } from "openapi-types"; import { describe, it } from "vitest"; -import { ApiNodeContext, ErrorCollector } from "../openapi/shared/interfaces/api.node.interface"; +import { ApiNodeContext, ErrorCollector } from "../openapi/base.node.interface"; describe("ComponentsNode", () => { describe("outputFdrShape", () => { diff --git a/packages/parsers/openapi/3.1/openapi.node.ts b/packages/parsers/openapi/3.1/openApi3_1.node.ts similarity index 100% rename from packages/parsers/openapi/3.1/openapi.node.ts rename to packages/parsers/openapi/3.1/openApi3_1.node.ts diff --git a/packages/parsers/openapi/base.node.interface.ts b/packages/parsers/openapi/base.node.interface.ts new file mode 100644 index 0000000000..129b741a87 --- /dev/null +++ b/packages/parsers/openapi/base.node.interface.ts @@ -0,0 +1,49 @@ +import { Logger } from "@playwright/test"; + +export class ErrorCollector { + errors: { + message: string; + path: string; + }[] = []; + + addError(error: string, accessPath: string[], pathId?: string): void { + this.errors.push({ + message: error, + path: `#/${accessPath.join("/")}${pathId ? `/${pathId}` : ""}`, + }); + } +} + +export interface ApiNodeContext { + orgId: string; + apiId: string; + logger: Logger; + errorCollector: ErrorCollector; +} + +abstract class ApiNode { + context: ApiNodeContext; + input: InputShape; + + accessPath: string[]; + + constructor(context: ApiNodeContext, input: InputShape, accessPath: string[]) { + this.context = context; + this.input = input; + this.accessPath = accessPath; + } + + abstract outputFdrShape: () => FdrShape | undefined; +} + +export abstract class InputApiNode extends ApiNode { + constructor(context: ApiNodeContext, input: InputShape, accessPath: string[], pathId?: string) { + if (pathId) { + accessPath.push(pathId); + context.logger.log("a", "info", `Processing #/${accessPath.join("/")}/${pathId}`); + } + super(context, input, accessPath); + } +} + +export abstract class OutputApiNode extends ApiNode {} diff --git a/packages/parsers/openapi/demo.ts b/packages/parsers/openapi/demo.ts index 6902c2b7d3..04b9efa31a 100644 --- a/packages/parsers/openapi/demo.ts +++ b/packages/parsers/openapi/demo.ts @@ -1,22 +1,17 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; import { OpenAPIV3_1 } from "openapi-types"; import { z } from "zod"; -import { ApiNode, ApiNodeContext } from "./shared/interfaces/api.node.interface"; +import { ApiNodeContext, InputApiNode } from "./base.node.interface"; import { FdrStage } from "./shared/interfaces/fdr.stage.interface"; -export class AvailabilityNode implements ApiNode { +export class AvailabilityNode implements InputApiNode { id: string; availability: FdrAPI.Availability | undefined; "x-fern-availability-shape" = z.object({ - "x-fern-availability": z.enum([ - "beta", - "deprecated", - "development", - "pre-release", - "stable", - "generally-available", - ]), + "x-fern-availability": z.optional( + z.enum(["beta", "deprecated", "development", "pre-release", "stable", "generally-available"]), + ), }); deprecatedShape = z.object({ @@ -26,13 +21,13 @@ export class AvailabilityNode implements ApiNode[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.AvailabilityNode`; if (input && typeof input === "object" && "x-fern-availability" in input) { const result = this["x-fern-availability-shape"].safeParse(input); if (result.success) { - this.availability = this.convertAvailability(result.data["x-fern-availability"]); + this.availability = this.convertAvailability(result.data["x-fern-availability"] ?? ""); } else { context.errorCollector.addError(`Availability is not defined for ${this.id}`); } @@ -74,7 +69,7 @@ export class AvailabilityNode implements ApiNode { +export class DemoStringNode implements InputApiNode { id: string; regex: string | undefined; @@ -86,7 +81,7 @@ export class DemoStringNode implements ApiNode[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.DemoStringNode`; this.regex = input.pattern; @@ -115,7 +110,7 @@ export class DemoTypeShapeStage implements FdrStage[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeShapeStage`; @@ -151,7 +146,7 @@ export class DemoTypeShapeStage implements FdrStage + implements InputApiNode { id: string; @@ -162,7 +157,7 @@ export class DemoPropertyNode readonly name: string, readonly context: ApiNodeContext, readonly input: OpenAPIV3_1.SchemaObject, - readonly accessPath: ApiNode[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.DemoPropertyNode`; if (input.type === "string") { @@ -186,7 +181,7 @@ export class DemoPropertyNode }; } -export class DemoSchemaNode implements ApiNode { +export class DemoSchemaNode implements InputApiNode { id: string; shape: DemoTypeShapeStage | undefined = undefined; @@ -196,7 +191,7 @@ export class DemoSchemaNode implements ApiNode[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDefinitionNode`; if (this.input.type === "object") { @@ -220,7 +215,7 @@ export class DemoSchemaNode implements ApiNode { +export class ComponentsNode implements InputApiNode { id: string; schemas: DemoSchemaNode[] = []; @@ -228,7 +223,7 @@ export class ComponentsNode implements ApiNode[], + readonly accessPath: InputApiNode[], ) { this.id = `${accessPath.map((node) => node.id).join(".")}.ComponentsNode`; this.schemas = Object.entries(this.input.schemas ?? {}).map(([name, schema]) => { diff --git a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts b/packages/parsers/openapi/shared/interfaces/api.node.interface.ts deleted file mode 100644 index b257982f08..0000000000 --- a/packages/parsers/openapi/shared/interfaces/api.node.interface.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Logger } from "@playwright/test"; - -export class ErrorCollector { - errors: string[] = []; - - addError(error: string): void { - this.errors.push(error); - } -} - -export interface ApiNodeContext { - orgId: string; - apiId: string; - logger: Logger; - errorCollector: ErrorCollector; -} - -export interface ApiNode { - context: ApiNodeContext; - input: InputShape; - - accessPath: ApiNode[]; - id: string; - - outputFdrShape: () => FdrShape; -} - -export interface ComposableApiNode, FdrShape> - extends ApiNode { - inputNode: InputNode; - - outputFdrShape: () => T & FdrShape; -} diff --git a/packages/parsers/openapi/shared/interfaces/container.node.interface.ts b/packages/parsers/openapi/shared/interfaces/container.node.interface.ts deleted file mode 100644 index a58ddec4e8..0000000000 --- a/packages/parsers/openapi/shared/interfaces/container.node.interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ApiNode } from "./api.node.interface"; - -export interface ContainerNode extends ApiNode { - innerNodeInputShape: InnerNodeInputShape; -} diff --git a/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts b/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts deleted file mode 100644 index 1f54110787..0000000000 --- a/packages/parsers/openapi/shared/interfaces/fdr.stage.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ApiNode } from "./api.node.interface"; - -export interface FdrStage extends ApiNode {} diff --git a/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts b/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts deleted file mode 100644 index d661752822..0000000000 --- a/packages/parsers/openapi/shared/interfaces/primitive.node.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ApiNode } from "./api.node.interface"; - -export interface PrimitiveNode extends ApiNode {} diff --git a/packages/parsers/openapi/shared/nodes/object.node.ts b/packages/parsers/openapi/shared/nodes/object.node.ts new file mode 100644 index 0000000000..52a6ee2756 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/object.node.ts @@ -0,0 +1,40 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { SchemaObject } from "../openapi.types"; +import { ObjectPropertyNode } from "./objectProperty.node"; +import { TypeReferenceNode, isReferenceObject } from "./typeReference.node"; + +export class ObjectNode extends OutputApiNode { + extends: FdrAPI.TypeId[] = []; + properties: ObjectPropertyNode[] = []; + extraProperties: TypeReferenceNode | undefined; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath); + + if (input.allOf !== undefined) { + this.extends = input.allOf + .map((type) => (isReferenceObject(type) ? FdrAPI.TypeId(type.$ref) : undefined)) + .filter((id): id is FdrAPI.TypeId => id !== undefined); + } + + if (input.properties !== undefined) { + Object.entries(input.properties).forEach(([key, property]) => { + this.properties.push(new ObjectPropertyNode(key, context, property, accessPath, accessorKey)); + }); + } + } + + outputFdrShape = (): FdrAPI.api.latest.ObjectType | undefined => { + const properties = this.properties + .map((property) => property.outputFdrShape()) + .filter((property): property is FdrAPI.api.latest.ObjectProperty => property !== undefined); + + return { + extends: this.extends, + properties, + extraProperties: undefined, + // TODO: add extraProperties + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/objectProperty.node.ts b/packages/parsers/openapi/shared/nodes/objectProperty.node.ts new file mode 100644 index 0000000000..4cb326cf55 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/objectProperty.node.ts @@ -0,0 +1,47 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { ReferenceObject, SchemaObject } from "../openapi.types"; +import { isReferenceObject, mapReferenceObject } from "./typeReference.node"; +import { TypeShapeNode } from "./typeShape.node"; + +export class ObjectPropertyNode extends OutputApiNode< + SchemaObject | ReferenceObject, + FdrAPI.api.latest.ObjectProperty +> { + valueShape: TypeShapeNode; + description: string | undefined; + // availability: AvailabilityNode; + + constructor( + private readonly key: string, + context: ApiNodeContext, + input: SchemaObject | ReferenceObject, + accessPath: string[], + accessorKey?: string, + ) { + super(context, input, accessPath); + + if (isReferenceObject(input)) { + input = mapReferenceObject(input); + } + + this.valueShape = new TypeShapeNode(context, input, accessPath, accessorKey); + this.description = input.description; + // this.availability = input.availability; + } + + outputFdrShape = (): FdrAPI.api.latest.ObjectProperty | undefined => { + const valueShape = this.valueShape.outputFdrShape(); + if (valueShape === undefined) { + return undefined; + } + + return { + key: FdrAPI.PropertyKey(this.key), + valueShape, + description: this.description, + availability: undefined, + // TODO: update to availability: this.availability.outputFdrShape(), + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/pathPart.node.ts b/packages/parsers/openapi/shared/nodes/pathPart.node.ts deleted file mode 100644 index 04929767e0..0000000000 --- a/packages/parsers/openapi/shared/nodes/pathPart.node.ts +++ /dev/null @@ -1,29 +0,0 @@ -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; - -// import { FdrAPI } from "@fern-api/fdr-sdk"; - -// export class PathPartNode implements ApiNode { -// name = "pathPart"; -// qualifiedId: string; - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: string, -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// this.accessPath.push(this); -// } - -// outputFdrShape = (): FdrAPI.api.latest.PathPart => { -// return this.preProcessedInput.startsWith("{") && this.preProcessedInput.endsWith("}") -// ? { -// type: "pathParameter", -// value: FdrAPI.PropertyKey(this.preProcessedInput.slice(1, -1)), -// } -// : { -// type: "literal", -// value: this.preProcessedInput, -// }; -// }; -// } diff --git a/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts b/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts new file mode 100644 index 0000000000..f7862e850e --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts @@ -0,0 +1,39 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; + +export class EnumNode extends InputApiNode { + default: number | undefined; + format: string | undefined; + minimum: number | undefined; + maximum: number | undefined; + + constructor( + context: ApiNodeContext, + input: OpenAPIV3_1.NonArraySchemaObject, + accessPath: string[], + accessorKey?: string, + ) { + super(context, input, accessPath, accessorKey); + if (input.type !== "integer") { + context.errorCollector.addError( + `Expected type "integer" for primitive, but got "${input.type}"`, + accessPath, + accessorKey, + ); + } + this.format = input.format; + this.default = input.default; + this.minimum = input.minimum; + this.maximum = input.maximum; + } + + outputFdrShape = (): FdrAPI.api.v1.read.PrimitiveType.Integer => { + return { + type: "integer", + minimum: this.minimum, + maximum: this.maximum, + default: this.default, + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts new file mode 100644 index 0000000000..a4e64b1947 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts @@ -0,0 +1,60 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; +import { SchemaObject } from "../../openapi.types"; +import { FloatNode } from "./number/float.node"; +import { IntegerNode } from "./number/integer.node"; + +type FdrNumberType = + | FdrAPI.api.v1.read.PrimitiveType.Integer + | FdrAPI.api.v1.read.PrimitiveType.Long + | FdrAPI.api.v1.read.PrimitiveType.Double + | FdrAPI.api.v1.read.PrimitiveType.Uint + | FdrAPI.api.v1.read.PrimitiveType.Uint64; + +export class NumberNode extends InputApiNode { + typeNode: IntegerNode | FloatNode | undefined; + default: number | undefined; + minimum: number | undefined; + maximum: number | undefined; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath); + + if (input.type !== "integer" && input.type !== "number") { + context.errorCollector.addError( + `Expected type "integer" or "number" for numerical primitive, but got "${input.type}"`, + accessPath, + accessorKey, + ); + return; + } + + switch (input.type) { + case "integer": + this.typeNode = new IntegerNode(context, input, accessPath, accessorKey); + break; + case "number": + this.typeNode = new FloatNode(context, input, accessPath, accessorKey); + break; + } + + this.default = input.default; + this.minimum = input.minimum; + this.maximum = input.maximum; + } + + outputFdrShape = (): FdrNumberType | undefined => { + const typeProperties = this.typeNode?.outputFdrShape(); + + if (typeProperties === undefined) { + return undefined; + } + + return { + ...typeProperties, + minimum: this.minimum, + maximum: this.maximum, + default: this.default, + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts new file mode 100644 index 0000000000..d0ab31a841 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts @@ -0,0 +1,59 @@ +import { UnreachableCaseError } from "ts-essentials"; +import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; +import { SchemaObject } from "../../../openapi.types"; +import { FdrFloatType } from "../types/fdr.types"; +import { OpenApiNumberTypeFormat } from "../types/format.types"; + +function isOpenApiNumberTypeFormat(format: string | undefined): format is OpenApiNumberTypeFormat { + return format === "float" || format === "double" || format === undefined; +} + +export class FloatNode extends OutputApiNode> { + type: FdrFloatType["type"] | undefined = undefined; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath); + + if (input.type !== "number") { + context.errorCollector.addError( + `Expected type "number" for numerical primitive, but got "${input.type}"`, + accessPath, + accessorKey, + ); + return; + } + + if (!isOpenApiNumberTypeFormat(input.format)) { + context.errorCollector.addError( + `Expected format for number primitive, but got "${input.format}"`, + accessPath, + accessorKey, + ); + return; + } + + switch (input.format) { + case "decimal": + case "decimal128": + case "double-int": + case "double": + case "float": + case "sf-decimal": + case undefined: + this.type = "double"; + break; + default: + new UnreachableCaseError(input.format); + } + } + + // In this, we pick only the non-shared types, so that we can push up the shared types to the parent for maximum reuse + outputFdrShape = (): Pick | undefined => { + if (this.type === undefined) { + return undefined; + } + return { + type: this.type, + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts new file mode 100644 index 0000000000..f6aec50bfd --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts @@ -0,0 +1,58 @@ +import { UnreachableCaseError } from "ts-essentials"; +import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; +import { SchemaObject } from "../../../openapi.types"; +import { FdrIntegerType } from "../types/fdr.types"; +import { OpenApiIntegerTypeFormat } from "../types/format.types"; + +function isOpenApiIntegerTypeFormat(format: string | undefined): format is OpenApiIntegerTypeFormat { + return format === "int32" || format === "int64" || format === undefined; +} + +export class IntegerNode extends OutputApiNode> { + type: FdrIntegerType["type"] = "integer"; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath); + + if (input.type !== "integer") { + context.errorCollector.addError( + `Expected type "integer" for numerical primitive, but got "${input.type}"`, + accessPath, + accessorKey, + ); + return; + } + + if (!isOpenApiIntegerTypeFormat(input.format)) { + context.errorCollector.addError( + `Expected format for integer primitive, but got "${input.format}"`, + accessPath, + accessorKey, + ); + return; + } + + switch (input.format) { + case "int64": + this.type = "long"; + break; + case "int8": + case "int16": + case "int32": + case "uint8": + case "sf-integer": + case undefined: + this.type = "integer"; + break; + default: + new UnreachableCaseError(input.format); + } + } + + // In this, we pick only the non-shared types, so that we can push up the shared types to the parent for maximum reuse + outputFdrShape = (): Pick => { + return { + type: this.type, + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts new file mode 100644 index 0000000000..36fba0a4b4 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts @@ -0,0 +1,105 @@ +import { UnreachableCaseError } from "ts-essentials"; +import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; +import { SchemaObject } from "../../openapi.types"; +import { FdrStringType } from "./types/fdr.types"; +import { OpenApiStringTypeFormat } from "./types/format.types"; + +export class StringNode extends InputApiNode { + type: FdrStringType["type"] | undefined; + regex: string | undefined; + default: string | undefined; + minLength: number | undefined; + maxLength: number | undefined; + + mapToFdrType = (format: OpenApiStringTypeFormat): FdrStringType["type"] | undefined => { + switch (format) { + case "base64url": + case "binary": + case "byte": + case "sf-binary": + return "base64"; + case "date-time": + return "datetime"; + case "int64": + return "bigInteger"; + case "date": + return "date"; + case "uuid": + return "uuid"; + case "char": + case "commonmark": + case "decimal": + case "decimal128": + case "duration": + case "email": + case "hostname": + case "html": + case "http-date": + case "idn-email": + case "idn-hostname": + case "ipv4": + case "ipv6": + case "iri-reference": + case "iri": + case "json-pointer": + case "media-range": + case "password": + case "regex": + case "relative-json-pointer": + case "sf-boolean": + case "sf-string": + case "sf-token": + case "time": + case "uri-reference": + case "uri-template": + case "uri": + case undefined: + return "string"; + default: + new UnreachableCaseError(format); + return undefined; + } + }; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath, accessorKey); + if (input.type !== "string") { + context.errorCollector.addError( + `Expected type "string" for primitive, but got "${input.type}"`, + accessPath, + accessorKey, + ); + return; + } + + this.type = this.mapToFdrType(input.format); + + if (this.type === undefined) { + context.errorCollector.addError( + `Expected proper "string" format, but got "${input.format}"`, + accessPath, + accessorKey, + ); + return; + } + + this.regex = input.pattern; + this.default = input.default; + this.minLength = input.minLength; + this.maxLength = input.maxLength; + } + + outputFdrShape = (): FdrStringType | undefined => { + if (this.type === undefined) { + return undefined; + } + + return { + type: this.type, + regex: this.regex, + minLength: this.minLength, + maxLength: this.maxLength, + default: this.default, + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts b/packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts new file mode 100644 index 0000000000..3b40386d43 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts @@ -0,0 +1,15 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; + +export type FdrFloatType = FdrAPI.api.v1.read.PrimitiveType.Double; +export type FdrIntegerType = + | FdrAPI.api.v1.read.PrimitiveType.Integer + | FdrAPI.api.v1.read.PrimitiveType.Long + | FdrAPI.api.v1.read.PrimitiveType.Uint + | FdrAPI.api.v1.read.PrimitiveType.Uint64; +export type FdrStringType = + | FdrAPI.api.v1.read.PrimitiveType.String + | FdrAPI.api.v1.read.PrimitiveType.BigInteger + | FdrAPI.api.v1.read.PrimitiveType.Datetime + | FdrAPI.api.v1.read.PrimitiveType.Uuid + | FdrAPI.api.v1.read.PrimitiveType.Base64 + | FdrAPI.api.v1.read.PrimitiveType.Date_; diff --git a/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts b/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts new file mode 100644 index 0000000000..b8872f7de1 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts @@ -0,0 +1,48 @@ +// Copied from https://spec.openapis.org/registry/format/ + +export type OpenApiNumberTypeFormat = + | "decimal" + | "decimal128" + | "double-int" + | "double" + | "float" + | "sf-decimal" + | undefined; +export type OpenApiIntegerTypeFormat = "int16" | "int32" | "int64" | "int8" | "sf-integer" | "uint8" | undefined; +export type OpenApiStringTypeFormat = + | "base64url" + | "binary" + | "byte" + | "char" + | "commonmark" + | "date-time" + | "date" + | "decimal" + | "decimal128" + | "duration" + | "email" + | "hostname" + | "html" + | "http-date" + | "idn-email" + | "idn-hostname" + | "int64" + | "ipv4" + | "ipv6" + | "iri-reference" + | "iri" + | "json-pointer" + | "media-range" + | "password" + | "regex" + | "relative-json-pointer" + | "sf-binary" + | "sf-boolean" + | "sf-string" + | "sf-token" + | "time" + | "uri-reference" + | "uri-template" + | "uri" + | "uuid" + | undefined; diff --git a/packages/parsers/openapi/shared/nodes/schema.node.ts b/packages/parsers/openapi/shared/nodes/schema.node.ts new file mode 100644 index 0000000000..d272539da6 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/schema.node.ts @@ -0,0 +1,39 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { ApiNodeContext, InputApiNode } from "../../base.node.interface"; +import { SchemaObject } from "../openapi.types"; +import { TypeShapeNode } from "./typeShape.node"; + +export class SchemaNode extends InputApiNode { + shape: TypeShapeNode; + description: string | undefined; + // availability: AvailabilityNode; + + constructor( + private readonly name: string, + context: ApiNodeContext, + input: OpenAPIV3_1.SchemaObject, + accessPath: string[], + accessorKey?: string, + ) { + super(context, input, accessPath, accessorKey); + + this.shape = new TypeShapeNode(context, input, accessPath, accessorKey); + this.description = input.description; + } + + outputFdrShape = (): FdrAPI.api.latest.TypeDefinition | undefined => { + const typeShape = this.shape.outputFdrShape(); + if (typeShape === undefined) { + return undefined; + } + + return { + name: this.name, + shape: typeShape, + description: this.description, + availability: undefined, + // TODO: update to availability: this.availability.outputFdrShape(), + }; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/string.node.ts b/packages/parsers/openapi/shared/nodes/string.node.ts deleted file mode 100644 index dc42a4b12b..0000000000 --- a/packages/parsers/openapi/shared/nodes/string.node.ts +++ /dev/null @@ -1,43 +0,0 @@ -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; -// import { PrimitiveNode } from "../interfaces/primitive.node.interface"; - -// type FdrStringShape = { -// value: string; -// format?: "uuid" | "email"; -// pattern?: RegExp; -// }; - -// type RedocStringNode = { -// stringValue: string; -// format?: "uuid" | "email"; -// pattern?: RegExp; -// }; - -// export class StringNode implements PrimitiveNode { -// name = "String"; -// qualifiedId: string; - -// private readonly stringValue: string; -// private readonly format?: "uuid" | "email"; -// private readonly pattern?: RegExp; - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: RedocStringNode, -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = accessPath.map((node) => node.qualifiedId).join("."); - -// this.stringValue = preProcessedInput.stringValue; -// this.format = preProcessedInput.format; -// this.pattern = preProcessedInput.pattern; -// } - -// outputFdrShape(): FdrStringShape { -// return { -// value: this.stringValue, -// ...(this.format ? {} : { format: this.format }), -// ...(this.pattern ? {} : { pattern: this.pattern }), -// }; -// } -// } diff --git a/packages/parsers/openapi/shared/nodes/typeReference.node.ts b/packages/parsers/openapi/shared/nodes/typeReference.node.ts new file mode 100644 index 0000000000..dbeb9aeb87 --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/typeReference.node.ts @@ -0,0 +1,55 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { ReferenceObject, SchemaObject } from "../openapi.types"; +import { NumberNode } from "./primitives/number.node"; +import { StringNode } from "./primitives/string.node"; + +export const isReferenceObject = (input: unknown): input is ReferenceObject => { + return typeof input === "object" && input != null && "$ref" in input && typeof input.$ref === "string"; +}; +export const mapReferenceObject = (referenceObject: ReferenceObject): SchemaObject => { + return referenceObject.$ref as SchemaObject; +}; + +// might want to split this out into AliasNode, PrimitiveNode, etc. +export class TypeReferenceNode extends OutputApiNode { + type: FdrAPI.api.latest.TypeReference["type"] | undefined; + typeNode: StringNode | NumberNode | undefined; + ref: string | undefined; + default: FdrAPI.api.latest.TypeReferenceIdDefault | undefined; + + constructor(context: ApiNodeContext, input: SchemaObject | ReferenceObject, accessPath: string[]) { + super(context, input, accessPath); + + if (isReferenceObject(input)) { + this.type = "id"; + this.ref = input.$ref; + this.default = undefined; + } else { + this.type = "primitive"; + this.typeNode = new NumberNode(context, input, accessPath); + } + // just support primitives and ids for now + } + + outputFdrShape = (): FdrAPI.api.latest.TypeReference | undefined => { + const primitiveShape = this.typeNode?.outputFdrShape(); + if (primitiveShape === undefined || this.ref === undefined) { + return undefined; + } + if (this.type === "id") { + return { + type: this.type, + id: FdrAPI.TypeId(this.ref), + default: this.default, + }; + } + if (this.type === "primitive") { + return { + type: this.type, + value: primitiveShape, + }; + } + return undefined; + }; +} diff --git a/packages/parsers/openapi/shared/nodes/typeShape.node.ts b/packages/parsers/openapi/shared/nodes/typeShape.node.ts new file mode 100644 index 0000000000..45ac300e6c --- /dev/null +++ b/packages/parsers/openapi/shared/nodes/typeShape.node.ts @@ -0,0 +1,45 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { SchemaObject } from "../openapi.types"; +import { ObjectNode } from "./object.node"; +import { TypeReferenceNode } from "./typeReference.node"; + +export class TypeShapeNode extends OutputApiNode { + // For now, we will just support Object nodes, in the future, this will need to be updated to an exhaustive switch + type: FdrAPI.api.latest.TypeShape["type"] | undefined; + typeNode: ObjectNode | TypeReferenceNode | undefined; + + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + super(context, input, accessPath); + + // For now, we will just support Object and alias nodes, in the future, this will need to be updated to an exhaustive switch + if (input.type === "alias") { + this.type = "alias"; + this.typeNode = new TypeReferenceNode(context, input, accessPath); + } else if (input.type === "object") { + this.type = "object"; + this.typeNode = new ObjectNode(context, input, accessPath, accessorKey); + } + } + + outputFdrShape = (): FdrAPI.api.latest.TypeShape | undefined => { + const typeShape = this.typeNode?.outputFdrShape(); + if (typeShape === undefined || this.type === undefined) { + return undefined; + } + switch (this.type) { + case "object": + return { + type: "object", + ...(typeShape as FdrAPI.api.latest.ObjectType), + }; + case "alias": + return { + type: this.type, + value: typeShape as FdrAPI.api.latest.TypeReference, + }; + default: + return undefined; + } + }; +} diff --git a/packages/parsers/openapi/shared/openapi.types.ts b/packages/parsers/openapi/shared/openapi.types.ts new file mode 100644 index 0000000000..a7bd020749 --- /dev/null +++ b/packages/parsers/openapi/shared/openapi.types.ts @@ -0,0 +1,3 @@ +import { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from "openapi-types"; +export type SchemaObject = OpenAPIV3_1.SchemaObject | OpenAPIV3.SchemaObject | OpenAPIV2.SchemaObject; +export type ReferenceObject = OpenAPIV3_1.ReferenceObject | OpenAPIV3.ReferenceObject | OpenAPIV2.ReferenceObject; diff --git a/packages/parsers/openapi/shared/nodes/authScheme.node.ts b/packages/parsers/openapi/shared/temporary/authScheme.node.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/authScheme.node.ts rename to packages/parsers/openapi/shared/temporary/authScheme.node.ts diff --git a/packages/parsers/openapi/shared/composable/availability.node.ts b/packages/parsers/openapi/shared/temporary/availability.node.ts similarity index 100% rename from packages/parsers/openapi/shared/composable/availability.node.ts rename to packages/parsers/openapi/shared/temporary/availability.node.ts diff --git a/packages/parsers/openapi/shared/nodes/endpoint.node.ts b/packages/parsers/openapi/shared/temporary/endpoint.node.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/endpoint.node.ts rename to packages/parsers/openapi/shared/temporary/endpoint.node.ts diff --git a/packages/parsers/openapi/shared/nodes/path.node.ts b/packages/parsers/openapi/shared/temporary/path.node.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/path.node.ts rename to packages/parsers/openapi/shared/temporary/path.node.ts diff --git a/packages/parsers/openapi/shared/temporary/pathPart.node.ts b/packages/parsers/openapi/shared/temporary/pathPart.node.ts new file mode 100644 index 0000000000..4a0d34b70d --- /dev/null +++ b/packages/parsers/openapi/shared/temporary/pathPart.node.ts @@ -0,0 +1,29 @@ +import { ApiNode, ApiNodeContext } from "../../base.node.interface"; + +import { FdrAPI } from "@fern-api/fdr-sdk"; + +export class PathPartNode implements ApiNode { + name = "pathPart"; + qualifiedId: string; + + constructor( + readonly context: ApiNodeContext, + readonly preProcessedInput: string, + readonly accessPath: ApiNode[], + ) { + this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; + this.accessPath.push(this); + } + + outputFdrShape = (): FdrAPI.api.latest.PathPart => { + return this.preProcessedInput.startsWith("{") && this.preProcessedInput.endsWith("}") + ? { + type: "pathParameter", + value: FdrAPI.PropertyKey(this.preProcessedInput.slice(1, -1)), + } + : { + type: "literal", + value: this.preProcessedInput, + }; + }; +} diff --git a/packages/parsers/openapi/shared/stages/fdr/api.stage.ts b/packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts similarity index 100% rename from packages/parsers/openapi/shared/stages/fdr/api.stage.ts rename to packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts diff --git a/packages/parsers/openapi/shared/nodes/webhook.node.ts b/packages/parsers/openapi/shared/temporary/webhook.node.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/webhook.node.ts rename to packages/parsers/openapi/shared/temporary/webhook.node.ts diff --git a/packages/parsers/openapi/shared/nodes/websocket.node.ts b/packages/parsers/openapi/shared/temporary/websocket.node.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/websocket.node.ts rename to packages/parsers/openapi/shared/temporary/websocket.node.ts diff --git a/packages/parsers/package.json b/packages/parsers/package.json index eabb37b625..75b6274c73 100644 --- a/packages/parsers/package.json +++ b/packages/parsers/package.json @@ -26,6 +26,7 @@ "dependencies": { "@fern-api/fdr-sdk": "workspace:*", "openapi-types": "^12.1.3", + "ts-essentials": "^10.0.1", "uuid": "^9.0.0", "zod": "^3.23.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04c3716321..05f7a98a62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -973,6 +973,9 @@ importers: openapi-types: specifier: ^12.1.3 version: 12.1.3 + ts-essentials: + specifier: ^10.0.1 + version: 10.0.1(typescript@5.4.3) uuid: specifier: ^9.0.0 version: 9.0.1 From a4304337d4a16301a86c36a40d95518f4e6b049b Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Fri, 15 Nov 2024 13:10:27 -0500 Subject: [PATCH 07/14] depcheck fixes --- .../parsers/openapi/shared/nodes/typeShape.node.ts | 1 + packages/parsers/package.json | 6 ++---- pnpm-lock.yaml | 13 ++++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/parsers/openapi/shared/nodes/typeShape.node.ts b/packages/parsers/openapi/shared/nodes/typeShape.node.ts index 45ac300e6c..a1317f8174 100644 --- a/packages/parsers/openapi/shared/nodes/typeShape.node.ts +++ b/packages/parsers/openapi/shared/nodes/typeShape.node.ts @@ -31,6 +31,7 @@ export class TypeShapeNode extends OutputApiNode Date: Fri, 15 Nov 2024 13:13:41 -0500 Subject: [PATCH 08/14] removed temporary files --- packages/parsers/.gitignore | 1 + packages/parsers/__test__/demo.test.ts | 148 ------- packages/parsers/openapi/demo.ts | 237 ---------- .../shared/temporary/authScheme.node.ts | 24 - .../shared/temporary/availability.node.ts | 419 ------------------ .../openapi/shared/temporary/endpoint.node.ts | 37 -- .../openapi/shared/temporary/path.node.ts | 24 - .../openapi/shared/temporary/pathPart.node.ts | 29 -- .../shared/temporary/stages/fdr/api.stage.ts | 72 --- .../openapi/shared/temporary/webhook.node.ts | 0 .../shared/temporary/websocket.node.ts | 0 11 files changed, 1 insertion(+), 990 deletions(-) create mode 100644 packages/parsers/.gitignore delete mode 100644 packages/parsers/__test__/demo.test.ts delete mode 100644 packages/parsers/openapi/demo.ts delete mode 100644 packages/parsers/openapi/shared/temporary/authScheme.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/availability.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/endpoint.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/path.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/pathPart.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts delete mode 100644 packages/parsers/openapi/shared/temporary/webhook.node.ts delete mode 100644 packages/parsers/openapi/shared/temporary/websocket.node.ts diff --git a/packages/parsers/.gitignore b/packages/parsers/.gitignore new file mode 100644 index 0000000000..64cc792cdf --- /dev/null +++ b/packages/parsers/.gitignore @@ -0,0 +1 @@ +openapi/shared/temporary \ No newline at end of file diff --git a/packages/parsers/__test__/demo.test.ts b/packages/parsers/__test__/demo.test.ts deleted file mode 100644 index 8fea9bca9d..0000000000 --- a/packages/parsers/__test__/demo.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { expect } from "vitest"; - -import { ComponentsNode } from "../openapi/demo"; - -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; -import { describe, it } from "vitest"; -import { ApiNodeContext, ErrorCollector } from "../openapi/base.node.interface"; - -describe("ComponentsNode", () => { - describe("outputFdrShape", () => { - it("should convert OpenAPI components to FDR type definitions", () => { - const context: ApiNodeContext = { - orgId: "test-org", - apiId: "test-api", - // @ts-expect-error logger is not required - logger: console, - errorCollector: new ErrorCollector(), - }; - - const input: OpenAPIV3_1.ComponentsObject = { - schemas: { - Person: { - // @ts-expect-error availability is not an out of the box OpenApi Property - "x-fern-availability": "generally-available", - type: "object", - properties: { - name: { - type: "string", - }, - }, - }, - }, - }; - - const componentsNode = new ComponentsNode(context, input, []); - const result = componentsNode.outputFdrShape(); - - expect(result).toHaveLength(1); - - const personType = result[0]; - expect(personType?.name).toBe("Person"); - expect(personType?.availability).toBe(FdrAPI.Availability.GenerallyAvailable); - expect(context.errorCollector.errors).toHaveLength(0); - }); - - it("should handle invalid availability value", () => { - const context: ApiNodeContext = { - orgId: "test-org", - apiId: "test-api", - // @ts-expect-error logger is not required - logger: console, - errorCollector: new ErrorCollector(), - }; - - const input: OpenAPIV3_1.ComponentsObject = { - schemas: { - Person: { - // @ts-expect-error availability is not an out of the box OpenApi Property - "x-fern-availability": "deep", - type: "object", - properties: { - name: { - type: "string", - }, - }, - }, - }, - }; - - const componentsNode = new ComponentsNode(context, input, []); - const result = componentsNode.outputFdrShape(); - - expect(result).toHaveLength(1); - const personType = result[0]; - expect(personType?.name).toBe("Person"); - expect(personType?.availability).toBeUndefined(); - expect(context.errorCollector.errors).toHaveLength(1); - }); - - it("should handle non-string availability value", () => { - const context: ApiNodeContext = { - orgId: "test-org", - apiId: "test-api", - // @ts-expect-error logger is not required - logger: console, - errorCollector: new ErrorCollector(), - }; - - const input: OpenAPIV3_1.ComponentsObject = { - schemas: { - Person: { - // @ts-expect-error availability is not an out of the box OpenApi Property - "x-fern-availability": true, - type: "object", - properties: { - name: { - type: "string", - }, - }, - }, - }, - }; - - const componentsNode = new ComponentsNode(context, input, []); - const result = componentsNode.outputFdrShape(); - - expect(result).toHaveLength(1); - const personType = result[0]; - expect(personType?.name).toBe("Person"); - expect(personType?.availability).toBeUndefined(); - expect(context.errorCollector.errors).toHaveLength(1); - }); - - it("should handle deprecated flag", () => { - const context: ApiNodeContext = { - orgId: "test-org", - apiId: "test-api", - // @ts-expect-error logger is not required - logger: console, - errorCollector: new ErrorCollector(), - }; - - const input: OpenAPIV3_1.ComponentsObject = { - schemas: { - Person: { - deprecated: true, - type: "object", - properties: { - name: { - type: "string", - }, - }, - }, - }, - }; - - const componentsNode = new ComponentsNode(context, input, []); - const result = componentsNode.outputFdrShape(); - - expect(result).toHaveLength(1); - const personType = result[0]; - expect(personType?.name).toBe("Person"); - expect(personType?.availability).toBe("Deprecated"); - expect(context.errorCollector.errors).toHaveLength(0); - }); - }); -}); diff --git a/packages/parsers/openapi/demo.ts b/packages/parsers/openapi/demo.ts deleted file mode 100644 index 04b9efa31a..0000000000 --- a/packages/parsers/openapi/demo.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; -import { z } from "zod"; -import { ApiNodeContext, InputApiNode } from "./base.node.interface"; -import { FdrStage } from "./shared/interfaces/fdr.stage.interface"; - -export class AvailabilityNode implements InputApiNode { - id: string; - availability: FdrAPI.Availability | undefined; - - "x-fern-availability-shape" = z.object({ - "x-fern-availability": z.optional( - z.enum(["beta", "deprecated", "development", "pre-release", "stable", "generally-available"]), - ), - }); - - deprecatedShape = z.object({ - deprecated: z.boolean(), - }); - - constructor( - readonly context: ApiNodeContext, - readonly input: unknown, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.AvailabilityNode`; - if (input && typeof input === "object" && "x-fern-availability" in input) { - const result = this["x-fern-availability-shape"].safeParse(input); - if (result.success) { - this.availability = this.convertAvailability(result.data["x-fern-availability"] ?? ""); - } else { - context.errorCollector.addError(`Availability is not defined for ${this.id}`); - } - return; - } - - if (input && typeof input === "object" && "deprecated" in input && typeof input.deprecated === "boolean") { - const result = this.deprecatedShape.safeParse(input); - if (result.success) { - this.availability = result.data.deprecated ? FdrAPI.Availability.Deprecated : undefined; - } else { - context.errorCollector.addError(`Availability is not defined for ${this.id}`); - } - return; - } - } - - convertAvailability = (availability: string): FdrAPI.Availability | undefined => { - switch (availability) { - case "beta": - return FdrAPI.Availability.Beta; - case "deprecated": - return FdrAPI.Availability.Deprecated; - case "development": - return FdrAPI.Availability.InDevelopment; - case "pre-release": - return FdrAPI.Availability.PreRelease; - case "stable": - return FdrAPI.Availability.Stable; - case "generally-available": - return FdrAPI.Availability.GenerallyAvailable; - default: - return undefined; - } - }; - - outputFdrShape = (): FdrAPI.Availability | undefined => { - return this.availability; - }; -} - -export class DemoStringNode implements InputApiNode { - id: string; - - regex: string | undefined; - format: string | undefined; - default: string | undefined; - minLength: number | undefined; - maxLength: number | undefined; - - constructor( - readonly context: ApiNodeContext, - readonly input: OpenAPIV3_1.SchemaObject, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.DemoStringNode`; - this.regex = input.pattern; - // this.format = input.format; - this.default = input.default; - this.minLength = input.minLength; - this.maxLength = input.maxLength; - } - - outputFdrShape = (): FdrAPI.api.latest.PrimitiveType => { - return { - type: "string", - regex: this.regex, - minLength: this.minLength, - maxLength: this.maxLength, - default: this.default, - }; - }; -} - -export class DemoTypeShapeStage implements FdrStage { - id: string; - - stringNode: DemoStringNode | undefined; - properties: DemoPropertyNode[] = []; - constructor( - readonly context: ApiNodeContext, - readonly input: OpenAPIV3_1.SchemaObject, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeShapeStage`; - - // This should be decomposed into container and primitive nodes, but this is a bit too much for now - if (input.type === "object") { - this.properties = Object.entries(input.properties ?? {}).map(([name, schema]) => { - return new DemoPropertyNode(name, this.context, schema, this.accessPath); - }); - } else if (input.type === "string") { - this.stringNode = new DemoStringNode(this.context, this.input, this.accessPath); - } - } - - outputFdrShape = (): FdrAPI.api.latest.TypeShape => { - // we include this because this is non-exhaustive for now, for demo purposes - if (this.stringNode) { - return { - type: "alias", - value: { - type: "primitive", - value: this.stringNode.outputFdrShape(), - }, - }; - } else { - return { - type: "object", - properties: this.properties.map((property) => property.outputFdrShape()), - extends: [], - extraProperties: undefined, - }; - } - }; -} - -export class DemoPropertyNode - implements InputApiNode -{ - id: string; - - propertyShape: DemoTypeShapeStage | undefined = undefined; - availability: AvailabilityNode | undefined = undefined; - - constructor( - readonly name: string, - readonly context: ApiNodeContext, - readonly input: OpenAPIV3_1.SchemaObject, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.DemoPropertyNode`; - if (input.type === "string") { - this.propertyShape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); - } - this.availability = new AvailabilityNode(this.context, this.input, this.accessPath); - } - - outputFdrShape = (): FdrAPI.api.latest.ObjectProperty => { - // we include this because this is non-exhaustive for now, for demo purposes - if (!this.propertyShape) { - throw new Error("Property shape is undefined"); - } - - return { - key: FdrAPI.PropertyKey(this.name), - valueShape: this.propertyShape.outputFdrShape(), - description: this.input.description, - availability: this.availability?.outputFdrShape(), - }; - }; -} - -export class DemoSchemaNode implements InputApiNode { - id: string; - - shape: DemoTypeShapeStage | undefined = undefined; - availability: AvailabilityNode; - - constructor( - readonly name: string, - readonly context: ApiNodeContext, - readonly input: OpenAPIV3_1.SchemaObject, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDefinitionNode`; - if (this.input.type === "object") { - this.shape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); - } - this.availability = new AvailabilityNode(this.context, this.input, this.accessPath); - } - - outputFdrShape = (): FdrAPI.api.latest.TypeDefinition => { - // we include this because this is non-exhaustive for now, for demo purposes - if (!this.shape) { - throw new Error("Shape is undefined"); - } - - return { - name: this.name, - shape: this.shape.outputFdrShape(), - description: this.input.description, - availability: this.availability.outputFdrShape(), - }; - }; -} - -export class ComponentsNode implements InputApiNode { - id: string; - - schemas: DemoSchemaNode[] = []; - - constructor( - readonly context: ApiNodeContext, - readonly input: OpenAPIV3_1.ComponentsObject, - readonly accessPath: InputApiNode[], - ) { - this.id = `${accessPath.map((node) => node.id).join(".")}.ComponentsNode`; - this.schemas = Object.entries(this.input.schemas ?? {}).map(([name, schema]) => { - return new DemoSchemaNode(name, this.context, schema, this.accessPath); - }); - } - - outputFdrShape = (): FdrAPI.api.latest.TypeDefinition[] => { - return this.schemas.map((schema) => schema.outputFdrShape()); - }; -} diff --git a/packages/parsers/openapi/shared/temporary/authScheme.node.ts b/packages/parsers/openapi/shared/temporary/authScheme.node.ts deleted file mode 100644 index ab37d93c50..0000000000 --- a/packages/parsers/openapi/shared/temporary/authScheme.node.ts +++ /dev/null @@ -1,24 +0,0 @@ -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { OpenAPIV3, OpenAPIV3_1 } from "openapi-types"; -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; - -// export class AuthSchemeIdsNode -// implements ApiNode -// { -// name = "authSchemeIds"; - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: OpenAPIV3_1.SecurityRequirementObject[], -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${this.accessPath.map((node) => node.id).join(".")}.AuthSchemeIdsNode`; -// this.accessPath.push(this); -// } -// input: OpenAPIV3.SecurityRequirementObject[]; -// id: string; - -// outputFdrShape = (): FdrAPI.api.latest.AuthSchemeId[] => { -// return this.preProcessedInput.map((authScheme) => FdrAPI.AuthSchemeId(authScheme[0])); -// }; -// } diff --git a/packages/parsers/openapi/shared/temporary/availability.node.ts b/packages/parsers/openapi/shared/temporary/availability.node.ts deleted file mode 100644 index 59ed03e726..0000000000 --- a/packages/parsers/openapi/shared/temporary/availability.node.ts +++ /dev/null @@ -1,419 +0,0 @@ -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { FernRegistry } from "@fern-api/fdr-sdk/src/client/generated"; -// import { OpenAPIV3_1 } from "openapi-types"; -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; -// import { FdrStage } from "../interfaces/fdr.stage.interface"; -// import { FdrApiStage } from "../stages/fdr/api.stage"; - -// // export class AvailabilityNode & { "x-fern-availability": string }> -// // implements -// // ComposableApiNode< -// // T, -// // InputNode, -// // T & { -// // availability: string; -// // } -// // > -// // { -// // name = "availability"; -// // preProcessedInput: InputNode["preProcessedInput"]; -// // qualifiedId: string; - -// // constructor( -// // readonly context: ApiNodeContext, -// // readonly inputNode: InputNode, -// // readonly accessPath: ApiNode[], -// // ) { -// // this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// // this.preProcessedInput = this.inputNode.preProcessedInput; -// // } - -// // outputFdrShape(): T & { -// // availability: string; -// // } { -// // const baseShape = this.inputNode.outputFdrShape(); -// // return { -// // ...baseShape, -// // availability: this.inputNode["x-fern-availability"], -// // }; -// // } -// // } - -// export class TypeNode implements ApiNode { -// name = "type"; -// qualifiedId: string; - -// reference?: string; - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = `${accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// this.reference = "$ref" in preProcessedInput ? preProcessedInput.$ref : undefined; -// } -// input: OpenAPIV3_1.SchemaObject; -// id: string; -// parse: () => -// | { ok: true } -// | { -// ok: false; -// // implements -// // ComposableApiNode< -// // T, -// // InputNode, -// // T & { -// // availability: string; -// // } -// // > -// // { -// // name = "availability"; -// // preProcessedInput: InputNode["preProcessedInput"]; -// // qualifiedId: string; -// // constructor( -// // readonly context: ApiNodeContext, -// // readonly inputNode: InputNode, -// // readonly accessPath: ApiNode[], -// // ) { -// // this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// // this.preProcessedInput = this.inputNode.preProcessedInput; -// // } -// // outputFdrShape(): T & { -// // availability: string; -// // } { -// // const baseShape = this.inputNode.outputFdrShape(); -// // return { -// // ...baseShape, -// // availability: this.inputNode["x-fern-availability"], -// // }; -// // } -// // } -// error: string; -// }; - -// outputFdrShape = (): FdrAPI.TypeId => { -// // This is not correct, but it's a placeholder for now -// return this.reference ? FdrAPI.TypeId(this.reference) : FdrAPI.TypeId("unknown"); -// }; -// } - -// export class ContainerDiscriminationStage implements FdrApiStage { -// id: string; -// outputFdrShape: () => FdrAPI.TypeId; -// } - -// export class PrimitiveNode implements ApiNode { -// context: ApiNodeContext; -// input: OpenAPIV3_1.SchemaObject; -// accessPath: ApiNode[]; -// id: string; -// outputFdrShape: () => FdrAPI.TypeId; -// } - -// export class PropertyNode implements ApiNode { -// id: string; -// constructor( -// readonly name: string, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly context: ApiNodeContext, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.PropertyNode:${name}`; - -// this.input. -// } -// outputFdrShape: () => FdrAPI.api.latest.ObjectProperty; -// } - -// export class ExtraPropertyNode implements ApiNode { -// context: ApiNodeContext; -// input: OpenAPIV3_1.SchemaObject; -// accessPath: ApiNode[]; -// id: string; -// parse: () => -// | { ok: true } -// | { -// ok: false; -// error: string; -// }; -// outputFdrShape: () => FdrAPI.api.latest.TypeReference; -// name = "extra-property"; -// } - -// export class SchemaNode -// implements ApiNode -// { -// id: string; - -// arrayItem: SchemaNode | undefined; -// extensions: TypeNode[] = []; -// schemasToMerge: SchemaNode[] = []; -// properties: PropertyNode[] = []; -// extraProperties: ExtraPropertyNode | undefined; - -// constructor( -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly context: ApiNodeContext, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.SchemaNode:${input.title ?? input.$schema ?? "unknown"}`; -// if (this.input.type === "array") { -// this.arrayItem = new SchemaNode(this.input.items, this.context, this.accessPath); -// } else { -// if (this.input.allOf && this.input.allOf.length > 0) { -// this.input.allOf.forEach((schema) => { -// // make this a guard -// if (typeof schema === "object" && "$ref" in schema) { -// this.extensions.push(new TypeNode(this.context, schema, this.accessPath)); -// } else { -// this.schemasToMerge.push(new SchemaNode(schema, this.context, this.accessPath)); -// } -// }); -// } - -// if (this.input.properties) { -// this.properties = Object.entries(this.input.properties).map(([name, property]) => { -// return new PropertyNode(name, property, this.context, this.accessPath); -// }); -// } -// } -// } - -// outputFdrShape = (): FdrAPI.api.latest.ObjectType => { -// return { -// extends: this.extensions.map((extension) => extension.outputFdrShape()), -// properties: this.properties.map((property) => property.outputFdrShape()), -// extraProperties: this.extraProperties?.outputFdrShape(), -// }; -// }; -// } - -// export class AvailabilityNode> -// implements ApiNode { - -// id: string; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: InputNode, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.AvailabilityNode:${input.id}`; -// } - -// convertAvailability = (availability: string): FernRegistry.Availability | undefined => { -// switch (availability) { -// case "beta": -// return FernRegistry.Availability.Beta; -// case "deprecated": -// return FernRegistry.Availability.Deprecated; -// case "development": -// return FernRegistry.Availability.InDevelopment; -// case "pre-release": -// return FernRegistry.Availability.PreRelease; -// case "stabel": -// return FernRegistry.Availability.Stable; -// case "generally-available": -// return FernRegistry.Availability.GenerallyAvailable; -// default: -// return undefined; -// } -// }; - -// outputFdrShape = (): FernRegistry.Availability | undefined => { -// return "x-fern-availability" in this.input ? this.convertAvailability(this.input["x-fern-availability"] as string) : undefined; -// }; -// } - -// export class DemoTypeStage implements FdrStage { -// id: string; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeStage:${input.title ?? input.$schema ?? "unknown"}`; - -// switch (input.type) { -// case "object": -// return new DemoSchemaNode(this.context, this.input, this.accessPath); -// case "array": -// return new DemoListNode(this.context, this.input, this.accessPath); -// default: -// return new TypeNode(this.context, this.input, this.accessPath); -// } -// } -// outputFdrShape: () => FdrAPI.api.latest.TypeShape { - -// }; -// } - -// export class DemoStringNode implements ApiNode { -// id: string; - -// regex: string | undefined; -// format: string | undefined; -// default: string | undefined; -// minLength: number | undefined; -// maxLength: number | undefined; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoStringNode`; -// this.regex = input.pattern; -// // this.format = input.format; -// this.default = input.default; -// this.minLength = input.minLength; -// this.maxLength = input.maxLength; -// } - -// outputFdrShape = (): FdrAPI.api.latest.PrimitiveType => { -// return { -// type: "string", -// regex: this.regex, -// minLength: this.minLength, -// maxLength: this.maxLength, -// default: this.default, -// } -// }; -// } - -// export class DemoTypeShapeStage implements FdrStage { -// id: string; - -// stringNode: DemoStringNode | undefined; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeShapeStage`; - -// if (input.type === "string") { -// this.stringNode = new DemoStringNode(this.context, this.input, this.accessPath); -// } -// } - -// outputFdrShape = (): FdrAPI.api.latest.TypeShape => { -// // we include this because this is non-exhaustive for now, for demo purposes -// if (!this.stringNode) { -// throw new Error("String node is undefined"); -// } - -// return { -// type: "alias", -// value: { -// type: "primitive", -// value: this.stringNode.outputFdrShape(), -// }, -// } -// }; -// } - -// export class DemoPropertyNode implements ApiNode { -// id: string; - -// propertyShape: DemoTypeShapeStage | undefined = undefined; -// availability: AvailabilityNode | undefined = undefined; - -// constructor( -// readonly name: string, -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoPropertyNode`; - -// if (input.type === "string") { -// this.propertyShape = new DemoTypeShapeStage(this.context, this.input, this.accessPath); -// } -// this.availability = new AvailabilityNode(this.context, this, this.accessPath); -// } - -// outputFdrShape = (): FdrAPI.api.latest.ObjectProperty => { -// // we include this because this is non-exhaustive for now, for demo purposes -// if (!this.propertyShape) { -// throw new Error("Property shape is undefined"); -// } - -// return { -// key: FdrAPI.PropertyKey(this.name), -// valueShape: this.propertyShape.outputFdrShape(), -// description: this.input.description, -// availability: this.availability?.outputFdrShape(), -// } -// }; -// } - -// export class DemoTypeDiscriminationStage implements FdrApiStage { -// id: string; - -// properties: DemoPropertyNode[] = []; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDiscriminationStage`; - -// if (input.type === "object" && input.properties) { -// this.properties = Object.entries(input.properties).map(([name, property]) => { -// return new DemoPropertyNode(name, property, this.context, this.accessPath); -// }); -// } -// } - -// outputFdrShape = (): FdrAPI.api.latest.TypeShape => { - -// }; -// } - -// export class DemoSchemaNode implements ApiNode { -// id: string; - -// constructor( -// readonly name: string, -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.SchemaObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.DemoTypeDefinitionNode`; -// this.shape = new DemoTypeDiscriminationStage(this.context, this.input, this.accessPath); -// } - -// outputFdrShape = (): FdrAPI.api.latest.TypeDefinition => { -// return { -// name: this.name, -// shape: -// } -// }; -// } - -// export class ComponentsNode implements ApiNode { -// id: string; - -// schemas: DemoSchemaNode[] = []; - -// constructor( -// readonly context: ApiNodeContext, -// readonly input: OpenAPIV3_1.ComponentsObject, -// readonly accessPath: ApiNode[], -// ) { -// this.id = `${accessPath.map((node) => node.id).join(".")}.ComponentsNode`; -// this.schemas = Object.entries(this.input.schemas ?? {}).map(([name, schema]) => { -// return new DemoSchemaNode(name, this.context, schema, this.accessPath); -// }); -// } - -// outputFdrShape = (): FdrAPI.api.latest.TypeDefinition[] => { -// return this.schemas.map((schema) => schema.outputFdrShape()); -// }; - -// } diff --git a/packages/parsers/openapi/shared/temporary/endpoint.node.ts b/packages/parsers/openapi/shared/temporary/endpoint.node.ts deleted file mode 100644 index a180d25db4..0000000000 --- a/packages/parsers/openapi/shared/temporary/endpoint.node.ts +++ /dev/null @@ -1,37 +0,0 @@ -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { OpenAPIV3_1 } from "openapi-types"; -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; -// import { PathNode } from "./path.node"; -// import { AuthSchemeIdsNode } from "./authScheme.node"; - -// export class EndpointNode implements ApiNode { -// name = "endpoint"; -// qualifiedId: string; - -// constructor( -// private readonly id: string, -// private readonly method: FdrAPI.HttpMethod, -// private readonly path: string, -// readonly context: ApiNodeContext, -// readonly preProcessedInput: OpenAPIV3_1.OperationObject, -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// this.accessPath.push(this); -// } - -// outputFdrShape = (): FdrAPI.api.latest.EndpointDefinition => { -// return { -// id: FdrAPI.EndpointId(this.id), -// method: this.method, -// path: new PathNode(this.context, this.path, this.accessPath).outputFdrShape(), -// auth: this.preProcessedInput.security -// ? new AuthSchemeIdsNode(this.context, this.preProcessedInput.security, this.accessPath).outputFdrShape() -// : undefined, -// defaultEnvironment: undefined, -// environments: undefined, -// pathParameters: undefined, -// queryParameters: undefined, -// }; -// }; -// } diff --git a/packages/parsers/openapi/shared/temporary/path.node.ts b/packages/parsers/openapi/shared/temporary/path.node.ts deleted file mode 100644 index c94fd38d3b..0000000000 --- a/packages/parsers/openapi/shared/temporary/path.node.ts +++ /dev/null @@ -1,24 +0,0 @@ -// import { ApiNode, ApiNodeContext } from "../interfaces/api.node.interface"; - -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { PathPartNode } from "./pathPart.node"; - -// export class PathNode implements ApiNode { -// name = "path"; -// qualifiedId: string; - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: string, -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// this.accessPath.push(this); -// } - -// outputFdrShape = (): FdrAPI.api.latest.PathPart[] => { -// return this.preProcessedInput -// .split("/") -// .map((part) => new PathPartNode(this.context, part, this.accessPath).outputFdrShape()); -// }; -// } diff --git a/packages/parsers/openapi/shared/temporary/pathPart.node.ts b/packages/parsers/openapi/shared/temporary/pathPart.node.ts deleted file mode 100644 index 4a0d34b70d..0000000000 --- a/packages/parsers/openapi/shared/temporary/pathPart.node.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ApiNode, ApiNodeContext } from "../../base.node.interface"; - -import { FdrAPI } from "@fern-api/fdr-sdk"; - -export class PathPartNode implements ApiNode { - name = "pathPart"; - qualifiedId: string; - - constructor( - readonly context: ApiNodeContext, - readonly preProcessedInput: string, - readonly accessPath: ApiNode[], - ) { - this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; - this.accessPath.push(this); - } - - outputFdrShape = (): FdrAPI.api.latest.PathPart => { - return this.preProcessedInput.startsWith("{") && this.preProcessedInput.endsWith("}") - ? { - type: "pathParameter", - value: FdrAPI.PropertyKey(this.preProcessedInput.slice(1, -1)), - } - : { - type: "literal", - value: this.preProcessedInput, - }; - }; -} diff --git a/packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts b/packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts deleted file mode 100644 index db66e05db9..0000000000 --- a/packages/parsers/openapi/shared/temporary/stages/fdr/api.stage.ts +++ /dev/null @@ -1,72 +0,0 @@ -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { OpenAPIV3_1 } from "openapi-types"; -// import { v4 } from "uuid"; -// import { ApiNode, ApiNodeContext } from "../../interfaces/api.node.interface"; -// import { FdrStage } from "../../interfaces/fdr.stage.interface"; -// import { EndpointNode, Method } from "../../nodes/endpoint.node"; - -// type ApiProcessingStageOutput = { -// endpoints: FdrAPI.api.latest.ApiDefinition["endpoints"]; -// websockets: FdrAPI.api.latest.ApiDefinition["websockets"]; -// webhooks: FdrAPI.api.latest.ApiDefinition["webhooks"]; -// }; - -// export class FdrApiStage implements FdrStage { -// stageName = "ApiProcessingStage"; -// name = "ApiProcessingStage"; -// qualifiedId: string; - -// endpoints: ApiProcessingStageOutput["endpoints"] = {}; -// websockets: ApiProcessingStageOutput["websockets"] = {}; -// webhooks: ApiProcessingStageOutput["webhooks"] = {}; - -// private processEndpoint(method: Method, path: string, pathItem: OpenAPIV3_1.PathItemObject) { -// const id = v4(); -// this.endpoints[FdrAPI.EndpointId(id)] = new EndpointNode( -// id, -// method, -// path, -// this.context, -// pathItem, -// this.accessPath, -// ).outputFdrShape(); -// } - -// constructor( -// readonly context: ApiNodeContext, -// readonly preProcessedInput: OpenAPIV3_1.Document["paths"], -// readonly accessPath: ApiNode[], -// ) { -// this.qualifiedId = `${this.accessPath.map((node) => node.qualifiedId).join(".")}.${this.name}`; -// this.accessPath.push(this); - -// if (this.preProcessedInput != null) { -// for (const [path, pathItem] of Object.entries(this.preProcessedInput)) { -// // handle $ref -// if (pathItem != null) { -// if (pathItem.get != null) { -// this.processEndpoint("GET", path, pathItem.get); -// } -// if (pathItem.post != null) { -// this.processEndpoint("POST", path, pathItem.post); -// } -// if (pathItem.put != null) { -// this.processEndpoint("PUT", path, pathItem.put); -// } -// if (pathItem.delete != null) { -// this.processEndpoint("DELETE", path, pathItem.delete); -// } -// // Right now, we only support the 4 methods above, head, options, and trace... are not supported -// } -// } -// } -// } - -// outputFdrShape = (): ApiProcessingStageOutput => { -// return { -// endpoints: {}, -// webhooks: {}, -// websockets: {}, -// }; -// }; -// } diff --git a/packages/parsers/openapi/shared/temporary/webhook.node.ts b/packages/parsers/openapi/shared/temporary/webhook.node.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/parsers/openapi/shared/temporary/websocket.node.ts b/packages/parsers/openapi/shared/temporary/websocket.node.ts deleted file mode 100644 index e69de29bb2..0000000000 From 21c12d7b817543edea71ea3d456fbb81f960c13e Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Fri, 15 Nov 2024 15:23:20 -0500 Subject: [PATCH 09/14] unit tests updated --- .../__test__/createMockContext.util.ts | 16 ++ .../__test__/shared/nodes/object.node.test.ts | 87 +++++++++++ .../shared/nodes/objectProperty.node.test.ts | 61 ++++++++ .../nodes/primitives/number.node.test.ts | 100 +++++++++++++ .../primitives/number/float.node.test.ts | 60 ++++++++ .../primitives/number/integer.node.test.ts | 70 +++++++++ .../nodes/primitives/string.node.test.ts | 110 ++++++++++++++ .../__test__/shared/nodes/schema.test.ts | 59 ++++++++ .../shared/nodes/typeReference.node.test.ts | 139 ++++++++++++++++++ .../shared/nodes/typeShape.node.test.ts | 114 ++++++++++++++ .../shared/nodes/primitives/number.node.ts | 13 +- .../nodes/primitives/number/float.node.ts | 6 +- .../nodes/primitives/number/integer.node.ts | 6 +- .../shared/nodes/primitives/string.node.ts | 4 +- .../nodes/primitives/types/format.types.ts | 104 +++++++------ .../openapi/shared/nodes/schema.node.ts | 3 +- .../shared/nodes/typeReference.node.ts | 17 ++- .../openapi/shared/nodes/typeShape.node.ts | 9 +- 18 files changed, 903 insertions(+), 75 deletions(-) create mode 100644 packages/parsers/__test__/createMockContext.util.ts create mode 100644 packages/parsers/__test__/shared/nodes/object.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/schema.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/typeReference.node.test.ts create mode 100644 packages/parsers/__test__/shared/nodes/typeShape.node.test.ts diff --git a/packages/parsers/__test__/createMockContext.util.ts b/packages/parsers/__test__/createMockContext.util.ts new file mode 100644 index 0000000000..cb71f9d028 --- /dev/null +++ b/packages/parsers/__test__/createMockContext.util.ts @@ -0,0 +1,16 @@ +import { vi } from "vitest"; +import { ApiNodeContext } from "../openapi/base.node.interface"; + +import { Logger } from "@playwright/test"; + +export function createMockContext(): ApiNodeContext { + return { + orgId: "orgId", + apiId: "apiId", + logger: {} as Logger, + errorCollector: { + addError: vi.fn(), + errors: [], + }, + }; +} diff --git a/packages/parsers/__test__/shared/nodes/object.node.test.ts b/packages/parsers/__test__/shared/nodes/object.node.test.ts new file mode 100644 index 0000000000..90da51e9a3 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/object.node.test.ts @@ -0,0 +1,87 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { ObjectNode } from "../../../openapi/shared/nodes/object.node"; +import { SchemaObject } from "../../../openapi/shared/openapi.types"; +import { createMockContext } from "../../createMockContext.util"; + +describe("ObjectNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle object with no properties or extends", () => { + const input: SchemaObject = { + type: "object", + }; + const node = new ObjectNode(mockContext, input, []); + expect(node.properties).toEqual([]); + expect(node.extends).toEqual([]); + expect(node.extraProperties).toBeUndefined(); + }); + + it("should handle object with properties", () => { + const input: SchemaObject = { + type: "object", + properties: { + name: { type: "string" }, + age: { type: "integer" }, + }, + }; + const node = new ObjectNode(mockContext, input, []); + expect(node.properties).toHaveLength(2); + }); + + it("should handle object with allOf/extends", () => { + const input: SchemaObject = { + type: "object", + allOf: [{ $ref: "TypeA" }, { $ref: "TypeB" }], + }; + const node = new ObjectNode(mockContext, input, []); + // This needs to change to the computed generated type id for FDR + expect(node.extends).toEqual([FdrAPI.TypeId("TypeA"), FdrAPI.TypeId("TypeB")]); + }); + + it("should filter out non-reference allOf items", () => { + const input: SchemaObject = { + type: "object", + allOf: [{ $ref: "TypeA" }, { type: "object" }], + }; + const node = new ObjectNode(mockContext, input, []); + expect(node.extends).toEqual([FdrAPI.TypeId("TypeA")]); + }); + }); + + describe("outputFdrShape", () => { + it("should output shape with no properties", () => { + const node = new ObjectNode(mockContext, { type: "object" }, []); + expect(node.outputFdrShape()).toEqual({ + extends: [], + properties: [], + extraProperties: undefined, + }); + }); + + it("should output shape with multiple properties and extends", () => { + const input: SchemaObject = { + type: "object", + properties: { + firstName: { type: "string" }, + lastName: { type: "string" }, + age: { type: "integer" }, + height: { type: "number" }, + id: { type: "string" }, + score: { type: "number" }, + }, + allOf: [{ $ref: "BaseType" }, { $ref: "PersonType" }], + }; + const node = new ObjectNode(mockContext, input, []); + const shape = node.outputFdrShape(); + expect(shape?.extends).toEqual([FdrAPI.TypeId("BaseType"), FdrAPI.TypeId("PersonType")]); + expect(shape?.properties).toHaveLength(6); + expect(shape?.extraProperties).toBeUndefined(); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts b/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts new file mode 100644 index 0000000000..91b52af370 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts @@ -0,0 +1,61 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { ObjectPropertyNode } from "../../../openapi/shared/nodes/objectProperty.node"; +import { ReferenceObject, SchemaObject } from "../../../openapi/shared/openapi.types"; +import { createMockContext } from "../../createMockContext.util"; + +describe("ObjectPropertyNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle basic schema object", () => { + const input: SchemaObject = { + type: "string", + description: "test description", + }; + const node = new ObjectPropertyNode("testKey", mockContext, input, []); + expect(node.description).toBe("test description"); + }); + + it("should handle reference object", () => { + const input: ReferenceObject = { + $ref: "TypeA", + }; + const node = new ObjectPropertyNode("testKey", mockContext, input, []); + expect(node.valueShape).toBeDefined(); + }); + }); + + describe("outputFdrShape", () => { + it("should output shape with primitive type", () => { + const input: SchemaObject = { + type: "string", + description: "test description", + }; + const node = new ObjectPropertyNode("testKey", mockContext, input, []); + const shape = node.outputFdrShape(); + expect(shape).toBeDefined(); + expect(shape?.key).toEqual(FdrAPI.PropertyKey("testKey")); + expect(shape?.description).toBe("test description"); + expect(shape?.availability).toBeUndefined(); + }); + + it("should return undefined if valueShape is undefined and collect error", () => { + const input: SchemaObject = { + type: "invalid", + }; + const node = new ObjectPropertyNode("testKey", mockContext, input, []); + vi.spyOn(node.valueShape, "outputFdrShape").mockReturnValue(undefined); + expect(node.outputFdrShape()).toBeUndefined(); + // this should show up, but since the examples are terse and non-exhaustive, we do not have any validation checking + // expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + // "Failed to generate shape for property testKey", + // [], + // ); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts new file mode 100644 index 0000000000..757a3bfb29 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts @@ -0,0 +1,100 @@ +import { expect } from "vitest"; + +import { beforeEach, describe, it, vi } from "vitest"; +import { NumberNode } from "../../../../openapi/shared/nodes/primitives/number.node"; +import { FloatNode } from "../../../../openapi/shared/nodes/primitives/number/float.node"; +import { IntegerNode } from "../../../../openapi/shared/nodes/primitives/number/integer.node"; +import { createMockContext } from "../../../createMockContext.util"; + +describe("NumberNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle valid integer input", () => { + const input = { + type: "integer", + minimum: 1, + maximum: 10, + default: 5, + }; + const node = new NumberNode(mockContext, input, []); + expect(node.typeNode).toBeInstanceOf(IntegerNode); + expect(node.minimum).toBe(1); + expect(node.maximum).toBe(10); + expect(node.default).toBe(5); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle valid number input", () => { + const input = { + type: "number", + minimum: 1.5, + maximum: 10.5, + default: 5.5, + }; + const node = new NumberNode(mockContext, input, []); + expect(node.typeNode).toBeInstanceOf(FloatNode); + expect(node.minimum).toBe(1.5); + expect(node.maximum).toBe(10.5); + expect(node.default).toBe(5.5); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle invalid type", () => { + const input = { type: "string" }; + const node = new NumberNode(mockContext, input, []); + expect(node.typeNode).toBeUndefined(); + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected type "integer" or "number" for numerical primitive, but got "string"', + [], + undefined, + ); + }); + }); + + describe("outputFdrShape", () => { + it("should return undefined when typeNode shape is undefined", () => { + const input = { type: "string" }; + const node = new NumberNode(mockContext, input, []); + expect(node.outputFdrShape()).toBeUndefined(); + }); + + it("should return complete shape for integer type", () => { + const input = { + type: "integer", + minimum: 1, + maximum: 10, + default: 5, + }; + const node = new NumberNode(mockContext, input, []); + const shape = node.outputFdrShape(); + expect(shape).toEqual({ + type: "integer", + minimum: 1, + maximum: 10, + default: 5, + }); + }); + + it("should return complete shape for number type", () => { + const input = { + type: "number", + minimum: 1.5, + maximum: 10.5, + default: 5.5, + }; + const node = new NumberNode(mockContext, input, []); + const shape = node.outputFdrShape(); + expect(shape).toEqual({ + type: "double", + minimum: 1.5, + maximum: 10.5, + default: 5.5, + }); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts new file mode 100644 index 0000000000..b3768dea08 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts @@ -0,0 +1,60 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { ApiNodeContext } from "../../../../../openapi/base.node.interface"; +import { FloatNode } from "../../../../../openapi/shared/nodes/primitives/number/float.node"; +import { createMockContext } from "../../../../createMockContext.util"; + +describe("FloatNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should handle valid number input with float format", () => { + const input = { type: "number", format: "float" }; + const node = new FloatNode(mockContext, input, []); + expect(node.type).toBe("double"); + expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle valid number input with double format", () => { + const input = { type: "number", format: "double" }; + const node = new FloatNode(mockContext, input, []); + expect(node.type).toBe("double"); + expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle valid number input with no format", () => { + const input = { type: "number" }; + const node = new FloatNode(mockContext, input, []); + expect(node.type).toBe("double"); + expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle invalid type", () => { + const input = { type: "string" }; + const node = new FloatNode(mockContext, input, []); + expect(node.type).toBeUndefined(); + expect(node.outputFdrShape()).toBeUndefined(); + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected type "number" for numerical primitive, but got "string"', + [], + undefined, + ); + }); + + it("should handle invalid format", () => { + const input = { type: "number", format: "invalid" }; + const node = new FloatNode(mockContext as ApiNodeContext, input, []); + expect(node.type).toBeUndefined(); + expect(node.outputFdrShape()).toBeUndefined(); + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected format for number primitive, but got "invalid"', + [], + undefined, + ); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts new file mode 100644 index 0000000000..d338ddc07e --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts @@ -0,0 +1,70 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { IntegerNode } from "../../../../../openapi/shared/nodes/primitives/number/integer.node"; +import { createMockContext } from "../../../../createMockContext.util"; + +describe("IntegerNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should handle valid integer input with int32 format", () => { + const input = { type: "integer", format: "int32" }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("integer"); + expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle valid integer input with int64 format", () => { + const input = { type: "integer", format: "int64" }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("long"); + expect(node.outputFdrShape()).toEqual({ type: "long" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle valid integer input with no format", () => { + const input = { type: "integer" }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("integer"); + expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + + it("should handle invalid type", () => { + const input = { type: "string" }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("integer"); // Default value + expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected type "integer" for numerical primitive, but got "string"', + [], + undefined, + ); + }); + + it("should handle invalid format", () => { + const input = { type: "integer", format: "invalid" }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("integer"); // Default value + expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected format for integer primitive, but got "invalid"', + [], + undefined, + ); + }); + + it("should handle other valid integer formats", () => { + const formats = ["int8", "int16", "uint8", "sf-integer"]; + formats.forEach((format) => { + const input = { type: "integer", format }; + const node = new IntegerNode(mockContext, input, []); + expect(node.type).toBe("integer"); + expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts new file mode 100644 index 0000000000..76b26066f9 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts @@ -0,0 +1,110 @@ +import { expect, it } from "vitest"; + +import { beforeEach, describe, vi } from "vitest"; +import { StringNode } from "../../../../openapi/shared/nodes/primitives/string.node"; +import { SchemaObject } from "../../../../openapi/shared/openapi.types"; +import { createMockContext } from "../../../createMockContext.util"; + +describe("StringNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle valid string input", () => { + const input: SchemaObject = { + type: "string", + format: "date-time", + pattern: "^test$", + default: "default", + minLength: 1, + maxLength: 10, + }; + + const node = new StringNode(mockContext, input, []); + + expect(node.type).toBe("datetime"); + expect(node.regex).toBe("^test$"); + expect(node.default).toBe("default"); + expect(node.minLength).toBe(1); + expect(node.maxLength).toBe(10); + }); + + it("should handle invalid type", () => { + const input = { + type: "number", + } as SchemaObject; + + new StringNode(mockContext, input, []); + + expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + 'Expected type "string" for primitive, but got "number"', + [], + undefined, + ); + }); + }); + + describe("mapToFdrType", () => { + let node: StringNode; + + beforeEach(() => { + node = new StringNode(mockContext, { type: "string" }, []); + }); + + it("should map base64 formats correctly", () => { + expect(node.mapToFdrType("base64url")).toBe("base64"); + expect(node.mapToFdrType("binary")).toBe("base64"); + expect(node.mapToFdrType("byte")).toBe("base64"); + }); + + it("should map date formats correctly", () => { + expect(node.mapToFdrType("date-time")).toBe("datetime"); + expect(node.mapToFdrType("date")).toBe("date"); + }); + + it("should map other special formats correctly", () => { + expect(node.mapToFdrType("int64")).toBe("bigInteger"); + expect(node.mapToFdrType("uuid")).toBe("uuid"); + }); + + it("should default to string for common formats", () => { + expect(node.mapToFdrType("email")).toBe("string"); + expect(node.mapToFdrType("uri")).toBe("string"); + expect(node.mapToFdrType(undefined)).toBe("string"); + }); + }); + + describe("outputFdrShape", () => { + it("should return undefined when type is undefined", () => { + const node = new StringNode(mockContext, { type: "string" }, []); + node.type = undefined; + expect(node.outputFdrShape()).toBeUndefined(); + }); + + it("should return complete shape with all properties", () => { + const node = new StringNode( + mockContext, + { + type: "string", + format: "date-time", + pattern: "^test$", + default: "default", + minLength: 1, + maxLength: 10, + }, + [], + ); + + expect(node.outputFdrShape()).toEqual({ + type: "datetime", + regex: "^test$", + default: "default", + minLength: 1, + maxLength: 10, + }); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/schema.test.ts b/packages/parsers/__test__/shared/nodes/schema.test.ts new file mode 100644 index 0000000000..eb52be1300 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/schema.test.ts @@ -0,0 +1,59 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createMockContext } from "../../../__test__/createMockContext.util"; +import { SchemaNode } from "../../../openapi/shared/nodes/schema.node"; +import { SchemaObject } from "../../../openapi/shared/openapi.types"; + +describe("SchemaNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle basic schema input", () => { + const input: SchemaObject = { + type: "string", + description: "test description", + }; + + const node = new SchemaNode("TestType", mockContext, input, []); + + expect(node.description).toBe("test description"); + expect(node.shape).toBeDefined(); + }); + }); + + describe("outputFdrShape", () => { + it("should output complete shape", () => { + const input: SchemaObject = { + type: "string", + description: "test description", + }; + + const node = new SchemaNode("TestType", mockContext, input, []); + const shape = node.outputFdrShape(); + + expect(shape).toBeDefined(); + expect(shape?.name).toBe("TestType"); + expect(shape?.description).toBe("test description"); + expect(shape?.availability).toBeUndefined(); + }); + + it("should return undefined if shape is undefined", () => { + const input: SchemaObject = { + type: "invalid", + }; + + const node = new SchemaNode("TestType", mockContext, input, []); + vi.spyOn(node.shape, "outputFdrShape").mockReturnValue(undefined); + + expect(node.outputFdrShape()).toBeUndefined(); + // this should show up, but since the examples are terse and non-exhaustive, we do not have any validation checking + // expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( + // "Failed to generate shape for type TestType", + // [], + // ); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts b/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts new file mode 100644 index 0000000000..9887350d86 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts @@ -0,0 +1,139 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createMockContext } from "../../../__test__/createMockContext.util"; +import { NumberNode } from "../../../openapi/shared/nodes/primitives/number.node"; +import { StringNode } from "../../../openapi/shared/nodes/primitives/string.node"; +import { FdrStringType } from "../../../openapi/shared/nodes/primitives/types/fdr.types"; +import { TypeReferenceNode, isReferenceObject } from "../../../openapi/shared/nodes/typeReference.node"; +import { ReferenceObject, SchemaObject } from "../../../openapi/shared/openapi.types"; + +// This will be broken up into multiple tests, but for now, this is a start. We will need to errors for invalid inputs +describe("TypeReferenceNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("isReferenceObject", () => { + it("should return true for valid reference objects", () => { + expect(isReferenceObject({ $ref: "TypeA" })).toBe(true); + }); + + it("should return false for non-reference objects", () => { + expect(isReferenceObject({ type: "string" })).toBe(false); + expect(isReferenceObject(null)).toBe(false); + expect(isReferenceObject(undefined)).toBe(false); + expect(isReferenceObject({ $ref: 123 })).toBe(false); + }); + }); + + describe("constructor", () => { + it("should handle reference object input", () => { + const input: ReferenceObject = { + $ref: "TypeA", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + + expect(node.type).toBe("id"); + expect(node.ref).toBe("TypeA"); + expect(node.default).toBeUndefined(); + expect(node.typeNode).toBeUndefined(); + }); + + it("should handle string schema input", () => { + const input: SchemaObject = { + type: "string", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + + expect(node.type).toBe("primitive"); + expect(node.ref).toBeUndefined(); + expect(node.typeNode).toBeInstanceOf(StringNode); + }); + + it("should handle number schema input", () => { + const input: SchemaObject = { + type: "number", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + + expect(node.type).toBe("primitive"); + expect(node.ref).toBeUndefined(); + expect(node.typeNode).toBeInstanceOf(NumberNode); + }); + + it("should handle integer schema input", () => { + const input: SchemaObject = { + type: "integer", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + + expect(node.type).toBe("primitive"); + expect(node.ref).toBeUndefined(); + expect(node.typeNode).toBeInstanceOf(NumberNode); + }); + }); + + describe("outputFdrShape", () => { + it("should output reference shape", () => { + const input: ReferenceObject = { + $ref: "TypeA", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + const shape = node.outputFdrShape(); + + expect(shape).toEqual({ + type: "id", + id: FdrAPI.TypeId("TypeA"), + default: undefined, + }); + }); + + it("should output primitive shape", () => { + const input: SchemaObject = { + type: "string", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + const mockPrimitiveShape: FdrStringType = { + type: "string", + regex: undefined, + minLength: undefined, + maxLength: undefined, + default: undefined, + }; + vi.spyOn(node.typeNode as StringNode, "outputFdrShape").mockReturnValue(mockPrimitiveShape); + + const shape = node.outputFdrShape(); + + expect(shape).toEqual({ + type: "primitive", + value: mockPrimitiveShape, + }); + }); + + it("should return undefined if ref is undefined for id type", () => { + const node = new TypeReferenceNode(mockContext, { $ref: "TypeA" }, []); + node.ref = undefined; + + expect(node.outputFdrShape()).toBeUndefined(); + }); + + it("should return undefined if primitive shape is undefined", () => { + const input: SchemaObject = { + type: "string", + }; + + const node = new TypeReferenceNode(mockContext, input, []); + vi.spyOn(node.typeNode as StringNode, "outputFdrShape").mockReturnValue(undefined); + + expect(node.outputFdrShape()).toBeUndefined(); + }); + }); +}); diff --git a/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts b/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts new file mode 100644 index 0000000000..f338055ee5 --- /dev/null +++ b/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts @@ -0,0 +1,114 @@ +import { FdrAPI } from "@fern-api/fdr-sdk"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createMockContext } from "../../../__test__/createMockContext.util"; +import { ObjectNode } from "../../../openapi/shared/nodes/object.node"; +import { TypeReferenceNode } from "../../../openapi/shared/nodes/typeReference.node"; +import { TypeShapeNode } from "../../../openapi/shared/nodes/typeShape.node"; +import { SchemaObject } from "../../../openapi/shared/openapi.types"; + +describe("TypeShapeNode", () => { + const mockContext = createMockContext(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("constructor", () => { + it("should handle object schema input", () => { + const input: SchemaObject = { + type: "object", + properties: {}, + }; + + const node = new TypeShapeNode(mockContext, input, [], "test"); + + expect(node.type).toBe("object"); + expect(node.typeNode).toBeInstanceOf(ObjectNode); + }); + + it("should handle non-object schema input as alias", () => { + const input: SchemaObject = { + type: "string", + }; + + const node = new TypeShapeNode(mockContext, input, []); + + expect(node.type).toBe("alias"); + expect(node.typeNode).toBeInstanceOf(TypeReferenceNode); + }); + }); + + describe("outputFdrShape", () => { + it("should output object shape", () => { + const input: SchemaObject = { + type: "object", + properties: {}, + }; + + const node = new TypeShapeNode(mockContext, input, []); + const mockObjectShape: FdrAPI.api.latest.ObjectType = { + properties: [], + extends: [], + extraProperties: undefined, + }; + vi.spyOn(node.typeNode as ObjectNode, "outputFdrShape").mockReturnValue(mockObjectShape); + + const shape = node.outputFdrShape(); + + expect(shape).toEqual({ + type: "object", + ...mockObjectShape, + }); + }); + + it("should output alias shape", () => { + const input: SchemaObject = { + type: "string", + }; + + const node = new TypeShapeNode(mockContext, input, []); + const mockTypeReference: FdrAPI.api.latest.TypeReference = { + type: "primitive", + value: { + type: "string", + regex: undefined, + minLength: undefined, + maxLength: undefined, + default: undefined, + }, + }; + vi.spyOn(node.typeNode as TypeReferenceNode, "outputFdrShape").mockReturnValue(mockTypeReference); + + const shape = node.outputFdrShape(); + + expect(shape).toEqual({ + type: "alias", + value: mockTypeReference, + }); + }); + + it("should return undefined if typeNode shape is undefined", () => { + const input: SchemaObject = { + type: "object", + properties: {}, + }; + + const node = new TypeShapeNode(mockContext, input, []); + vi.spyOn(node.typeNode as ObjectNode, "outputFdrShape").mockReturnValue(undefined); + + expect(node.outputFdrShape()).toBeUndefined(); + }); + + it("should return undefined if type is undefined", () => { + const input: SchemaObject = { + type: "object", + properties: {}, + }; + + const node = new TypeShapeNode(mockContext, input, []); + node.type = undefined; + + expect(node.outputFdrShape()).toBeUndefined(); + }); + }); +}); diff --git a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts index a4e64b1947..f285cba694 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts @@ -1,17 +1,10 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; import { SchemaObject } from "../../openapi.types"; import { FloatNode } from "./number/float.node"; import { IntegerNode } from "./number/integer.node"; -type FdrNumberType = - | FdrAPI.api.v1.read.PrimitiveType.Integer - | FdrAPI.api.v1.read.PrimitiveType.Long - | FdrAPI.api.v1.read.PrimitiveType.Double - | FdrAPI.api.v1.read.PrimitiveType.Uint - | FdrAPI.api.v1.read.PrimitiveType.Uint64; - -export class NumberNode extends InputApiNode { +import { FdrFloatType, FdrIntegerType } from "./types/fdr.types"; +export class NumberNode extends InputApiNode { typeNode: IntegerNode | FloatNode | undefined; default: number | undefined; minimum: number | undefined; @@ -43,7 +36,7 @@ export class NumberNode extends InputApiNode { this.maximum = input.maximum; } - outputFdrShape = (): FdrNumberType | undefined => { + outputFdrShape = (): FdrFloatType | FdrIntegerType | undefined => { const typeProperties = this.typeNode?.outputFdrShape(); if (typeProperties === undefined) { diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts index d0ab31a841..2306be3914 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts @@ -2,10 +2,10 @@ import { UnreachableCaseError } from "ts-essentials"; import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; import { SchemaObject } from "../../../openapi.types"; import { FdrFloatType } from "../types/fdr.types"; -import { OpenApiNumberTypeFormat } from "../types/format.types"; +import { ConstArrayToType, OPENAPI_NUMBER_TYPE_FORMAT } from "../types/format.types"; -function isOpenApiNumberTypeFormat(format: string | undefined): format is OpenApiNumberTypeFormat { - return format === "float" || format === "double" || format === undefined; +function isOpenApiNumberTypeFormat(format: unknown): format is ConstArrayToType { + return OPENAPI_NUMBER_TYPE_FORMAT.includes(format as ConstArrayToType); } export class FloatNode extends OutputApiNode> { diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts index f6aec50bfd..0235bd80cc 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts @@ -2,10 +2,10 @@ import { UnreachableCaseError } from "ts-essentials"; import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; import { SchemaObject } from "../../../openapi.types"; import { FdrIntegerType } from "../types/fdr.types"; -import { OpenApiIntegerTypeFormat } from "../types/format.types"; +import { ConstArrayToType, OPENAPI_INTEGER_TYPE_FORMAT } from "../types/format.types"; -function isOpenApiIntegerTypeFormat(format: string | undefined): format is OpenApiIntegerTypeFormat { - return format === "int32" || format === "int64" || format === undefined; +function isOpenApiIntegerTypeFormat(format: unknown): format is ConstArrayToType { + return OPENAPI_INTEGER_TYPE_FORMAT.includes(format as ConstArrayToType); } export class IntegerNode extends OutputApiNode> { diff --git a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts index 36fba0a4b4..9866af3a3d 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts @@ -2,7 +2,7 @@ import { UnreachableCaseError } from "ts-essentials"; import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; import { SchemaObject } from "../../openapi.types"; import { FdrStringType } from "./types/fdr.types"; -import { OpenApiStringTypeFormat } from "./types/format.types"; +import { ConstArrayToType, OPENAPI_STRING_TYPE_FORMAT } from "./types/format.types"; export class StringNode extends InputApiNode { type: FdrStringType["type"] | undefined; @@ -11,7 +11,7 @@ export class StringNode extends InputApiNode { minLength: number | undefined; maxLength: number | undefined; - mapToFdrType = (format: OpenApiStringTypeFormat): FdrStringType["type"] | undefined => { + mapToFdrType = (format: ConstArrayToType): FdrStringType["type"] | undefined => { switch (format) { case "base64url": case "binary": diff --git a/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts b/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts index b8872f7de1..ba8a50d802 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts @@ -1,48 +1,60 @@ // Copied from https://spec.openapis.org/registry/format/ -export type OpenApiNumberTypeFormat = - | "decimal" - | "decimal128" - | "double-int" - | "double" - | "float" - | "sf-decimal" - | undefined; -export type OpenApiIntegerTypeFormat = "int16" | "int32" | "int64" | "int8" | "sf-integer" | "uint8" | undefined; -export type OpenApiStringTypeFormat = - | "base64url" - | "binary" - | "byte" - | "char" - | "commonmark" - | "date-time" - | "date" - | "decimal" - | "decimal128" - | "duration" - | "email" - | "hostname" - | "html" - | "http-date" - | "idn-email" - | "idn-hostname" - | "int64" - | "ipv4" - | "ipv6" - | "iri-reference" - | "iri" - | "json-pointer" - | "media-range" - | "password" - | "regex" - | "relative-json-pointer" - | "sf-binary" - | "sf-boolean" - | "sf-string" - | "sf-token" - | "time" - | "uri-reference" - | "uri-template" - | "uri" - | "uuid" - | undefined; +export type ConstArrayToType = T[number]; + +export const OPENAPI_NUMBER_TYPE_FORMAT = [ + "decimal", + "decimal128", + "double-int", + "double", + "float", + "sf-decimal", + undefined, +] as const; +export const OPENAPI_INTEGER_TYPE_FORMAT = [ + "int16", + "int32", + "int64", + "int8", + "sf-integer", + "uint8", + undefined, +] as const; +export const OPENAPI_STRING_TYPE_FORMAT = [ + "base64url", + "binary", + "byte", + "char", + "commonmark", + "date-time", + "date", + "decimal", + "decimal128", + "duration", + "email", + "hostname", + "html", + "http-date", + "idn-email", + "idn-hostname", + "int64", + "ipv4", + "ipv6", + "iri-reference", + "iri", + "json-pointer", + "media-range", + "password", + "regex", + "relative-json-pointer", + "sf-binary", + "sf-boolean", + "sf-string", + "sf-token", + "time", + "uri-reference", + "uri-template", + "uri", + "uuid", + undefined, +] as const; diff --git a/packages/parsers/openapi/shared/nodes/schema.node.ts b/packages/parsers/openapi/shared/nodes/schema.node.ts index d272539da6..d6baea8a1e 100644 --- a/packages/parsers/openapi/shared/nodes/schema.node.ts +++ b/packages/parsers/openapi/shared/nodes/schema.node.ts @@ -1,5 +1,4 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; import { ApiNodeContext, InputApiNode } from "../../base.node.interface"; import { SchemaObject } from "../openapi.types"; import { TypeShapeNode } from "./typeShape.node"; @@ -12,7 +11,7 @@ export class SchemaNode extends InputApiNode { - const primitiveShape = this.typeNode?.outputFdrShape(); - if (primitiveShape === undefined || this.ref === undefined) { - return undefined; - } if (this.type === "id") { + if (this.ref === undefined) { + return undefined; + } return { type: this.type, id: FdrAPI.TypeId(this.ref), @@ -45,6 +48,10 @@ export class TypeReferenceNode extends OutputApiNode Date: Fri, 15 Nov 2024 15:37:15 -0500 Subject: [PATCH 10/14] addressed comments --- .../parsers/__test__/createMockContext.util.ts | 2 +- .../__test__/shared/nodes/object.node.test.ts | 6 +++--- .../shared/nodes/objectProperty.node.test.ts | 8 ++++---- .../shared/nodes/primitives/number.node.test.ts | 8 ++++---- .../nodes/primitives/number/float.node.test.ts | 12 ++++++------ .../primitives/number/integer.node.test.ts | 12 ++++++------ .../shared/nodes/primitives/string.node.test.ts | 6 +++--- .../__test__/shared/nodes/schema.test.ts | 8 ++++---- .../shared/nodes/typeReference.node.test.ts | 16 ++++++++-------- .../shared/nodes/typeShape.node.test.ts | 16 ++++++++-------- .../nodes/primitives/types => fdr}/fdr.types.ts | 0 packages/parsers/openapi/3.1/openApi3_1.node.ts | 10 +++++----- .../{base.node.interface.ts => ApiNode.ts} | 17 ++--------------- packages/parsers/openapi/ErrorCollector.ts | 14 ++++++++++++++ .../parsers/openapi/shared/nodes/object.node.ts | 15 +++++++-------- .../openapi/shared/nodes/objectProperty.node.ts | 8 ++++---- .../shared/nodes/primitives/enum.node.ts | 5 ++--- .../shared/nodes/primitives/number.node.ts | 15 +++++++-------- .../nodes/primitives/number/float.node.ts | 10 ++++------ .../nodes/primitives/number/integer.node.ts | 10 ++++------ .../shared/nodes/primitives/string.node.ts | 13 ++++--------- .../parsers/openapi/shared/nodes/schema.node.ts | 8 ++++---- .../openapi/shared/nodes/typeReference.node.ts | 9 +++++---- .../openapi/shared/nodes/typeShape.node.ts | 6 +++--- packages/parsers/package.json | 1 + pnpm-lock.yaml | 3 +++ 26 files changed, 116 insertions(+), 122 deletions(-) rename packages/parsers/{openapi/shared/nodes/primitives/types => fdr}/fdr.types.ts (100%) rename packages/parsers/openapi/{base.node.interface.ts => ApiNode.ts} (72%) create mode 100644 packages/parsers/openapi/ErrorCollector.ts diff --git a/packages/parsers/__test__/createMockContext.util.ts b/packages/parsers/__test__/createMockContext.util.ts index cb71f9d028..e22e828457 100644 --- a/packages/parsers/__test__/createMockContext.util.ts +++ b/packages/parsers/__test__/createMockContext.util.ts @@ -1,5 +1,5 @@ import { vi } from "vitest"; -import { ApiNodeContext } from "../openapi/base.node.interface"; +import { ApiNodeContext } from "../openapi/ApiNode"; import { Logger } from "@playwright/test"; diff --git a/packages/parsers/__test__/shared/nodes/object.node.test.ts b/packages/parsers/__test__/shared/nodes/object.node.test.ts index 90da51e9a3..f9067e931f 100644 --- a/packages/parsers/__test__/shared/nodes/object.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/object.node.test.ts @@ -54,10 +54,10 @@ describe("ObjectNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should output shape with no properties", () => { const node = new ObjectNode(mockContext, { type: "object" }, []); - expect(node.outputFdrShape()).toEqual({ + expect(node.toFdrShape()).toEqual({ extends: [], properties: [], extraProperties: undefined, @@ -78,7 +78,7 @@ describe("ObjectNode", () => { allOf: [{ $ref: "BaseType" }, { $ref: "PersonType" }], }; const node = new ObjectNode(mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape?.extends).toEqual([FdrAPI.TypeId("BaseType"), FdrAPI.TypeId("PersonType")]); expect(shape?.properties).toHaveLength(6); expect(shape?.extraProperties).toBeUndefined(); diff --git a/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts b/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts index 91b52af370..44bc54358b 100644 --- a/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/objectProperty.node.test.ts @@ -30,14 +30,14 @@ describe("ObjectPropertyNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should output shape with primitive type", () => { const input: SchemaObject = { type: "string", description: "test description", }; const node = new ObjectPropertyNode("testKey", mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toBeDefined(); expect(shape?.key).toEqual(FdrAPI.PropertyKey("testKey")); expect(shape?.description).toBe("test description"); @@ -49,8 +49,8 @@ describe("ObjectPropertyNode", () => { type: "invalid", }; const node = new ObjectPropertyNode("testKey", mockContext, input, []); - vi.spyOn(node.valueShape, "outputFdrShape").mockReturnValue(undefined); - expect(node.outputFdrShape()).toBeUndefined(); + vi.spyOn(node.valueShape, "toFdrShape").mockReturnValue(undefined); + expect(node.toFdrShape()).toBeUndefined(); // this should show up, but since the examples are terse and non-exhaustive, we do not have any validation checking // expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( // "Failed to generate shape for property testKey", diff --git a/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts index 757a3bfb29..ade73df788 100644 --- a/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/primitives/number.node.test.ts @@ -56,11 +56,11 @@ describe("NumberNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should return undefined when typeNode shape is undefined", () => { const input = { type: "string" }; const node = new NumberNode(mockContext, input, []); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); it("should return complete shape for integer type", () => { @@ -71,7 +71,7 @@ describe("NumberNode", () => { default: 5, }; const node = new NumberNode(mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "integer", minimum: 1, @@ -88,7 +88,7 @@ describe("NumberNode", () => { default: 5.5, }; const node = new NumberNode(mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "double", minimum: 1.5, diff --git a/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts index b3768dea08..d5133f8b10 100644 --- a/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/primitives/number/float.node.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { ApiNodeContext } from "../../../../../openapi/base.node.interface"; +import { ApiNodeContext } from "../../../../../openapi/ApiNode"; import { FloatNode } from "../../../../../openapi/shared/nodes/primitives/number/float.node"; import { createMockContext } from "../../../../createMockContext.util"; @@ -14,7 +14,7 @@ describe("FloatNode", () => { const input = { type: "number", format: "float" }; const node = new FloatNode(mockContext, input, []); expect(node.type).toBe("double"); - expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(node.toFdrShape()).toEqual({ type: "double" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -22,7 +22,7 @@ describe("FloatNode", () => { const input = { type: "number", format: "double" }; const node = new FloatNode(mockContext, input, []); expect(node.type).toBe("double"); - expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(node.toFdrShape()).toEqual({ type: "double" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -30,7 +30,7 @@ describe("FloatNode", () => { const input = { type: "number" }; const node = new FloatNode(mockContext, input, []); expect(node.type).toBe("double"); - expect(node.outputFdrShape()).toEqual({ type: "double" }); + expect(node.toFdrShape()).toEqual({ type: "double" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -38,7 +38,7 @@ describe("FloatNode", () => { const input = { type: "string" }; const node = new FloatNode(mockContext, input, []); expect(node.type).toBeUndefined(); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( 'Expected type "number" for numerical primitive, but got "string"', [], @@ -50,7 +50,7 @@ describe("FloatNode", () => { const input = { type: "number", format: "invalid" }; const node = new FloatNode(mockContext as ApiNodeContext, input, []); expect(node.type).toBeUndefined(); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( 'Expected format for number primitive, but got "invalid"', [], diff --git a/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts index d338ddc07e..27c7fc49eb 100644 --- a/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/primitives/number/integer.node.test.ts @@ -13,7 +13,7 @@ describe("IntegerNode", () => { const input = { type: "integer", format: "int32" }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("integer"); - expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(node.toFdrShape()).toEqual({ type: "integer" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -21,7 +21,7 @@ describe("IntegerNode", () => { const input = { type: "integer", format: "int64" }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("long"); - expect(node.outputFdrShape()).toEqual({ type: "long" }); + expect(node.toFdrShape()).toEqual({ type: "long" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -29,7 +29,7 @@ describe("IntegerNode", () => { const input = { type: "integer" }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("integer"); - expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(node.toFdrShape()).toEqual({ type: "integer" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); @@ -37,7 +37,7 @@ describe("IntegerNode", () => { const input = { type: "string" }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("integer"); // Default value - expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(node.toFdrShape()).toEqual({ type: "integer" }); expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( 'Expected type "integer" for numerical primitive, but got "string"', [], @@ -49,7 +49,7 @@ describe("IntegerNode", () => { const input = { type: "integer", format: "invalid" }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("integer"); // Default value - expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(node.toFdrShape()).toEqual({ type: "integer" }); expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( 'Expected format for integer primitive, but got "invalid"', [], @@ -63,7 +63,7 @@ describe("IntegerNode", () => { const input = { type: "integer", format }; const node = new IntegerNode(mockContext, input, []); expect(node.type).toBe("integer"); - expect(node.outputFdrShape()).toEqual({ type: "integer" }); + expect(node.toFdrShape()).toEqual({ type: "integer" }); expect(mockContext.errorCollector.addError).not.toHaveBeenCalled(); }); }); diff --git a/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts b/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts index 76b26066f9..12c3a015b8 100644 --- a/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/primitives/string.node.test.ts @@ -77,11 +77,11 @@ describe("StringNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should return undefined when type is undefined", () => { const node = new StringNode(mockContext, { type: "string" }, []); node.type = undefined; - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); it("should return complete shape with all properties", () => { @@ -98,7 +98,7 @@ describe("StringNode", () => { [], ); - expect(node.outputFdrShape()).toEqual({ + expect(node.toFdrShape()).toEqual({ type: "datetime", regex: "^test$", default: "default", diff --git a/packages/parsers/__test__/shared/nodes/schema.test.ts b/packages/parsers/__test__/shared/nodes/schema.test.ts index eb52be1300..76926bf2ec 100644 --- a/packages/parsers/__test__/shared/nodes/schema.test.ts +++ b/packages/parsers/__test__/shared/nodes/schema.test.ts @@ -24,7 +24,7 @@ describe("SchemaNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should output complete shape", () => { const input: SchemaObject = { type: "string", @@ -32,7 +32,7 @@ describe("SchemaNode", () => { }; const node = new SchemaNode("TestType", mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toBeDefined(); expect(shape?.name).toBe("TestType"); @@ -46,9 +46,9 @@ describe("SchemaNode", () => { }; const node = new SchemaNode("TestType", mockContext, input, []); - vi.spyOn(node.shape, "outputFdrShape").mockReturnValue(undefined); + vi.spyOn(node.shape, "toFdrShape").mockReturnValue(undefined); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); // this should show up, but since the examples are terse and non-exhaustive, we do not have any validation checking // expect(mockContext.errorCollector.addError).toHaveBeenCalledWith( // "Failed to generate shape for type TestType", diff --git a/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts b/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts index 9887350d86..88516fd074 100644 --- a/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/typeReference.node.test.ts @@ -1,9 +1,9 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { createMockContext } from "../../../__test__/createMockContext.util"; +import { FdrStringType } from "../../../fdr/fdr.types"; import { NumberNode } from "../../../openapi/shared/nodes/primitives/number.node"; import { StringNode } from "../../../openapi/shared/nodes/primitives/string.node"; -import { FdrStringType } from "../../../openapi/shared/nodes/primitives/types/fdr.types"; import { TypeReferenceNode, isReferenceObject } from "../../../openapi/shared/nodes/typeReference.node"; import { ReferenceObject, SchemaObject } from "../../../openapi/shared/openapi.types"; @@ -79,14 +79,14 @@ describe("TypeReferenceNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should output reference shape", () => { const input: ReferenceObject = { $ref: "TypeA", }; const node = new TypeReferenceNode(mockContext, input, []); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "id", @@ -108,9 +108,9 @@ describe("TypeReferenceNode", () => { maxLength: undefined, default: undefined, }; - vi.spyOn(node.typeNode as StringNode, "outputFdrShape").mockReturnValue(mockPrimitiveShape); + vi.spyOn(node.typeNode as StringNode, "toFdrShape").mockReturnValue(mockPrimitiveShape); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "primitive", @@ -122,7 +122,7 @@ describe("TypeReferenceNode", () => { const node = new TypeReferenceNode(mockContext, { $ref: "TypeA" }, []); node.ref = undefined; - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); it("should return undefined if primitive shape is undefined", () => { @@ -131,9 +131,9 @@ describe("TypeReferenceNode", () => { }; const node = new TypeReferenceNode(mockContext, input, []); - vi.spyOn(node.typeNode as StringNode, "outputFdrShape").mockReturnValue(undefined); + vi.spyOn(node.typeNode as StringNode, "toFdrShape").mockReturnValue(undefined); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); }); }); diff --git a/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts b/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts index f338055ee5..4456f44218 100644 --- a/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts +++ b/packages/parsers/__test__/shared/nodes/typeShape.node.test.ts @@ -38,7 +38,7 @@ describe("TypeShapeNode", () => { }); }); - describe("outputFdrShape", () => { + describe("toFdrShape", () => { it("should output object shape", () => { const input: SchemaObject = { type: "object", @@ -51,9 +51,9 @@ describe("TypeShapeNode", () => { extends: [], extraProperties: undefined, }; - vi.spyOn(node.typeNode as ObjectNode, "outputFdrShape").mockReturnValue(mockObjectShape); + vi.spyOn(node.typeNode as ObjectNode, "toFdrShape").mockReturnValue(mockObjectShape); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "object", @@ -77,9 +77,9 @@ describe("TypeShapeNode", () => { default: undefined, }, }; - vi.spyOn(node.typeNode as TypeReferenceNode, "outputFdrShape").mockReturnValue(mockTypeReference); + vi.spyOn(node.typeNode as TypeReferenceNode, "toFdrShape").mockReturnValue(mockTypeReference); - const shape = node.outputFdrShape(); + const shape = node.toFdrShape(); expect(shape).toEqual({ type: "alias", @@ -94,9 +94,9 @@ describe("TypeShapeNode", () => { }; const node = new TypeShapeNode(mockContext, input, []); - vi.spyOn(node.typeNode as ObjectNode, "outputFdrShape").mockReturnValue(undefined); + vi.spyOn(node.typeNode as ObjectNode, "toFdrShape").mockReturnValue(undefined); - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); it("should return undefined if type is undefined", () => { @@ -108,7 +108,7 @@ describe("TypeShapeNode", () => { const node = new TypeShapeNode(mockContext, input, []); node.type = undefined; - expect(node.outputFdrShape()).toBeUndefined(); + expect(node.toFdrShape()).toBeUndefined(); }); }); }); diff --git a/packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts b/packages/parsers/fdr/fdr.types.ts similarity index 100% rename from packages/parsers/openapi/shared/nodes/primitives/types/fdr.types.ts rename to packages/parsers/fdr/fdr.types.ts diff --git a/packages/parsers/openapi/3.1/openApi3_1.node.ts b/packages/parsers/openapi/3.1/openApi3_1.node.ts index 6b6f8cb914..070f1ba3f3 100644 --- a/packages/parsers/openapi/3.1/openApi3_1.node.ts +++ b/packages/parsers/openapi/3.1/openApi3_1.node.ts @@ -19,30 +19,30 @@ // accessPath: ApiNode[]; // validate -// outputFdrShape = (): FdrAPI.api.latest.ApiDefinition => { +// toFdrShape = (): FdrAPI.api.latest.ApiDefinition => { // const { endpoints, websockets, webhooks } = new FdrApiStage( // this.context, // this.preProcessedInput.paths, // this.accessPath, -// ).outputFdrShape(); +// ).toFdrShape(); // const types = new FdrTypesStage( // this.context, // this.preProcessedInput.components?.schemas, // this.accessPath, -// ).outputFdrShape(); +// ).toFdrShape(); // const auths = new FdrAuthsStage( // this.context, // this.preProcessedInput.components?.securitySchemes, // this.accessPath, -// ).outputFdrShape(); +// ).toFdrShape(); // const globalHeaders = new FdrGlobalHeadersStage( // this.context, // this.preProcessedInput.components?.headers, // this.accessPath, -// ).outputFdrShape(); +// ).toFdrShape(); // return { // id: v4(), diff --git a/packages/parsers/openapi/base.node.interface.ts b/packages/parsers/openapi/ApiNode.ts similarity index 72% rename from packages/parsers/openapi/base.node.interface.ts rename to packages/parsers/openapi/ApiNode.ts index 129b741a87..c020d06cd8 100644 --- a/packages/parsers/openapi/base.node.interface.ts +++ b/packages/parsers/openapi/ApiNode.ts @@ -1,18 +1,5 @@ import { Logger } from "@playwright/test"; - -export class ErrorCollector { - errors: { - message: string; - path: string; - }[] = []; - - addError(error: string, accessPath: string[], pathId?: string): void { - this.errors.push({ - message: error, - path: `#/${accessPath.join("/")}${pathId ? `/${pathId}` : ""}`, - }); - } -} +import { ErrorCollector } from "./ErrorCollector"; export interface ApiNodeContext { orgId: string; @@ -33,7 +20,7 @@ abstract class ApiNode { this.accessPath = accessPath; } - abstract outputFdrShape: () => FdrShape | undefined; + abstract toFdrShape: () => FdrShape | undefined; } export abstract class InputApiNode extends ApiNode { diff --git a/packages/parsers/openapi/ErrorCollector.ts b/packages/parsers/openapi/ErrorCollector.ts new file mode 100644 index 0000000000..a450d597cd --- /dev/null +++ b/packages/parsers/openapi/ErrorCollector.ts @@ -0,0 +1,14 @@ +type ValidationError = { + message: string; + path: string[]; +}; +export class ErrorCollector { + errors: ValidationError[] = []; + + addError(error: string, accessPath: string[]): void { + this.errors.push({ + message: error, + path: accessPath, + }); + } +} diff --git a/packages/parsers/openapi/shared/nodes/object.node.ts b/packages/parsers/openapi/shared/nodes/object.node.ts index 52a6ee2756..54acf21fc7 100644 --- a/packages/parsers/openapi/shared/nodes/object.node.ts +++ b/packages/parsers/openapi/shared/nodes/object.node.ts @@ -1,5 +1,6 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { isNonNullish } from "@fern-api/ui-core-utils"; +import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; import { SchemaObject } from "../openapi.types"; import { ObjectPropertyNode } from "./objectProperty.node"; import { TypeReferenceNode, isReferenceObject } from "./typeReference.node"; @@ -12,23 +13,21 @@ export class ObjectNode extends OutputApiNode (isReferenceObject(type) ? FdrAPI.TypeId(type.$ref) : undefined)) - .filter((id): id is FdrAPI.TypeId => id !== undefined); + .filter(isNonNullish); } - if (input.properties !== undefined) { + if (input.properties != null) { Object.entries(input.properties).forEach(([key, property]) => { this.properties.push(new ObjectPropertyNode(key, context, property, accessPath, accessorKey)); }); } } - outputFdrShape = (): FdrAPI.api.latest.ObjectType | undefined => { - const properties = this.properties - .map((property) => property.outputFdrShape()) - .filter((property): property is FdrAPI.api.latest.ObjectProperty => property !== undefined); + toFdrShape = (): FdrAPI.api.latest.ObjectType | undefined => { + const properties = this.properties.map((property) => property.toFdrShape()).filter(isNonNullish); return { extends: this.extends, diff --git a/packages/parsers/openapi/shared/nodes/objectProperty.node.ts b/packages/parsers/openapi/shared/nodes/objectProperty.node.ts index 4cb326cf55..876e735f68 100644 --- a/packages/parsers/openapi/shared/nodes/objectProperty.node.ts +++ b/packages/parsers/openapi/shared/nodes/objectProperty.node.ts @@ -1,5 +1,5 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, OutputApiNode } from "../../base.node.interface"; +import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; import { ReferenceObject, SchemaObject } from "../openapi.types"; import { isReferenceObject, mapReferenceObject } from "./typeReference.node"; import { TypeShapeNode } from "./typeShape.node"; @@ -30,8 +30,8 @@ export class ObjectPropertyNode extends OutputApiNode< // this.availability = input.availability; } - outputFdrShape = (): FdrAPI.api.latest.ObjectProperty | undefined => { - const valueShape = this.valueShape.outputFdrShape(); + toFdrShape = (): FdrAPI.api.latest.ObjectProperty | undefined => { + const valueShape = this.valueShape.toFdrShape(); if (valueShape === undefined) { return undefined; } @@ -41,7 +41,7 @@ export class ObjectPropertyNode extends OutputApiNode< valueShape, description: this.description, availability: undefined, - // TODO: update to availability: this.availability.outputFdrShape(), + // TODO: update to availability: this.availability.toFdrShape(), }; }; } diff --git a/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts b/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts index f7862e850e..b068f0ee4d 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts @@ -1,6 +1,6 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; import { OpenAPIV3_1 } from "openapi-types"; -import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; +import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; export class EnumNode extends InputApiNode { default: number | undefined; @@ -19,7 +19,6 @@ export class EnumNode extends InputApiNode { + toFdrShape = (): FdrAPI.api.v1.read.PrimitiveType.Integer => { return { type: "integer", minimum: this.minimum, diff --git a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts index f285cba694..0908a231a9 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts @@ -1,33 +1,32 @@ -import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; +import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; import { SchemaObject } from "../../openapi.types"; import { FloatNode } from "./number/float.node"; import { IntegerNode } from "./number/integer.node"; -import { FdrFloatType, FdrIntegerType } from "./types/fdr.types"; +import { FdrFloatType, FdrIntegerType } from "../../../../fdr/fdr.types"; export class NumberNode extends InputApiNode { typeNode: IntegerNode | FloatNode | undefined; default: number | undefined; minimum: number | undefined; maximum: number | undefined; - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { super(context, input, accessPath); if (input.type !== "integer" && input.type !== "number") { context.errorCollector.addError( `Expected type "integer" or "number" for numerical primitive, but got "${input.type}"`, accessPath, - accessorKey, ); return; } switch (input.type) { case "integer": - this.typeNode = new IntegerNode(context, input, accessPath, accessorKey); + this.typeNode = new IntegerNode(context, input, accessPath); break; case "number": - this.typeNode = new FloatNode(context, input, accessPath, accessorKey); + this.typeNode = new FloatNode(context, input, accessPath); break; } @@ -36,8 +35,8 @@ export class NumberNode extends InputApiNode { - const typeProperties = this.typeNode?.outputFdrShape(); + toFdrShape = (): FdrFloatType | FdrIntegerType | undefined => { + const typeProperties = this.typeNode?.toFdrShape(); if (typeProperties === undefined) { return undefined; diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts index 2306be3914..c80b4fe08b 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts @@ -1,7 +1,7 @@ import { UnreachableCaseError } from "ts-essentials"; -import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; +import { FdrFloatType } from "../../../../../fdr/fdr.types"; +import { ApiNodeContext, OutputApiNode } from "../../../../ApiNode"; import { SchemaObject } from "../../../openapi.types"; -import { FdrFloatType } from "../types/fdr.types"; import { ConstArrayToType, OPENAPI_NUMBER_TYPE_FORMAT } from "../types/format.types"; function isOpenApiNumberTypeFormat(format: unknown): format is ConstArrayToType { @@ -11,14 +11,13 @@ function isOpenApiNumberTypeFormat(format: unknown): format is ConstArrayToType< export class FloatNode extends OutputApiNode> { type: FdrFloatType["type"] | undefined = undefined; - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { super(context, input, accessPath); if (input.type !== "number") { context.errorCollector.addError( `Expected type "number" for numerical primitive, but got "${input.type}"`, accessPath, - accessorKey, ); return; } @@ -27,7 +26,6 @@ export class FloatNode extends OutputApiNode | undefined => { + toFdrShape = (): Pick | undefined => { if (this.type === undefined) { return undefined; } diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts index 0235bd80cc..2b57c8e737 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts @@ -1,7 +1,7 @@ import { UnreachableCaseError } from "ts-essentials"; -import { ApiNodeContext, OutputApiNode } from "../../../../base.node.interface"; +import { FdrIntegerType } from "../../../../../fdr/fdr.types"; +import { ApiNodeContext, OutputApiNode } from "../../../../ApiNode"; import { SchemaObject } from "../../../openapi.types"; -import { FdrIntegerType } from "../types/fdr.types"; import { ConstArrayToType, OPENAPI_INTEGER_TYPE_FORMAT } from "../types/format.types"; function isOpenApiIntegerTypeFormat(format: unknown): format is ConstArrayToType { @@ -11,14 +11,13 @@ function isOpenApiIntegerTypeFormat(format: unknown): format is ConstArrayToType export class IntegerNode extends OutputApiNode> { type: FdrIntegerType["type"] = "integer"; - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { + constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { super(context, input, accessPath); if (input.type !== "integer") { context.errorCollector.addError( `Expected type "integer" for numerical primitive, but got "${input.type}"`, accessPath, - accessorKey, ); return; } @@ -27,7 +26,6 @@ export class IntegerNode extends OutputApiNode => { + toFdrShape = (): Pick => { return { type: this.type, }; diff --git a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts index 9866af3a3d..37ce06f2ca 100644 --- a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts +++ b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts @@ -1,7 +1,7 @@ import { UnreachableCaseError } from "ts-essentials"; -import { ApiNodeContext, InputApiNode } from "../../../base.node.interface"; +import { FdrStringType } from "../../../../fdr/fdr.types"; +import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; import { SchemaObject } from "../../openapi.types"; -import { FdrStringType } from "./types/fdr.types"; import { ConstArrayToType, OPENAPI_STRING_TYPE_FORMAT } from "./types/format.types"; export class StringNode extends InputApiNode { @@ -67,7 +67,6 @@ export class StringNode extends InputApiNode { context.errorCollector.addError( `Expected type "string" for primitive, but got "${input.type}"`, accessPath, - accessorKey, ); return; } @@ -75,11 +74,7 @@ export class StringNode extends InputApiNode { this.type = this.mapToFdrType(input.format); if (this.type === undefined) { - context.errorCollector.addError( - `Expected proper "string" format, but got "${input.format}"`, - accessPath, - accessorKey, - ); + context.errorCollector.addError(`Expected proper "string" format, but got "${input.format}"`, accessPath); return; } @@ -89,7 +84,7 @@ export class StringNode extends InputApiNode { this.maxLength = input.maxLength; } - outputFdrShape = (): FdrStringType | undefined => { + toFdrShape = (): FdrStringType | undefined => { if (this.type === undefined) { return undefined; } diff --git a/packages/parsers/openapi/shared/nodes/schema.node.ts b/packages/parsers/openapi/shared/nodes/schema.node.ts index d6baea8a1e..1f5e71fc9a 100644 --- a/packages/parsers/openapi/shared/nodes/schema.node.ts +++ b/packages/parsers/openapi/shared/nodes/schema.node.ts @@ -1,5 +1,5 @@ import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, InputApiNode } from "../../base.node.interface"; +import { ApiNodeContext, InputApiNode } from "../../ApiNode"; import { SchemaObject } from "../openapi.types"; import { TypeShapeNode } from "./typeShape.node"; @@ -21,8 +21,8 @@ export class SchemaNode extends InputApiNode { - const typeShape = this.shape.outputFdrShape(); + toFdrShape = (): FdrAPI.api.latest.TypeDefinition | undefined => { + const typeShape = this.shape.toFdrShape(); if (typeShape === undefined) { return undefined; } @@ -32,7 +32,7 @@ export class SchemaNode extends InputApiNode { - return typeof input === "object" && input != null && "$ref" in input && typeof input.$ref === "string"; + return typeof input === "object" && isNonNullish(input) && "$ref" in input && typeof input.$ref === "string"; }; export const mapReferenceObject = (referenceObject: ReferenceObject): SchemaObject => { return referenceObject.$ref as SchemaObject; @@ -36,7 +37,7 @@ export class TypeReferenceNode extends OutputApiNode { + toFdrShape = (): FdrAPI.api.latest.TypeReference | undefined => { if (this.type === "id") { if (this.ref === undefined) { return undefined; @@ -48,7 +49,7 @@ export class TypeReferenceNode extends OutputApiNode { - const typeShape = this.typeNode?.outputFdrShape(); + toFdrShape = (): FdrAPI.api.latest.TypeShape | undefined => { + const typeShape = this.typeNode?.toFdrShape(); if (typeShape === undefined || this.type === undefined) { return undefined; } diff --git a/packages/parsers/package.json b/packages/parsers/package.json index 399ffb615f..bf82959236 100644 --- a/packages/parsers/package.json +++ b/packages/parsers/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "@fern-api/fdr-sdk": "workspace:*", + "@fern-api/ui-core-utils": "workspace:*", "@playwright/test": "^1.47.1", "openapi-types": "^12.1.3", "ts-essentials": "^10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4129a3ce81..65ff2fcdad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -970,6 +970,9 @@ importers: '@fern-api/fdr-sdk': specifier: workspace:* version: link:../fdr-sdk + '@fern-api/ui-core-utils': + specifier: workspace:* + version: link:../commons/core-utils '@playwright/test': specifier: ^1.47.1 version: 1.47.1 From f467f83531953ca5a5db5c3e3a4b0d99e7da201f Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Fri, 15 Nov 2024 15:40:48 -0500 Subject: [PATCH 11/14] remove zod for now --- packages/parsers/package.json | 3 +-- pnpm-lock.yaml | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/parsers/package.json b/packages/parsers/package.json index bf82959236..b3a1de9097 100644 --- a/packages/parsers/package.json +++ b/packages/parsers/package.json @@ -29,7 +29,6 @@ "@playwright/test": "^1.47.1", "openapi-types": "^12.1.3", "ts-essentials": "^10.0.1", - "vitest": "^2.1.4", - "zod": "^3.23.8" + "vitest": "^2.1.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65ff2fcdad..02f6cf5dfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -985,9 +985,6 @@ importers: vitest: specifier: ^2.1.4 version: 2.1.4(@edge-runtime/vm@3.2.0)(@types/node@22.5.5)(jsdom@24.0.0)(less@4.2.0)(sass@1.77.0)(stylus@0.62.0)(terser@5.31.0) - zod: - specifier: ^3.23.8 - version: 3.23.8 packages/scripts: dependencies: From 792096dc74b97a1335099a60c7f705c5b0207fa5 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Fri, 15 Nov 2024 15:55:08 -0500 Subject: [PATCH 12/14] update logger path --- .../__test__/createMockContext.util.ts | 4 ++-- packages/parsers/openapi/ApiNode.ts | 4 ++-- packages/parsers/package.json | 1 + pnpm-lock.yaml | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/parsers/__test__/createMockContext.util.ts b/packages/parsers/__test__/createMockContext.util.ts index e22e828457..b32a24b5e7 100644 --- a/packages/parsers/__test__/createMockContext.util.ts +++ b/packages/parsers/__test__/createMockContext.util.ts @@ -1,13 +1,13 @@ import { vi } from "vitest"; import { ApiNodeContext } from "../openapi/ApiNode"; -import { Logger } from "@playwright/test"; +import { createLogger } from "@fern-api/logger"; export function createMockContext(): ApiNodeContext { return { orgId: "orgId", apiId: "apiId", - logger: {} as Logger, + logger: createLogger(() => undefined), errorCollector: { addError: vi.fn(), errors: [], diff --git a/packages/parsers/openapi/ApiNode.ts b/packages/parsers/openapi/ApiNode.ts index c020d06cd8..729e46ca47 100644 --- a/packages/parsers/openapi/ApiNode.ts +++ b/packages/parsers/openapi/ApiNode.ts @@ -1,4 +1,4 @@ -import { Logger } from "@playwright/test"; +import { Logger } from "@fern-api/logger"; import { ErrorCollector } from "./ErrorCollector"; export interface ApiNodeContext { @@ -27,7 +27,7 @@ export abstract class InputApiNode extends ApiNode=18.17.0'} @@ -18614,12 +18623,22 @@ snapshots: strip-ansi: 7.1.0 ua-parser-js: 1.0.37 + '@fern-api/core-utils@0.4.24-rc1': + dependencies: + lodash-es: 4.17.21 + strip-ansi: 7.1.0 + '@fern-api/fs-utils@0.15.0-rc63': dependencies: '@fern-api/core-utils': 0.15.0-rc63 json-stream-stringify: 3.1.4 tmp-promise: 3.0.3 + '@fern-api/logger@0.4.24-rc1': + dependencies: + '@fern-api/core-utils': 0.4.24-rc1 + chalk: 5.3.0 + '@fern-api/next@14.2.9-fork.2(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.0)': dependencies: '@next/env': 14.2.9 From c3768baaf3495ebee2a33c1c04e6e96b79b826b6 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Fri, 15 Nov 2024 15:55:49 -0500 Subject: [PATCH 13/14] remove unused dep --- packages/parsers/package.json | 1 - pnpm-lock.yaml | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/parsers/package.json b/packages/parsers/package.json index 92d07b4d67..eae1e9950f 100644 --- a/packages/parsers/package.json +++ b/packages/parsers/package.json @@ -27,7 +27,6 @@ "@fern-api/fdr-sdk": "workspace:*", "@fern-api/logger": "0.4.24-rc1", "@fern-api/ui-core-utils": "workspace:*", - "@playwright/test": "^1.47.1", "openapi-types": "^12.1.3", "ts-essentials": "^10.0.1", "vitest": "^2.1.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 246df37459..d5cd4549bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -976,9 +976,6 @@ importers: '@fern-api/ui-core-utils': specifier: workspace:* version: link:../commons/core-utils - '@playwright/test': - specifier: ^1.47.1 - version: 1.47.1 openapi-types: specifier: ^12.1.3 version: 12.1.3 @@ -19286,7 +19283,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} @@ -19305,7 +19302,7 @@ snapshots: '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping@0.3.9': dependencies: From ce459e49ed2c56e047747f2201def136ab53538f Mon Sep 17 00:00:00 2001 From: dsinghvi Date: Sun, 17 Nov 2024 10:30:36 -0500 Subject: [PATCH 14/14] raw thoughts on openapi parser --- packages/parsers/BaseAPIConverterNode.ts | 31 ++++++ packages/parsers/ErrorCollector.ts | 35 ++++++ packages/parsers/fdr/fdr.types.ts | 15 --- .../openapi/3.1/OpenAPIV3_1ConverterNode.ts | 28 +++++ .../parsers/openapi/3.1/openApi3_1.node.ts | 58 ---------- .../parsers/openapi/3.1/schemas/ArrayNode.ts | 0 .../parsers/openapi/3.1/schemas/EnumNode.ts | 0 .../parsers/openapi/3.1/schemas/ObjectNode.ts | 25 +++++ .../openapi/3.1/schemas/PrimitiveNode.ts | 0 .../parsers/openapi/3.1/schemas/SchemaNode.ts | 17 +++ packages/parsers/openapi/ApiNode.ts | 36 ------- packages/parsers/openapi/ErrorCollector.ts | 14 --- .../openapi/shared/nodes/object.node.ts | 39 ------- .../shared/nodes/objectProperty.node.ts | 47 -------- .../shared/nodes/primitives/enum.node.ts | 38 ------- .../shared/nodes/primitives/number.node.ts | 52 --------- .../nodes/primitives/number/float.node.ts | 57 ---------- .../nodes/primitives/number/integer.node.ts | 56 ---------- .../shared/nodes/primitives/string.node.ts | 100 ------------------ .../nodes/primitives/types/format.types.ts | 60 ----------- .../openapi/shared/nodes/schema.node.ts | 38 ------- .../shared/nodes/typeReference.node.ts | 63 ----------- .../openapi/shared/nodes/typeShape.node.ts | 47 -------- .../parsers/openapi/shared/openapi.types.ts | 3 - 24 files changed, 136 insertions(+), 723 deletions(-) create mode 100644 packages/parsers/BaseAPIConverterNode.ts create mode 100644 packages/parsers/ErrorCollector.ts delete mode 100644 packages/parsers/fdr/fdr.types.ts create mode 100644 packages/parsers/openapi/3.1/OpenAPIV3_1ConverterNode.ts delete mode 100644 packages/parsers/openapi/3.1/openApi3_1.node.ts create mode 100644 packages/parsers/openapi/3.1/schemas/ArrayNode.ts create mode 100644 packages/parsers/openapi/3.1/schemas/EnumNode.ts create mode 100644 packages/parsers/openapi/3.1/schemas/ObjectNode.ts create mode 100644 packages/parsers/openapi/3.1/schemas/PrimitiveNode.ts create mode 100644 packages/parsers/openapi/3.1/schemas/SchemaNode.ts delete mode 100644 packages/parsers/openapi/ApiNode.ts delete mode 100644 packages/parsers/openapi/ErrorCollector.ts delete mode 100644 packages/parsers/openapi/shared/nodes/object.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/objectProperty.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/enum.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/number.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/string.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts delete mode 100644 packages/parsers/openapi/shared/nodes/schema.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/typeReference.node.ts delete mode 100644 packages/parsers/openapi/shared/nodes/typeShape.node.ts delete mode 100644 packages/parsers/openapi/shared/openapi.types.ts diff --git a/packages/parsers/BaseAPIConverterNode.ts b/packages/parsers/BaseAPIConverterNode.ts new file mode 100644 index 0000000000..e56330232d --- /dev/null +++ b/packages/parsers/BaseAPIConverterNode.ts @@ -0,0 +1,31 @@ +import { ErrorCollector } from "./ErrorCollector"; + +/** + * Base context class for API converter nodes. + * Provides logging and error collection capabilities. + */ +export abstract class BaseAPIConverterNodeContext { + public readonly logger: Console = console; + public readonly errors: ErrorCollector = new ErrorCollector(); +} + +/** + * APIConverterNode is responsible for converting API concepts between different API definition formats. + * It takes an input from one API definition format and transforms it into an equivalent output + * in another format. For example, it can convert an OpenAPI operation into an FDR endpoint definition, + * preserving the semantic meaning while adapting to the target format's structure. + * + * @typeparam Input - The type from the source format + * @typeparam Output - The type from the target format + */ +export abstract class BaseAPIConverterNode { + constructor( + protected readonly input: Input, + protected readonly context: BaseAPIConverterNodeContext, + ) {} + + /** + * @returns The converted API definition in the target output format + */ + public abstract convert(): Output; +} diff --git a/packages/parsers/ErrorCollector.ts b/packages/parsers/ErrorCollector.ts new file mode 100644 index 0000000000..6958cabb3f --- /dev/null +++ b/packages/parsers/ErrorCollector.ts @@ -0,0 +1,35 @@ +export declare namespace ErrorCollector { + interface ValidationError { + message: string; + path: string[]; + } + + interface ValidationWarning { + message: string; + path: string[]; + } +} + +/** + * ErrorCollector is used to collect validation errors and warnings during parsing. + * It provides methods to track both blocking errors and non-blocking warnings. + */ +export class ErrorCollector { + public readonly warnings: ErrorCollector.ValidationWarning[] = []; + public readonly errors: ErrorCollector.ValidationError[] = []; + + /** + * An error will block parsing + * @param error + */ + public error(error: ErrorCollector.ValidationError): void { + this.errors.push(error); + } + + /** + * A warning will not block parsing + */ + public warning(warning: ErrorCollector.ValidationWarning): void { + this.warnings.push(warning); + } +} diff --git a/packages/parsers/fdr/fdr.types.ts b/packages/parsers/fdr/fdr.types.ts deleted file mode 100644 index 3b40386d43..0000000000 --- a/packages/parsers/fdr/fdr.types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; - -export type FdrFloatType = FdrAPI.api.v1.read.PrimitiveType.Double; -export type FdrIntegerType = - | FdrAPI.api.v1.read.PrimitiveType.Integer - | FdrAPI.api.v1.read.PrimitiveType.Long - | FdrAPI.api.v1.read.PrimitiveType.Uint - | FdrAPI.api.v1.read.PrimitiveType.Uint64; -export type FdrStringType = - | FdrAPI.api.v1.read.PrimitiveType.String - | FdrAPI.api.v1.read.PrimitiveType.BigInteger - | FdrAPI.api.v1.read.PrimitiveType.Datetime - | FdrAPI.api.v1.read.PrimitiveType.Uuid - | FdrAPI.api.v1.read.PrimitiveType.Base64 - | FdrAPI.api.v1.read.PrimitiveType.Date_; diff --git a/packages/parsers/openapi/3.1/OpenAPIV3_1ConverterNode.ts b/packages/parsers/openapi/3.1/OpenAPIV3_1ConverterNode.ts new file mode 100644 index 0000000000..01d074a444 --- /dev/null +++ b/packages/parsers/openapi/3.1/OpenAPIV3_1ConverterNode.ts @@ -0,0 +1,28 @@ +import { ApiDefinition } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { BaseAPIConverterNode, BaseAPIConverterNodeContext } from "../../BaseAPIConverterNode"; + +export abstract class OpenAPIV3_1ConverterNode extends BaseAPIConverterNode { + constructor( + protected readonly input: OpenAPIV3_1.Document, + protected readonly context: BaseAPIConverterNodeContext, + ) { + super(input, context); + } + + /** + * @returns The converted API definition in the target output format + */ + public convert(): ApiDefinition.ApiDefinition { + return { + id: ApiDefinition.ApiDefinitionId(Buffer.from(JSON.stringify(this.input)).toString("base64")), + globalHeaders: [], + auths: {}, + endpoints: {}, + websockets: {}, + webhooks: {}, + types: {}, + subpackages: {}, + }; + } +} diff --git a/packages/parsers/openapi/3.1/openApi3_1.node.ts b/packages/parsers/openapi/3.1/openApi3_1.node.ts deleted file mode 100644 index 070f1ba3f3..0000000000 --- a/packages/parsers/openapi/3.1/openApi3_1.node.ts +++ /dev/null @@ -1,58 +0,0 @@ -// import { FdrAPI } from "@fern-api/fdr-sdk"; -// import { OpenAPIV3_1 } from "openapi-types"; -// import { v4 } from "uuid"; -// import { ApiNode, ApiNodeContext } from "../shared/interfaces/api.node.interface"; -// import { FdrApiStage } from "../shared/stages/fdr/api.stage"; - -// export class OpenApi3_1Node implements ApiNode { -// name = "OpenApi3_1"; -// context: ApiNodeContext; -// qualifiedId: string = "OpenApi3_1"; - -// constructor( -// context: ApiNodeContext, -// readonly preProcessedInput: OpenAPIV3_1.Document, -// ) { -// this.context = context; -// this.accessPath = [this]; -// } -// accessPath: ApiNode[]; - -// validate -// toFdrShape = (): FdrAPI.api.latest.ApiDefinition => { -// const { endpoints, websockets, webhooks } = new FdrApiStage( -// this.context, -// this.preProcessedInput.paths, -// this.accessPath, -// ).toFdrShape(); - -// const types = new FdrTypesStage( -// this.context, -// this.preProcessedInput.components?.schemas, -// this.accessPath, -// ).toFdrShape(); - -// const auths = new FdrAuthsStage( -// this.context, -// this.preProcessedInput.components?.securitySchemes, -// this.accessPath, -// ).toFdrShape(); - -// const globalHeaders = new FdrGlobalHeadersStage( -// this.context, -// this.preProcessedInput.components?.headers, -// this.accessPath, -// ).toFdrShape(); - -// return { -// id: v4(), -// endpoints, -// websockets, -// webhooks, -// types, -// subpackages: {}, -// auths, -// globalHeaders, -// }; -// }; -// } diff --git a/packages/parsers/openapi/3.1/schemas/ArrayNode.ts b/packages/parsers/openapi/3.1/schemas/ArrayNode.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/3.1/schemas/EnumNode.ts b/packages/parsers/openapi/3.1/schemas/EnumNode.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/3.1/schemas/ObjectNode.ts b/packages/parsers/openapi/3.1/schemas/ObjectNode.ts new file mode 100644 index 0000000000..2ca9e00edf --- /dev/null +++ b/packages/parsers/openapi/3.1/schemas/ObjectNode.ts @@ -0,0 +1,25 @@ +import { ApiDefinition } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { BaseAPIConverterNode, BaseAPIConverterNodeContext } from "../../../BaseAPIConverterNode"; + +export declare namespace ObjectNode { + interface Input extends OpenAPIV3_1.NonArraySchemaObject { + type: "object"; + } +} + +export abstract class ObjectNode extends BaseAPIConverterNode { + constructor( + protected readonly input: ObjectNode.Input, + protected readonly context: BaseAPIConverterNodeContext, + ) { + super(input, context); + } + + /** + * @returns The converted API definition in the target output format + */ + public convert(): ApiDefinition.ObjectType { + + } +} diff --git a/packages/parsers/openapi/3.1/schemas/PrimitiveNode.ts b/packages/parsers/openapi/3.1/schemas/PrimitiveNode.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/parsers/openapi/3.1/schemas/SchemaNode.ts b/packages/parsers/openapi/3.1/schemas/SchemaNode.ts new file mode 100644 index 0000000000..39cec0e824 --- /dev/null +++ b/packages/parsers/openapi/3.1/schemas/SchemaNode.ts @@ -0,0 +1,17 @@ +import { ApiDefinition } from "@fern-api/fdr-sdk"; +import { OpenAPIV3_1 } from "openapi-types"; +import { BaseAPIConverterNode, BaseAPIConverterNodeContext } from "../../../BaseAPIConverterNode"; + +export abstract class SchemaNode extends BaseAPIConverterNode { + constructor( + protected readonly input: ObjectNode.Input, + protected readonly context: BaseAPIConverterNodeContext, + ) { + super(input, context); + } + + /** + * @returns The converted API definition in the target output format + */ + public convert(): ApiDefinition.ObjectType {} +} diff --git a/packages/parsers/openapi/ApiNode.ts b/packages/parsers/openapi/ApiNode.ts deleted file mode 100644 index 729e46ca47..0000000000 --- a/packages/parsers/openapi/ApiNode.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Logger } from "@fern-api/logger"; -import { ErrorCollector } from "./ErrorCollector"; - -export interface ApiNodeContext { - orgId: string; - apiId: string; - logger: Logger; - errorCollector: ErrorCollector; -} - -abstract class ApiNode { - context: ApiNodeContext; - input: InputShape; - - accessPath: string[]; - - constructor(context: ApiNodeContext, input: InputShape, accessPath: string[]) { - this.context = context; - this.input = input; - this.accessPath = accessPath; - } - - abstract toFdrShape: () => FdrShape | undefined; -} - -export abstract class InputApiNode extends ApiNode { - constructor(context: ApiNodeContext, input: InputShape, accessPath: string[], pathId?: string) { - if (pathId) { - accessPath.push(pathId); - context.logger.info(`Processing #/${accessPath.join("/")}/${pathId}`); - } - super(context, input, accessPath); - } -} - -export abstract class OutputApiNode extends ApiNode {} diff --git a/packages/parsers/openapi/ErrorCollector.ts b/packages/parsers/openapi/ErrorCollector.ts deleted file mode 100644 index a450d597cd..0000000000 --- a/packages/parsers/openapi/ErrorCollector.ts +++ /dev/null @@ -1,14 +0,0 @@ -type ValidationError = { - message: string; - path: string[]; -}; -export class ErrorCollector { - errors: ValidationError[] = []; - - addError(error: string, accessPath: string[]): void { - this.errors.push({ - message: error, - path: accessPath, - }); - } -} diff --git a/packages/parsers/openapi/shared/nodes/object.node.ts b/packages/parsers/openapi/shared/nodes/object.node.ts deleted file mode 100644 index 54acf21fc7..0000000000 --- a/packages/parsers/openapi/shared/nodes/object.node.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { isNonNullish } from "@fern-api/ui-core-utils"; -import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; -import { SchemaObject } from "../openapi.types"; -import { ObjectPropertyNode } from "./objectProperty.node"; -import { TypeReferenceNode, isReferenceObject } from "./typeReference.node"; - -export class ObjectNode extends OutputApiNode { - extends: FdrAPI.TypeId[] = []; - properties: ObjectPropertyNode[] = []; - extraProperties: TypeReferenceNode | undefined; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { - super(context, input, accessPath); - - if (input.allOf != null) { - this.extends = input.allOf - .map((type) => (isReferenceObject(type) ? FdrAPI.TypeId(type.$ref) : undefined)) - .filter(isNonNullish); - } - - if (input.properties != null) { - Object.entries(input.properties).forEach(([key, property]) => { - this.properties.push(new ObjectPropertyNode(key, context, property, accessPath, accessorKey)); - }); - } - } - - toFdrShape = (): FdrAPI.api.latest.ObjectType | undefined => { - const properties = this.properties.map((property) => property.toFdrShape()).filter(isNonNullish); - - return { - extends: this.extends, - properties, - extraProperties: undefined, - // TODO: add extraProperties - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/objectProperty.node.ts b/packages/parsers/openapi/shared/nodes/objectProperty.node.ts deleted file mode 100644 index 876e735f68..0000000000 --- a/packages/parsers/openapi/shared/nodes/objectProperty.node.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; -import { ReferenceObject, SchemaObject } from "../openapi.types"; -import { isReferenceObject, mapReferenceObject } from "./typeReference.node"; -import { TypeShapeNode } from "./typeShape.node"; - -export class ObjectPropertyNode extends OutputApiNode< - SchemaObject | ReferenceObject, - FdrAPI.api.latest.ObjectProperty -> { - valueShape: TypeShapeNode; - description: string | undefined; - // availability: AvailabilityNode; - - constructor( - private readonly key: string, - context: ApiNodeContext, - input: SchemaObject | ReferenceObject, - accessPath: string[], - accessorKey?: string, - ) { - super(context, input, accessPath); - - if (isReferenceObject(input)) { - input = mapReferenceObject(input); - } - - this.valueShape = new TypeShapeNode(context, input, accessPath, accessorKey); - this.description = input.description; - // this.availability = input.availability; - } - - toFdrShape = (): FdrAPI.api.latest.ObjectProperty | undefined => { - const valueShape = this.valueShape.toFdrShape(); - if (valueShape === undefined) { - return undefined; - } - - return { - key: FdrAPI.PropertyKey(this.key), - valueShape, - description: this.description, - availability: undefined, - // TODO: update to availability: this.availability.toFdrShape(), - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts b/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts deleted file mode 100644 index b068f0ee4d..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/enum.node.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { OpenAPIV3_1 } from "openapi-types"; -import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; - -export class EnumNode extends InputApiNode { - default: number | undefined; - format: string | undefined; - minimum: number | undefined; - maximum: number | undefined; - - constructor( - context: ApiNodeContext, - input: OpenAPIV3_1.NonArraySchemaObject, - accessPath: string[], - accessorKey?: string, - ) { - super(context, input, accessPath, accessorKey); - if (input.type !== "integer") { - context.errorCollector.addError( - `Expected type "integer" for primitive, but got "${input.type}"`, - accessPath, - ); - } - this.format = input.format; - this.default = input.default; - this.minimum = input.minimum; - this.maximum = input.maximum; - } - - toFdrShape = (): FdrAPI.api.v1.read.PrimitiveType.Integer => { - return { - type: "integer", - minimum: this.minimum, - maximum: this.maximum, - default: this.default, - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number.node.ts deleted file mode 100644 index 0908a231a9..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/number.node.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; -import { SchemaObject } from "../../openapi.types"; -import { FloatNode } from "./number/float.node"; -import { IntegerNode } from "./number/integer.node"; - -import { FdrFloatType, FdrIntegerType } from "../../../../fdr/fdr.types"; -export class NumberNode extends InputApiNode { - typeNode: IntegerNode | FloatNode | undefined; - default: number | undefined; - minimum: number | undefined; - maximum: number | undefined; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { - super(context, input, accessPath); - - if (input.type !== "integer" && input.type !== "number") { - context.errorCollector.addError( - `Expected type "integer" or "number" for numerical primitive, but got "${input.type}"`, - accessPath, - ); - return; - } - - switch (input.type) { - case "integer": - this.typeNode = new IntegerNode(context, input, accessPath); - break; - case "number": - this.typeNode = new FloatNode(context, input, accessPath); - break; - } - - this.default = input.default; - this.minimum = input.minimum; - this.maximum = input.maximum; - } - - toFdrShape = (): FdrFloatType | FdrIntegerType | undefined => { - const typeProperties = this.typeNode?.toFdrShape(); - - if (typeProperties === undefined) { - return undefined; - } - - return { - ...typeProperties, - minimum: this.minimum, - maximum: this.maximum, - default: this.default, - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts deleted file mode 100644 index c80b4fe08b..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/number/float.node.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { UnreachableCaseError } from "ts-essentials"; -import { FdrFloatType } from "../../../../../fdr/fdr.types"; -import { ApiNodeContext, OutputApiNode } from "../../../../ApiNode"; -import { SchemaObject } from "../../../openapi.types"; -import { ConstArrayToType, OPENAPI_NUMBER_TYPE_FORMAT } from "../types/format.types"; - -function isOpenApiNumberTypeFormat(format: unknown): format is ConstArrayToType { - return OPENAPI_NUMBER_TYPE_FORMAT.includes(format as ConstArrayToType); -} - -export class FloatNode extends OutputApiNode> { - type: FdrFloatType["type"] | undefined = undefined; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { - super(context, input, accessPath); - - if (input.type !== "number") { - context.errorCollector.addError( - `Expected type "number" for numerical primitive, but got "${input.type}"`, - accessPath, - ); - return; - } - - if (!isOpenApiNumberTypeFormat(input.format)) { - context.errorCollector.addError( - `Expected format for number primitive, but got "${input.format}"`, - accessPath, - ); - return; - } - - switch (input.format) { - case "decimal": - case "decimal128": - case "double-int": - case "double": - case "float": - case "sf-decimal": - case undefined: - this.type = "double"; - break; - default: - new UnreachableCaseError(input.format); - } - } - - // In this, we pick only the non-shared types, so that we can push up the shared types to the parent for maximum reuse - toFdrShape = (): Pick | undefined => { - if (this.type === undefined) { - return undefined; - } - return { - type: this.type, - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts b/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts deleted file mode 100644 index 2b57c8e737..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/number/integer.node.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { UnreachableCaseError } from "ts-essentials"; -import { FdrIntegerType } from "../../../../../fdr/fdr.types"; -import { ApiNodeContext, OutputApiNode } from "../../../../ApiNode"; -import { SchemaObject } from "../../../openapi.types"; -import { ConstArrayToType, OPENAPI_INTEGER_TYPE_FORMAT } from "../types/format.types"; - -function isOpenApiIntegerTypeFormat(format: unknown): format is ConstArrayToType { - return OPENAPI_INTEGER_TYPE_FORMAT.includes(format as ConstArrayToType); -} - -export class IntegerNode extends OutputApiNode> { - type: FdrIntegerType["type"] = "integer"; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[]) { - super(context, input, accessPath); - - if (input.type !== "integer") { - context.errorCollector.addError( - `Expected type "integer" for numerical primitive, but got "${input.type}"`, - accessPath, - ); - return; - } - - if (!isOpenApiIntegerTypeFormat(input.format)) { - context.errorCollector.addError( - `Expected format for integer primitive, but got "${input.format}"`, - accessPath, - ); - return; - } - - switch (input.format) { - case "int64": - this.type = "long"; - break; - case "int8": - case "int16": - case "int32": - case "uint8": - case "sf-integer": - case undefined: - this.type = "integer"; - break; - default: - new UnreachableCaseError(input.format); - } - } - - // In this, we pick only the non-shared types, so that we can push up the shared types to the parent for maximum reuse - toFdrShape = (): Pick => { - return { - type: this.type, - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts b/packages/parsers/openapi/shared/nodes/primitives/string.node.ts deleted file mode 100644 index 37ce06f2ca..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/string.node.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { UnreachableCaseError } from "ts-essentials"; -import { FdrStringType } from "../../../../fdr/fdr.types"; -import { ApiNodeContext, InputApiNode } from "../../../ApiNode"; -import { SchemaObject } from "../../openapi.types"; -import { ConstArrayToType, OPENAPI_STRING_TYPE_FORMAT } from "./types/format.types"; - -export class StringNode extends InputApiNode { - type: FdrStringType["type"] | undefined; - regex: string | undefined; - default: string | undefined; - minLength: number | undefined; - maxLength: number | undefined; - - mapToFdrType = (format: ConstArrayToType): FdrStringType["type"] | undefined => { - switch (format) { - case "base64url": - case "binary": - case "byte": - case "sf-binary": - return "base64"; - case "date-time": - return "datetime"; - case "int64": - return "bigInteger"; - case "date": - return "date"; - case "uuid": - return "uuid"; - case "char": - case "commonmark": - case "decimal": - case "decimal128": - case "duration": - case "email": - case "hostname": - case "html": - case "http-date": - case "idn-email": - case "idn-hostname": - case "ipv4": - case "ipv6": - case "iri-reference": - case "iri": - case "json-pointer": - case "media-range": - case "password": - case "regex": - case "relative-json-pointer": - case "sf-boolean": - case "sf-string": - case "sf-token": - case "time": - case "uri-reference": - case "uri-template": - case "uri": - case undefined: - return "string"; - default: - new UnreachableCaseError(format); - return undefined; - } - }; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { - super(context, input, accessPath, accessorKey); - if (input.type !== "string") { - context.errorCollector.addError( - `Expected type "string" for primitive, but got "${input.type}"`, - accessPath, - ); - return; - } - - this.type = this.mapToFdrType(input.format); - - if (this.type === undefined) { - context.errorCollector.addError(`Expected proper "string" format, but got "${input.format}"`, accessPath); - return; - } - - this.regex = input.pattern; - this.default = input.default; - this.minLength = input.minLength; - this.maxLength = input.maxLength; - } - - toFdrShape = (): FdrStringType | undefined => { - if (this.type === undefined) { - return undefined; - } - - return { - type: this.type, - regex: this.regex, - minLength: this.minLength, - maxLength: this.maxLength, - default: this.default, - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts b/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts deleted file mode 100644 index ba8a50d802..0000000000 --- a/packages/parsers/openapi/shared/nodes/primitives/types/format.types.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copied from https://spec.openapis.org/registry/format/ - -export type ConstArrayToType = T[number]; - -export const OPENAPI_NUMBER_TYPE_FORMAT = [ - "decimal", - "decimal128", - "double-int", - "double", - "float", - "sf-decimal", - undefined, -] as const; -export const OPENAPI_INTEGER_TYPE_FORMAT = [ - "int16", - "int32", - "int64", - "int8", - "sf-integer", - "uint8", - undefined, -] as const; -export const OPENAPI_STRING_TYPE_FORMAT = [ - "base64url", - "binary", - "byte", - "char", - "commonmark", - "date-time", - "date", - "decimal", - "decimal128", - "duration", - "email", - "hostname", - "html", - "http-date", - "idn-email", - "idn-hostname", - "int64", - "ipv4", - "ipv6", - "iri-reference", - "iri", - "json-pointer", - "media-range", - "password", - "regex", - "relative-json-pointer", - "sf-binary", - "sf-boolean", - "sf-string", - "sf-token", - "time", - "uri-reference", - "uri-template", - "uri", - "uuid", - undefined, -] as const; diff --git a/packages/parsers/openapi/shared/nodes/schema.node.ts b/packages/parsers/openapi/shared/nodes/schema.node.ts deleted file mode 100644 index 1f5e71fc9a..0000000000 --- a/packages/parsers/openapi/shared/nodes/schema.node.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, InputApiNode } from "../../ApiNode"; -import { SchemaObject } from "../openapi.types"; -import { TypeShapeNode } from "./typeShape.node"; - -export class SchemaNode extends InputApiNode { - shape: TypeShapeNode; - description: string | undefined; - // availability: AvailabilityNode; - - constructor( - private readonly name: string, - context: ApiNodeContext, - input: SchemaObject, - accessPath: string[], - accessorKey?: string, - ) { - super(context, input, accessPath, accessorKey); - - this.shape = new TypeShapeNode(context, input, accessPath, accessorKey); - this.description = input.description; - } - - toFdrShape = (): FdrAPI.api.latest.TypeDefinition | undefined => { - const typeShape = this.shape.toFdrShape(); - if (typeShape === undefined) { - return undefined; - } - - return { - name: this.name, - shape: typeShape, - description: this.description, - availability: undefined, - // TODO: update to availability: this.availability.toFdrShape(), - }; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/typeReference.node.ts b/packages/parsers/openapi/shared/nodes/typeReference.node.ts deleted file mode 100644 index cf58f0e408..0000000000 --- a/packages/parsers/openapi/shared/nodes/typeReference.node.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { isNonNullish } from "@fern-api/ui-core-utils"; -import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; -import { ReferenceObject, SchemaObject } from "../openapi.types"; -import { NumberNode } from "./primitives/number.node"; -import { StringNode } from "./primitives/string.node"; - -export const isReferenceObject = (input: unknown): input is ReferenceObject => { - return typeof input === "object" && isNonNullish(input) && "$ref" in input && typeof input.$ref === "string"; -}; -export const mapReferenceObject = (referenceObject: ReferenceObject): SchemaObject => { - return referenceObject.$ref as SchemaObject; -}; - -// might want to split this out into AliasNode, PrimitiveNode, etc. -export class TypeReferenceNode extends OutputApiNode { - type: FdrAPI.api.latest.TypeReference["type"] | undefined; - typeNode: StringNode | NumberNode | undefined; - ref: string | undefined; - default: FdrAPI.api.latest.TypeReferenceIdDefault | undefined; - - constructor(context: ApiNodeContext, input: SchemaObject | ReferenceObject, accessPath: string[]) { - super(context, input, accessPath); - - if (isReferenceObject(input)) { - this.type = "id"; - this.ref = input.$ref; - this.default = undefined; - } else { - this.type = "primitive"; - if (input.type === "integer" || input.type === "number") { - this.typeNode = new NumberNode(context, input, accessPath); - } else if (input.type === "string") { - this.typeNode = new StringNode(context, input, accessPath); - } - } - // just support primitives and ids for now - } - - toFdrShape = (): FdrAPI.api.latest.TypeReference | undefined => { - if (this.type === "id") { - if (this.ref === undefined) { - return undefined; - } - return { - type: this.type, - id: FdrAPI.TypeId(this.ref), - default: this.default, - }; - } - if (this.type === "primitive") { - const primitiveShape = this.typeNode?.toFdrShape(); - if (primitiveShape === undefined) { - return undefined; - } - return { - type: this.type, - value: primitiveShape, - }; - } - return undefined; - }; -} diff --git a/packages/parsers/openapi/shared/nodes/typeShape.node.ts b/packages/parsers/openapi/shared/nodes/typeShape.node.ts deleted file mode 100644 index b379d72dbf..0000000000 --- a/packages/parsers/openapi/shared/nodes/typeShape.node.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { FdrAPI } from "@fern-api/fdr-sdk"; -import { ApiNodeContext, OutputApiNode } from "../../ApiNode"; -import { SchemaObject } from "../openapi.types"; -import { ObjectNode } from "./object.node"; -import { TypeReferenceNode } from "./typeReference.node"; - -export class TypeShapeNode extends OutputApiNode { - // For now, we will just support Object nodes, in the future, this will need to be updated to an exhaustive switch - type: FdrAPI.api.latest.TypeShape["type"] | undefined; - typeNode: ObjectNode | TypeReferenceNode | undefined; - - constructor(context: ApiNodeContext, input: SchemaObject, accessPath: string[], accessorKey?: string) { - super(context, input, accessPath); - - // For now, we will just support Object and alias nodes, in the future, this will need to be updated to an exhaustive switch - - if (input.type === "object") { - this.type = "object"; - this.typeNode = new ObjectNode(context, input, accessPath, accessorKey); - } else { - this.type = "alias"; - this.typeNode = new TypeReferenceNode(context, input, accessPath); - } - } - - toFdrShape = (): FdrAPI.api.latest.TypeShape | undefined => { - const typeShape = this.typeNode?.toFdrShape(); - if (typeShape === undefined || this.type === undefined) { - return undefined; - } - switch (this.type) { - case "object": - return { - type: "object", - // Figure out a better way to do this - ...(typeShape as FdrAPI.api.latest.ObjectType), - }; - case "alias": - return { - type: this.type, - value: typeShape as FdrAPI.api.latest.TypeReference, - }; - default: - return undefined; - } - }; -} diff --git a/packages/parsers/openapi/shared/openapi.types.ts b/packages/parsers/openapi/shared/openapi.types.ts deleted file mode 100644 index a7bd020749..0000000000 --- a/packages/parsers/openapi/shared/openapi.types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from "openapi-types"; -export type SchemaObject = OpenAPIV3_1.SchemaObject | OpenAPIV3.SchemaObject | OpenAPIV2.SchemaObject; -export type ReferenceObject = OpenAPIV3_1.ReferenceObject | OpenAPIV3.ReferenceObject | OpenAPIV2.ReferenceObject;