From 81dafda1889bd5220e5a5d720106938da94de62e Mon Sep 17 00:00:00 2001 From: mathcovax Date: Fri, 29 May 2026 17:06:00 +0000 Subject: [PATCH 01/10] feat(39): generate identified dataParser --- package-lock.json | 26 +-- package.json | 6 +- .../findIdentifiedDataParserInSteps.ts | 97 +++++++++++ scripts/plugins/codeGenerator/plugin.ts | 164 ++++++++++++++++-- .../codeGenerator/typescriptTransformer.ts | 8 + .../findIdentifiedDataParserInSteps.test.ts | 128 ++++++++++++++ 6 files changed, 399 insertions(+), 30 deletions(-) create mode 100644 scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts create mode 100644 tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts diff --git a/package-lock.json b/package-lock.json index 5b46202..3cb5cb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ "node": ">=22.15.1" }, "peerDependencies": { - "@duplojs/data-parser-tools": ">=0.3.0 <1.0.0", - "@duplojs/server-utils": ">=0.3.0 <1.0.0", - "@duplojs/utils": ">=1.8.0 <2.0.0" + "@duplojs/data-parser-tools": ">=0.5.0 <1.0.0", + "@duplojs/server-utils": ">=0.3.1 <1.0.0", + "@duplojs/utils": ">=1.8.5 <2.0.0" } }, "docs": { @@ -1125,9 +1125,9 @@ "license": "MIT" }, "node_modules/@duplojs/data-parser-tools": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@duplojs/data-parser-tools/-/data-parser-tools-0.3.0.tgz", - "integrity": "sha512-Kt8DtcYg273lkSK+yUK4EyDT+N5jxAejqdUmQe6DYLGVitu5NxItOLWWer4Zk7Pi6h/p5rwX/jyeMa5n1kVUEQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@duplojs/data-parser-tools/-/data-parser-tools-0.5.0.tgz", + "integrity": "sha512-M8acaPcUnF+R27LoLs1S5tEI07PDoyGXOXQh9ygRN4MWQguQzAGkwL4pKADQC8UA+f9J0O06rkuPfKDZ3K9y4g==", "license": "MIT", "peer": true, "workspaces": [ @@ -1142,7 +1142,7 @@ }, "peerDependencies": { "@duplojs/server-utils": ">=0.3.0 < 1.0.0", - "@duplojs/utils": ">=1.8.1 <2.0.0" + "@duplojs/utils": ">=1.8.4 <2.0.0" } }, "node_modules/@duplojs/eslint": { @@ -1602,9 +1602,9 @@ "link": true }, "node_modules/@duplojs/server-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.3.0.tgz", - "integrity": "sha512-CMVHvod5fE/ttua916oBfcvKyP2HBG32OcQfAiK8j+fr7+AoUdMJXogJKhS07pl/y03axhFr0cQLS+iKB15iLw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.3.1.tgz", + "integrity": "sha512-ygyR/rlbONRnhbW14rTv0p5nKL/suszHCowCBwCGeU2UWHqPH6uutgGwq3Glm1rPBqly9YppZSvxx/lMxopyBw==", "license": "MIT", "peer": true, "workspaces": [ @@ -1619,9 +1619,9 @@ } }, "node_modules/@duplojs/utils": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.8.1.tgz", - "integrity": "sha512-dVsvAPOTCsayWSr2HDA/2XebK35NVsB8efnd3u5Tw+aqSUB09/S4uaqBNCUkkaVQW/S0OTc5OlHGxnTWsPz00A==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.8.5.tgz", + "integrity": "sha512-I69oR/LCcUtjGxmQaUuUitwCUvrlUzgA3WrlXJ0dLc8sCzOLK3QyCEBwIDQvFwe0rqFO4oMoEelgbJmcZbwYhQ==", "license": "MIT", "peer": true, "workspaces": [ diff --git a/package.json b/package.json index 68de402..7d190b9 100644 --- a/package.json +++ b/package.json @@ -88,9 +88,9 @@ "README.md" ], "peerDependencies": { - "@duplojs/data-parser-tools": ">=0.3.0 <1.0.0", - "@duplojs/server-utils": ">=0.3.0 <1.0.0", - "@duplojs/utils": ">=1.8.0 <2.0.0" + "@duplojs/data-parser-tools": ">=0.5.1 <1.0.0", + "@duplojs/server-utils": ">=0.3.1 <1.0.0", + "@duplojs/utils": ">=1.8.5 <2.0.0" }, "devDependencies": { "@commitlint/cli": "19.8.1", diff --git a/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts b/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts new file mode 100644 index 0000000..aa090cf --- /dev/null +++ b/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts @@ -0,0 +1,97 @@ +import { checkerStepKind, cutStepKind, extractStepKind, handlerStepKind, presetCheckerStepKind, processStepKind, type Steps } from "@core/steps"; +import { DataParserFinder } from "@duplojs/data-parser-tools"; +import { A, DP, forward, hasSomeKinds, innerPipe, O, P, pipe, whenNot } from "@duplojs/utils"; + +export type IdentifiedDataParser = ( + & DP.DataParser + & { definition: { identifier: string } } +); + +export function dataParserHasIdentifier( + dataParser: DP.DataParser, +): dataParser is IdentifiedDataParser { + return !!dataParser.definition.identifier; +} + +export interface findIdentifiedDataParserInStepsParams { + readonly ignoreDataParser: Set; +} + +export function findIdentifiedDataParserInSteps( + steps: readonly Steps[], + params: findIdentifiedDataParserInStepsParams, +): IdentifiedDataParser[] { + return pipe( + steps, + A.flatMap( + innerPipe( + P.when( + extractStepKind.has, + (extractStep) => pipe( + extractStep.definition.shape, + O.values, + A.flatMap( + innerPipe( + whenNot( + DP.dataParserKind.has, + O.values, + ), + ), + ), + A.concat( + extractStep.definition.responseContract?.body + ? [extractStep.definition.responseContract?.body] + : [], + ), + ), + ), + P.when( + processStepKind.has, + forward, + ), + P.when( + presetCheckerStepKind.has, + (step) => [step.definition.presetChecker.definition.responseContract.body], + ), + P.when( + hasSomeKinds([ + checkerStepKind, + cutStepKind, + handlerStepKind, + ]), + (step) => pipe( + step.definition.responseContract, + A.coalescing, + A.map( + ({ body }) => body, + ), + ), + ), + P.exhaustive, + ), + ), + A.flatMap( + innerPipe( + P.when( + processStepKind.has, + (processStep) => findIdentifiedDataParserInSteps( + processStep.definition.process.definition.steps, + params, + ), + ), + P.when( + DP.dataParserKind.has, + (dataParser) => DataParserFinder.dataParserFinder( + dataParser, + dataParserHasIdentifier, + { + researchers: DataParserFinder.defaultResearchers, + ignore: params.ignoreDataParser, + }, + ), + ), + P.exhaustive, + ), + ), + ); +} diff --git a/scripts/plugins/codeGenerator/plugin.ts b/scripts/plugins/codeGenerator/plugin.ts index 6b25940..4328f87 100644 --- a/scripts/plugins/codeGenerator/plugin.ts +++ b/scripts/plugins/codeGenerator/plugin.ts @@ -1,12 +1,22 @@ import * as DataParserToTypescript from "@duplojs/data-parser-tools/toTypescript"; import { type HubPlugin } from "@core/hub"; -import { A, asserts, DP, E, equal } from "@duplojs/utils"; +import { A, asserts, DP, E, equal, G, Path, pipe } from "@duplojs/utils"; import { routeToDataParser } from "./routeToDataParser"; import { SF } from "@duplojs/server-utils"; -import { dateTransformer, fileTransformer, timeTransformer } from "./typescriptTransformer"; +import { typescriptTransformers } from "./typescriptTransformer"; +import { dataParserHasIdentifier, findIdentifiedDataParserInSteps } from "./findIdentifiedDataParserInSteps"; +import { DataParserFinder, DataParserToDataParser } from "@duplojs/data-parser-tools"; +import { type Route } from "@core/route"; + +export interface GenerateDataParserParams { + outputFolder: string; + disabledFromRoute?: boolean; + dataParsers?: DP.DataParser[]; +} export interface CodeGeneratorPluginParams { outputFile: string; + generateDataParser?: GenerateDataParserParams; } export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { @@ -19,13 +29,13 @@ export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { return; } - const routes = A.from(hub.routes); - - const dataParserRoutes = A.flatMap( - routes, - (route) => routeToDataParser(route, { + const dataParserRoutes = pipe( + hub.routes, + G.map((route) => routeToDataParser(route, { defaultExtractContract: hub.defaultExtractContract, - }), + })), + G.flat, + A.from, ); if (!A.minElements(dataParserRoutes, 1)) { @@ -37,12 +47,7 @@ export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { { identifier: "Routes", mode: "in", - transformers: [ - fileTransformer, - dateTransformer, - timeTransformer, - ...DataParserToTypescript.defaultTransformers, - ], + transformers: typescriptTransformers, }, ); @@ -50,6 +55,137 @@ export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { await SF.writeTextFile(pluginParams.outputFile, output), E.isRight, ); + + if (pluginParams.generateDataParser) { + const generateDataParserParams = pluginParams.generateDataParser; + + const buildedContext: DataParserToDataParser.BuildedContext = { + context: new Map(), + importContext: new Map(), + typescriptContext: new Map(), + importMode: "lite", + }; + const ignoreDataParser = new Set(); + + pipe( + [], + A.concat( + pipe( + generateDataParserParams.dataParsers ?? [], + A.flatMap( + (dataParser) => DataParserFinder.dataParserFinder( + dataParser, + dataParserHasIdentifier, + { + researchers: DataParserFinder.defaultResearchers, + ignore: ignoreDataParser, + }, + ), + ), + ), + ), + A.concat( + pipe( + generateDataParserParams.disabledFromRoute + ? new Set() + : hub.routes, + G.map( + (route) => findIdentifiedDataParserInSteps( + route.definition.steps, + { ignoreDataParser }, + ), + ), + G.flat, + A.from, + ), + ), + A.map( + (dataParser) => DataParserToDataParser.buildContext( + dataParser, + { + identifier: dataParser.definition.identifier, + checkerTransformers: DataParserToDataParser.defaultCheckerTransformers, + dataParserTransformers: DataParserToDataParser.defaultTransformers, + typescriptTransformers: DataParserToTypescript.defaultTransformers, + ...buildedContext, + }, + ), + ), + ); + + asserts( + await SF.writeTextFile( + Path.resolveRelative([ + generateDataParserParams.outputFolder, + "types.ts", + ]), + DataParserToTypescript.printer({ + context: buildedContext.typescriptContext, + importContext: buildedContext.importContext, + }), + ), + E.isRight, + ); + + await pipe( + buildedContext.context.entries(), + G.map( + ([dataParser, contextValue]) => { + const subImportContext: DataParserToTypescript.MapImportContext = new Map( + buildedContext.importContext, + ); + + pipe( + contextValue.dependencies, + G.map( + (dataParser) => { + const subContextValue = buildedContext.context.get(dataParser); + if (!subContextValue) { + return null; + } + + subImportContext.set(`./${subContextValue.identifier.text}`, { + direct: [subContextValue.identifier.text], + }); + + return null; + }, + ), + ); + + if (contextValue.typeIdentifier) { + subImportContext.set("./types", { + direct: [contextValue.typeIdentifier.text], + }); + } + + return { + fileName: `${contextValue.identifier.text}.ts`, + importContext: subImportContext, + context: new Map([[dataParser, contextValue]]), + importMode: buildedContext.importMode, + typescriptContext: new Map(), + }; + }, + ), + G.asyncMap( + async(context) => { + asserts( + await SF.writeTextFile( + Path.resolveRelative([ + generateDataParserParams.outputFolder, + context.fileName, + ]), + DataParserToDataParser.printer( + context, + ), + ), + E.isRight, + ); + }, + ), + ); + } }, }, ], diff --git a/scripts/plugins/codeGenerator/typescriptTransformer.ts b/scripts/plugins/codeGenerator/typescriptTransformer.ts index 2200606..6d1bfbc 100644 --- a/scripts/plugins/codeGenerator/typescriptTransformer.ts +++ b/scripts/plugins/codeGenerator/typescriptTransformer.ts @@ -1,3 +1,4 @@ +import { DataParserToTypescript } from "@duplojs/data-parser-tools"; import { createTransformer } from "@duplojs/data-parser-tools/toTypescript"; import { SDP } from "@duplojs/server-utils"; import { DP } from "@duplojs/utils"; @@ -41,3 +42,10 @@ export const timeTransformer = createTransformer( ])); }, ); + +export const typescriptTransformers = [ + fileTransformer, + dateTransformer, + timeTransformer, + ...DataParserToTypescript.defaultTransformers, +]; diff --git a/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts new file mode 100644 index 0000000..7a1a92c --- /dev/null +++ b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts @@ -0,0 +1,128 @@ +import { createPresetChecker, ResponseContract, useProcessBuilder, useRouteBuilder } from "@core"; +import { DPE } from "@duplojs/utils"; +import { findIdentifiedDataParserInSteps } from "@plugin-codeGenerator/findIdentifiedDataParserInSteps"; +import { testChecker } from "@test-utils/checker"; + +describe("findIdentifiedDataParserInSteps", () => { + it("finds only identified data parsers from extract steps", () => { + const dataParser1 = DPE.string().setIdentifier("extractNested"); + const dataParser2 = DPE.string().setIdentifier("extractHeader"); + const dataParser3 = DPE.string().setIdentifier("extractQuery"); + + const extractBody = DPE.object({ + nested: dataParser1, + ignored: DPE.number(), + }); + + const route = useRouteBuilder("GET", "/test") + .extract({ + body: extractBody, + headers: dataParser2, + query: { + search: dataParser3, + page: DPE.number(), + }, + }) + .handler( + ResponseContract.noContent("extract.ok"), + (__, { response }) => response("extract.ok"), + ); + + const result = findIdentifiedDataParserInSteps( + route.definition.steps, + { ignoreDataParser: new Set() }, + ); + + expect(result).toStrictEqual([ + dataParser1, + dataParser2, + dataParser3, + ]); + }); + + it("finds only identified data parsers from step response contracts", () => { + const presetBody = DPE.string().setIdentifier("presetBody"); + + const presetChecker = createPresetChecker( + testChecker, + { + result: "info", + otherwise: ResponseContract.badRequest("preset.invalid", presetBody), + }, + ); + + const dataParser1 = DPE.empty().setIdentifier("checkerBody"); + const dataParser2 = DPE.string().setIdentifier("cutBody"); + const dataParser3 = DPE.string().setIdentifier("handlerBody"); + + const route = useRouteBuilder("GET", "/test") + .extract({ body: DPE.string() }) + .check( + testChecker, + { + input: () => "", + result: "info", + otherwise: ResponseContract.badRequest( + "checker.invalid", + dataParser1, + ), + }, + ) + .cut( + ResponseContract.conflict("cut.conflict", dataParser2), + (__, { response }) => response("cut.conflict", ""), + ) + .presetCheck(presetChecker, () => "") + .handler( + ResponseContract.ok( + "handler.ok", + DPE.object({ + kept: dataParser3, + ignored: DPE.number(), + }), + ), + (__, { response }) => response("handler.ok", { + kept: "", + ignored: 1, + }), + ); + + const result = findIdentifiedDataParserInSteps( + route.definition.steps, + { ignoreDataParser: new Set() }, + ); + + expect(result).toStrictEqual([ + dataParser1, + dataParser2, + presetBody, + dataParser3, + ]); + }); + + it("walks nested process steps and ignores already visited data parsers", () => { + const sharedDataParser = DPE.string().setIdentifier("sharedDataParser"); + + const process = useProcessBuilder() + .extract({ body: sharedDataParser }) + .cut( + ResponseContract.conflict("process.conflict", sharedDataParser), + (__, { response }) => response("process.conflict", ""), + ) + .exports(); + + const route = useRouteBuilder("GET", "/test") + .exec(process) + .handler( + ResponseContract.ok("route.ok", sharedDataParser), + (__, { response }) => response("route.ok", ""), + ); + + const result = findIdentifiedDataParserInSteps( + route.definition.steps, + { ignoreDataParser: new Set() }, + ); + + expect(result).toStrictEqual([sharedDataParser]); + }); +}); From 533606dbc85bbb1edb1efd316ba93bdd600d28a6 Mon Sep 17 00:00:00 2001 From: mathcovax Date: Thu, 4 Jun 2026 20:03:42 +0000 Subject: [PATCH 02/10] feat(39): update utils --- .gitignore | 3 +- eslint.config.js | 2 +- .../__snapshots__/index.test.ts.snap | 36 +- integration/codeGenerator/index.test.ts | 12 +- integration/core/index.ts | 10 +- integration/core/routes/users.ts | 16 +- .../__snapshots__/index.test.ts.snap | 153 ++--- integration/static/index.test.ts | 2 +- package-lock.json | 606 ++++++++---------- package.json | 6 +- scripts/client/tsconfig.build.json | 4 +- scripts/client/tsconfig.json | 6 +- scripts/core/tsconfig.build.json | 4 +- scripts/core/tsconfig.json | 4 +- .../createSubDataParserBuildedContext.ts | 57 ++ scripts/plugins/codeGenerator/plugin.ts | 105 ++- .../steps/defaults/handlerStep.test.ts | 1 + .../__snapshots__/plugin.test.ts.snap | 110 +++- .../routeToDataParser.test.ts.snap | 2 +- .../typescriptTransfomer.test.ts.snap | 4 +- .../createSubDataParserBuildedContext.test.ts | 136 ++++ .../findIdentifiedDataParserInSteps.test.ts | 21 + tests/plugins/codeGenerator/plugin.test.ts | 118 ++++ 23 files changed, 906 insertions(+), 512 deletions(-) create mode 100644 scripts/plugins/codeGenerator/createSubDataParserBuildedContext.ts create mode 100644 tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts diff --git a/.gitignore b/.gitignore index 7af13d4..d5a7ce1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ dist coverage cache -**/*.generate.* \ No newline at end of file +**/*.generate.* +**/*.generate \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 853c73f..609a897 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -36,6 +36,6 @@ export default [ files: ["docs/examples/**/*.ts"], }, { - ignores: ["coverage", "dist", "**/*.generate.*", "docs/libs/*", "docs/.vitepress/cache/*", "docs/.vitepress/dist/*", "**/*.d.ts"] + ignores: ["coverage", "dist", "**/*.generate.*", "**/*.generate", "docs/libs/*", "docs/.vitepress/cache/*", "docs/.vitepress/dist/*", "**/*.d.ts"] } ]; diff --git a/integration/codeGenerator/__snapshots__/index.test.ts.snap b/integration/codeGenerator/__snapshots__/index.test.ts.snap index adf5a3b..f507e02 100644 --- a/integration/codeGenerator/__snapshots__/index.test.ts.snap +++ b/integration/codeGenerator/__snapshots__/index.test.ts.snap @@ -1,7 +1,18 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`codeGenerator > correct generate file 1`] = ` -"import type { TheFormData } from "@duplojs/utils"; +"import { TheFormData } from "@duplojs/utils"; + +export type UserId = number; + +export type UserName = string; + +export type User = { + id: UserId; + name: UserName; + age: number; + friends?: User[] | undefined; +}; export type Routes = { method: "GET"; @@ -9,11 +20,7 @@ export type Routes = { responses: { code: "200"; information: "users.findMany"; - body: { - id: number; - name: string; - age: number; - }[]; + body: User[]; }; } | { method: "GET"; @@ -28,19 +35,16 @@ export type Routes = { } | { code: "200"; information: "users.find"; - body: { - id: number; - name: string; - age: number; - }; + body: User; }; } | { method: "POST"; path: "/users"; body: { - id: number; - name: string; + id: UserId; + name: UserName; age: number; + friends?: User[] | undefined; }; responses: { code: "422"; @@ -49,11 +53,7 @@ export type Routes = { } | { code: "200"; information: "users.create"; - body: { - id: number; - name: string; - age: number; - }; + body: User; }; } | { method: "DELETE"; diff --git a/integration/codeGenerator/index.test.ts b/integration/codeGenerator/index.test.ts index a4df839..68f59b3 100644 --- a/integration/codeGenerator/index.test.ts +++ b/integration/codeGenerator/index.test.ts @@ -5,15 +5,25 @@ import { launchHookServer } from "@duplojs/http"; describe("codeGenerator", () => { const fileName = `${import.meta.dirname}/generateCode.generate.ts`; + const folderName = `${import.meta.dirname}/generateDataParser.generate`; beforeAll(() => { if (existsSync(fileName)) { rmSync(fileName); } + if (existsSync(folderName)) { + rmSync(folderName, { + recursive: true, + force: true, + }); + } }); it("correct generate file", async() => { const hubWithPlugins = hub.plug( - codeGeneratorPlugin({ outputFile: fileName }), + codeGeneratorPlugin({ + outputFile: fileName, + generateDataParser: { outputFolder: folderName }, + }), ); await launchHookServer( hubWithPlugins.aggregatesHooksHubLifeCycle("beforeStartServer"), diff --git a/integration/core/index.ts b/integration/core/index.ts index 42fbd13..2e25c31 100644 --- a/integration/core/index.ts +++ b/integration/core/index.ts @@ -1,19 +1,17 @@ -import { asserts, E, Path } from "@duplojs/utils"; -import { setCurrentWorkingDirectory, SF } from "@duplojs/server-utils"; +import { Path } from "@duplojs/utils"; +import { setCurrentWorkingDirectoryOrThrow, SF } from "@duplojs/server-utils"; import { createHub, routeStore } from "@duplojs/http"; import { staticPlugin } from "@duplojs/http/static"; import { corsPlugin } from "@duplojs/http/cors"; import { cookiePlugin } from "@duplojs/http/cookie"; +import "@duplojs/http/codeGenerator"; import "./routes"; const sourceFile = SF.createFileInterface("files/fakeFiles/superTextFile.txt"); const sourceFolder = SF.createFolderInterface("files/fakeFiles"); -asserts( - setCurrentWorkingDirectory(Path.resolveRelative([import.meta.dirname, "../"])), - E.isRight, -); +setCurrentWorkingDirectoryOrThrow(Path.resolveRelative([import.meta.dirname, "../"])); export const hub = createHub({ environment: "DEV" }) .register(routeStore.getAll()) diff --git a/integration/core/routes/users.ts b/integration/core/routes/users.ts index ea8b045..d80c9ea 100644 --- a/integration/core/routes/users.ts +++ b/integration/core/routes/users.ts @@ -2,11 +2,19 @@ import { ResponseContract, useRouteBuilder } from "@duplojs/http"; import { createCacheControllerHooks } from "@duplojs/http/cacheController"; import { DPE } from "@duplojs/utils"; -const user = DPE.object({ - id: DPE.number(), - name: DPE.string(), +interface User { + id: number; + name: string; + age: number; + friends?: User[]; +} + +const user: DPE.DataParserExtended = DPE.object({ + id: DPE.number().setIdentifier("UserId"), + name: DPE.string().setIdentifier("UserName"), age: DPE.number(), -}); + friends: DPE.lazy(() => user).array().optional(), +}).contractExtended().setIdentifier("User"); useRouteBuilder("GET", "/users", { hooks: [ diff --git a/integration/openApiGenerator/__snapshots__/index.test.ts.snap b/integration/openApiGenerator/__snapshots__/index.test.ts.snap index dcb28e7..e9edb95 100644 --- a/integration/openApiGenerator/__snapshots__/index.test.ts.snap +++ b/integration/openApiGenerator/__snapshots__/index.test.ts.snap @@ -58,7 +58,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified6" + "$ref": "#/components/schemas/User" } } } @@ -83,7 +83,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified7" + "$ref": "#/components/schemas/NotIdentified6" } } } @@ -140,7 +140,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified3" + "$ref": "#/components/schemas/User" } } } @@ -167,7 +167,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/NotIdentified10" + "$ref": "#/components/schemas/NotIdentified9" } } } @@ -215,7 +215,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified13" + "$ref": "#/components/schemas/NotIdentified12" } } } @@ -231,7 +231,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "in": "query", "required": false, "schema": { - "$ref": "#/components/schemas/NotIdentified14" + "$ref": "#/components/schemas/NotIdentified13" } } ], @@ -249,7 +249,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "text/event-stream": { "itemSchema": { - "$ref": "#/components/schemas/NotIdentified16" + "$ref": "#/components/schemas/NotIdentified15" } } } @@ -296,7 +296,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified19" + "$ref": "#/components/schemas/NotIdentified18" } } } @@ -341,7 +341,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "in": "query", "required": true, "schema": { - "$ref": "#/components/schemas/NotIdentified21" + "$ref": "#/components/schemas/NotIdentified20" } } ], @@ -387,7 +387,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified23" + "$ref": "#/components/schemas/NotIdentified22" } } } @@ -406,7 +406,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/NotIdentified25" + "$ref": "#/components/schemas/NotIdentified24" } } } @@ -442,7 +442,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified26" + "$ref": "#/components/schemas/NotIdentified25" } } } @@ -478,7 +478,7 @@ exports[`openApiGenerator > correct generate file 1`] = ` "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotIdentified28" + "$ref": "#/components/schemas/NotIdentified27" } } } @@ -511,50 +511,26 @@ exports[`openApiGenerator > correct generate file 1`] = ` }, "components": { "schemas": { - "NotIdentified0": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "name": { - "type": "string" - }, - "age": { - "type": "number" - } - }, - "required": [ - "id", - "name", - "age" - ] - } + "UserId": { + "type": "number" }, - "NotIdentified1": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] + "UserName": { + "type": "string" }, - "NotIdentified2": {}, - "NotIdentified3": { + "User": { "type": "object", "properties": { "id": { - "type": "number" + "$ref": "#/components/schemas/UserId" }, "name": { - "type": "string" + "$ref": "#/components/schemas/UserName" }, "age": { "type": "number" + }, + "friends": { + "$ref": "#/components/schemas/RecursiveType2" } }, "required": [ @@ -563,37 +539,43 @@ exports[`openApiGenerator > correct generate file 1`] = ` "age" ] }, - "NotIdentified4": { - "type": "object", - "properties": { - "id": { + "NotIdentified0": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "NotIdentified1": { + "anyOf": [ + { "type": "number" }, - "name": { + { "type": "string" - }, - "age": { - "type": "number" } - }, - "required": [ - "id", - "name", - "age" ] }, - "NotIdentified5": {}, - "NotIdentified6": { + "NotIdentified2": {}, + "RecursiveType2": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "NotIdentified4": { "type": "object", "properties": { "id": { - "type": "number" + "$ref": "#/components/schemas/UserId" }, "name": { - "type": "string" + "$ref": "#/components/schemas/UserName" }, "age": { "type": "number" + }, + "friends": { + "$ref": "#/components/schemas/RecursiveType2" } }, "required": [ @@ -602,7 +584,8 @@ exports[`openApiGenerator > correct generate file 1`] = ` "age" ] }, - "NotIdentified7": { + "NotIdentified5": {}, + "NotIdentified6": { "type": "object", "properties": { "id": { @@ -620,9 +603,9 @@ exports[`openApiGenerator > correct generate file 1`] = ` "id" ] }, + "NotIdentified7": {}, "NotIdentified8": {}, - "NotIdentified9": {}, - "NotIdentified10": { + "NotIdentified9": { "type": "object", "properties": { "bool": { @@ -670,13 +653,13 @@ exports[`openApiGenerator > correct generate file 1`] = ` "name" ] }, + "NotIdentified10": {}, "NotIdentified11": {}, - "NotIdentified12": {}, - "NotIdentified13": { + "NotIdentified12": { "type": "string", "format": "binary" }, - "NotIdentified14": { + "NotIdentified13": { "anyOf": [ { "type": "boolean" @@ -699,8 +682,8 @@ exports[`openApiGenerator > correct generate file 1`] = ` } ] }, - "NotIdentified15": {}, - "NotIdentified16": { + "NotIdentified14": {}, + "NotIdentified15": { "type": "object", "properties": { "event": { @@ -745,9 +728,9 @@ exports[`openApiGenerator > correct generate file 1`] = ` "data" ] }, + "NotIdentified16": {}, "NotIdentified17": {}, - "NotIdentified18": {}, - "NotIdentified19": { + "NotIdentified18": { "type": "object", "properties": { "session": { @@ -758,8 +741,8 @@ exports[`openApiGenerator > correct generate file 1`] = ` "session" ] }, - "NotIdentified20": {}, - "NotIdentified21": { + "NotIdentified19": {}, + "NotIdentified20": { "anyOf": [ { "type": "number" @@ -769,8 +752,8 @@ exports[`openApiGenerator > correct generate file 1`] = ` } ] }, - "NotIdentified22": {}, - "NotIdentified23": { + "NotIdentified21": {}, + "NotIdentified22": { "type": "object", "properties": { "value": { @@ -781,21 +764,21 @@ exports[`openApiGenerator > correct generate file 1`] = ` "value" ] }, - "NotIdentified24": {}, - "NotIdentified25": { + "NotIdentified23": {}, + "NotIdentified24": { "type": "string" }, - "NotIdentified26": { + "NotIdentified25": { "type": "string", "format": "binary" }, - "NotIdentified27": {}, - "NotIdentified28": { + "NotIdentified26": {}, + "NotIdentified27": { "type": "string", "format": "binary" }, - "NotIdentified29": {}, - "NotIdentified30": {} + "NotIdentified28": {}, + "NotIdentified29": {} } } }" diff --git a/integration/static/index.test.ts b/integration/static/index.test.ts index b53ae3b..d5ba9dc 100644 --- a/integration/static/index.test.ts +++ b/integration/static/index.test.ts @@ -1,5 +1,5 @@ import { type SF, TESTImplementation, setEnvironment } from "@duplojs/server-utils"; -import { E, D, mimeType } from "@duplojs/utils"; +import { E, D } from "@duplojs/utils"; import { createHttpServer } from "@duplojs/http/node"; import { hub } from "@core"; diff --git a/package-lock.json b/package-lock.json index 3cb5cb6..bc0c389 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ "node": ">=22.15.1" }, "peerDependencies": { - "@duplojs/data-parser-tools": ">=0.5.0 <1.0.0", - "@duplojs/server-utils": ">=0.3.1 <1.0.0", - "@duplojs/utils": ">=1.8.5 <2.0.0" + "@duplojs/data-parser-tools": ">=0.5.4 <1.0.0", + "@duplojs/server-utils": ">=0.4.0 <1.0.0", + "@duplojs/utils": ">=1.9.1 <2.0.0" } }, "docs": { @@ -496,13 +496,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -511,9 +511,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -521,21 +521,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -562,14 +562,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -579,14 +579,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -606,9 +606,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -616,29 +616,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -648,9 +648,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -658,9 +658,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -668,9 +668,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -678,27 +678,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -708,33 +708,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -742,14 +742,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -762,45 +762,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", - "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/gast": "12.0.0", - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/gast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", - "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/types": "12.0.0" - } - }, - "node_modules/@chevrotain/regexp-to-ast": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", - "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/@chevrotain/types": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", - "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/utils": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", - "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", "dev": true, "license": "Apache-2.0" }, @@ -1125,9 +1090,9 @@ "license": "MIT" }, "node_modules/@duplojs/data-parser-tools": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@duplojs/data-parser-tools/-/data-parser-tools-0.5.0.tgz", - "integrity": "sha512-M8acaPcUnF+R27LoLs1S5tEI07PDoyGXOXQh9ygRN4MWQguQzAGkwL4pKADQC8UA+f9J0O06rkuPfKDZ3K9y4g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@duplojs/data-parser-tools/-/data-parser-tools-0.5.4.tgz", + "integrity": "sha512-+OAwK1jqHV1Djs3MbkPSEVL9GTr10KNQSQoBWFlhr39CnNZGrmZ899GNQgMKClJzKdHY9SPgqAMYYaBLVya72Q==", "license": "MIT", "peer": true, "workspaces": [ @@ -1141,8 +1106,8 @@ "node": ">=22.15.1" }, "peerDependencies": { - "@duplojs/server-utils": ">=0.3.0 < 1.0.0", - "@duplojs/utils": ">=1.8.4 <2.0.0" + "@duplojs/server-utils": ">=0.4.0 < 1.0.0", + "@duplojs/utils": ">=1.9.1 <2.0.0" } }, "node_modules/@duplojs/eslint": { @@ -1602,9 +1567,9 @@ "link": true }, "node_modules/@duplojs/server-utils": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.3.1.tgz", - "integrity": "sha512-ygyR/rlbONRnhbW14rTv0p5nKL/suszHCowCBwCGeU2UWHqPH6uutgGwq3Glm1rPBqly9YppZSvxx/lMxopyBw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.4.0.tgz", + "integrity": "sha512-wpsBdc/Rz4NTWQ4UywF3qo1mfosZDSzj7kMzBT2FF+Jyn5MEyUEyor4DjSHYssjakiOHxqu5zWiVaopqJpznuQ==", "license": "MIT", "peer": true, "workspaces": [ @@ -1615,13 +1580,13 @@ "node": ">=22.15.1" }, "peerDependencies": { - "@duplojs/utils": ">=1.8.0 <2.0.0" + "@duplojs/utils": ">=1.9.1 <2.0.0" } }, "node_modules/@duplojs/utils": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.8.5.tgz", - "integrity": "sha512-I69oR/LCcUtjGxmQaUuUitwCUvrlUzgA3WrlXJ0dLc8sCzOLK3QyCEBwIDQvFwe0rqFO4oMoEelgbJmcZbwYhQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.9.1.tgz", + "integrity": "sha512-dQGYiobo7ZSyk1N5G1e5Acjk7nxeKsh9TiGFaHxbBtS0xXfH1h2fnk2nqCkIdSU9mJw6BdNA05uX35AMYcvMPA==", "license": "MIT", "peer": true, "workspaces": [ @@ -2434,9 +2399,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", "engines": { @@ -2519,13 +2484,13 @@ "optional": true }, "node_modules/@mermaid-js/parser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.0.0.tgz", - "integrity": "sha512-vvK0Hi/VWndxoh03Mmz6wa1KDriSPjS2XMZL/1l19HFwygiObEEoEwSDxOqyLzzAI6J2PU3261JjTMTO7x+BPw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz", + "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==", "dev": true, "license": "MIT", "dependencies": { - "langium": "^4.0.0" + "@chevrotain/types": "~11.1.1" } }, "node_modules/@nodelib/fs.scandir": { @@ -3803,6 +3768,17 @@ "dev": true, "license": "ISC" }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", @@ -3900,9 +3876,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.6.tgz", + "integrity": "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==", "dev": true, "license": "MIT", "dependencies": { @@ -3942,6 +3918,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vitest/spy": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", @@ -3970,6 +3959,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/utils/node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@volar/language-core": { "version": "2.4.27", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.27.tgz", @@ -4465,13 +4467,16 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", - "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/binary-extensions": { @@ -4521,9 +4526,9 @@ } }, "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -4541,11 +4546,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -4599,9 +4604,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001751", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", - "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -4694,45 +4699,15 @@ } }, "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", "dev": true, "license": "MIT", "engines": { "node": ">= 16" } }, - "node_modules/chevrotain": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", - "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/cst-dts-gen": "12.0.0", - "@chevrotain/gast": "12.0.0", - "@chevrotain/regexp-to-ast": "12.0.0", - "@chevrotain/types": "12.0.0", - "@chevrotain/utils": "12.0.0" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/chevrotain-allstar": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", - "integrity": "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21" - }, - "peerDependencies": { - "chevrotain": "^12.0.0" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5643,9 +5618,9 @@ } }, "node_modules/dagre-d3-es": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", - "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", "dev": true, "license": "MIT", "dependencies": { @@ -5826,9 +5801,9 @@ "link": true }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz", + "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==", "dev": true, "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { @@ -5871,9 +5846,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz", - "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==", + "version": "1.5.367", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.367.tgz", + "integrity": "sha512-4Mk/mrynCNQ+atY40D3UpmhLWB6AHMbYMlIrPhHcMF6x0L7O0b052FCAsxw1LlaR++UFuNg3D/A6XCuGDa0guQ==", "dev": true, "license": "ISC" }, @@ -5981,6 +5956,17 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.47.0.tgz", + "integrity": "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", @@ -6401,9 +6387,9 @@ } }, "node_modules/expect-type": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", - "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6462,9 +6448,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -7518,25 +7504,6 @@ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", "dev": true }, - "node_modules/langium": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.2.tgz", - "integrity": "sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@chevrotain/regexp-to-ast": "~12.0.0", - "chevrotain": "~12.0.0", - "chevrotain-allstar": "~0.4.1", - "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.1.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, "node_modules/layout-base": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", @@ -8095,32 +8062,33 @@ } }, "node_modules/mermaid": { - "version": "11.12.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.3.tgz", - "integrity": "sha512-wN5ZSgJQIC+CHJut9xaKWsknLxaFBwCPwPkGTSUYrTiHORWvpT8RxGk849HPnpUAQ+/9BPRqYb80jTpearrHzQ==", + "version": "11.15.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz", + "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==", "dev": true, "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.1", - "@iconify/utils": "^3.0.1", - "@mermaid-js/parser": "^1.0.0", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.1.1", "@types/d3": "^7.4.3", - "cytoscape": "^3.29.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.13", - "dayjs": "^1.11.18", - "dompurify": "^3.2.5", - "katex": "^0.16.22", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "es-toolkit": "^1.45.1", + "katex": "^0.16.25", "khroma": "^2.1.0", - "lodash-es": "^4.17.23", - "marked": "^16.2.1", + "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", - "uuid": "^11.1.0" + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "node_modules/methods": { @@ -8751,9 +8719,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -8821,11 +8789,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", - "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/non-layered-tidy-tree-layout": { "version": "2.0.2", @@ -9156,9 +9127,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -9176,7 +9147,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -9825,13 +9796,13 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -9951,20 +9922,59 @@ "license": "MIT" }, "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", "dev": true, "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^10.4.1", - "minimatch": "^9.0.4" + "minimatch": "^10.2.2" }, "engines": { "node": ">=18" } }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-extensions": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", @@ -9993,11 +10003,14 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -10398,9 +10411,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -10439,9 +10452,9 @@ } }, "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -10449,7 +10462,7 @@ ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/vfile": { @@ -12062,61 +12075,6 @@ "dev": true, "license": "MIT" }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "dev": true, - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "dev": true, - "license": "MIT" - }, "node_modules/vue": { "version": "3.5.27", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", diff --git a/package.json b/package.json index 7d190b9..60e5231 100644 --- a/package.json +++ b/package.json @@ -88,9 +88,9 @@ "README.md" ], "peerDependencies": { - "@duplojs/data-parser-tools": ">=0.5.1 <1.0.0", - "@duplojs/server-utils": ">=0.3.1 <1.0.0", - "@duplojs/utils": ">=1.8.5 <2.0.0" + "@duplojs/data-parser-tools": ">=0.5.4 <1.0.0", + "@duplojs/server-utils": ">=0.4.0 <1.0.0", + "@duplojs/utils": ">=1.9.1 <2.0.0" }, "devDependencies": { "@commitlint/cli": "19.8.1", diff --git a/scripts/client/tsconfig.build.json b/scripts/client/tsconfig.build.json index 686c577..75de9ae 100644 --- a/scripts/client/tsconfig.build.json +++ b/scripts/client/tsconfig.build.json @@ -5,6 +5,6 @@ "noEmit": false, "declaration": true, "declarationDir": "../../dist/client", - "types": null, - }, + "types": null + } } \ No newline at end of file diff --git a/scripts/client/tsconfig.json b/scripts/client/tsconfig.json index 7c71339..c432db8 100644 --- a/scripts/client/tsconfig.json +++ b/scripts/client/tsconfig.json @@ -3,9 +3,9 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@client/*": ["./*"], + "@client/*": ["./*"] }, - "types": ["web"], + "types": ["web"] }, - "include": ["**/*.ts"], + "include": ["**/*.ts"] } diff --git a/scripts/core/tsconfig.build.json b/scripts/core/tsconfig.build.json index 6ba419d..bcf71c0 100644 --- a/scripts/core/tsconfig.build.json +++ b/scripts/core/tsconfig.build.json @@ -6,6 +6,6 @@ "declaration": true, "declarationDir": "../../dist/core", "types": null, - "stripInternal": true, - }, + "stripInternal": true + } } \ No newline at end of file diff --git a/scripts/core/tsconfig.json b/scripts/core/tsconfig.json index 895f6ea..9b256be 100644 --- a/scripts/core/tsconfig.json +++ b/scripts/core/tsconfig.json @@ -3,8 +3,8 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@core/*": ["./*"], + "@core/*": ["./*"] }, }, - "include": ["**/*.ts"], + "include": ["**/*.ts"] } diff --git a/scripts/plugins/codeGenerator/createSubDataParserBuildedContext.ts b/scripts/plugins/codeGenerator/createSubDataParserBuildedContext.ts new file mode 100644 index 0000000..3f4707a --- /dev/null +++ b/scripts/plugins/codeGenerator/createSubDataParserBuildedContext.ts @@ -0,0 +1,57 @@ +import { type DataParserToTypescript, type DataParserToDataParser } from "@duplojs/data-parser-tools"; +import { G, pipe } from "@duplojs/utils"; + +export interface SubBuildedContext extends DataParserToDataParser.BuildedContext { + identifier: string; +} + +export function createSubDataParserBuildedContext( + buildedContext: DataParserToDataParser.BuildedContext, +) { + return pipe( + buildedContext.context.entries(), + G.map( + ([dataParser, contextValue]): SubBuildedContext => { + const subImportContext: DataParserToTypescript.MapImportContext = new Map( + buildedContext.importContext, + ); + + pipe( + contextValue.dependencies, + G.map( + (dataParserDependencies) => { + if (dataParser === dataParserDependencies) { + return null; + } + const subContextValue = buildedContext.context.get(dataParserDependencies); + if (!subContextValue) { + return null; + } + + subImportContext.set(`./${subContextValue.identifier.text}`, { + direct: [subContextValue.identifier.text], + }); + + return null; + }, + ), + G.execute, + ); + + if (contextValue.typeIdentifier) { + subImportContext.set("./types", { + direct: [contextValue.typeIdentifier.text], + }); + } + + return { + identifier: contextValue.identifier.text, + importContext: subImportContext, + context: new Map([[dataParser, contextValue]]), + importMode: buildedContext.importMode, + typescriptContext: new Map(), + }; + }, + ), + ); +} diff --git a/scripts/plugins/codeGenerator/plugin.ts b/scripts/plugins/codeGenerator/plugin.ts index 4328f87..4ff6859 100644 --- a/scripts/plugins/codeGenerator/plugin.ts +++ b/scripts/plugins/codeGenerator/plugin.ts @@ -1,12 +1,13 @@ import * as DataParserToTypescript from "@duplojs/data-parser-tools/toTypescript"; import { type HubPlugin } from "@core/hub"; -import { A, asserts, DP, E, equal, G, Path, pipe } from "@duplojs/utils"; +import { A, asserts, asyncPipe, DP, E, equal, G, Path, pipe } from "@duplojs/utils"; import { routeToDataParser } from "./routeToDataParser"; import { SF } from "@duplojs/server-utils"; import { typescriptTransformers } from "./typescriptTransformer"; import { dataParserHasIdentifier, findIdentifiedDataParserInSteps } from "./findIdentifiedDataParserInSteps"; import { DataParserFinder, DataParserToDataParser } from "@duplojs/data-parser-tools"; import { type Route } from "@core/route"; +import { createSubDataParserBuildedContext } from "./createSubDataParserBuildedContext"; export interface GenerateDataParserParams { outputFolder: string; @@ -113,6 +114,23 @@ export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { ), ); + await asyncPipe( + SF.exists(generateDataParserParams.outputFolder), + E.whenIsRight( + async() => void asserts( + await SF.remove( + generateDataParserParams.outputFolder, + { recursive: true }, + ), + E.isRight, + ), + ), + async() => void asserts( + await SF.makeDirectory(generateDataParserParams.outputFolder), + E.isRight, + ), + ); + asserts( await SF.writeTextFile( Path.resolveRelative([ @@ -127,63 +145,40 @@ export function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams) { E.isRight, ); + for (const context of createSubDataParserBuildedContext(buildedContext)) { + asserts( + await SF.writeTextFile( + Path.resolveRelative([ + generateDataParserParams.outputFolder, + `${context.identifier}.ts`, + ]), + DataParserToDataParser.printer( + context, + ), + ), + E.isRight, + ); + } + await pipe( - buildedContext.context.entries(), + buildedContext.context.values(), G.map( - ([dataParser, contextValue]) => { - const subImportContext: DataParserToTypescript.MapImportContext = new Map( - buildedContext.importContext, - ); - - pipe( - contextValue.dependencies, - G.map( - (dataParser) => { - const subContextValue = buildedContext.context.get(dataParser); - if (!subContextValue) { - return null; - } - - subImportContext.set(`./${subContextValue.identifier.text}`, { - direct: [subContextValue.identifier.text], - }); - - return null; - }, - ), - ); - - if (contextValue.typeIdentifier) { - subImportContext.set("./types", { - direct: [contextValue.typeIdentifier.text], - }); - } - - return { - fileName: `${contextValue.identifier.text}.ts`, - importContext: subImportContext, - context: new Map([[dataParser, contextValue]]), - importMode: buildedContext.importMode, - typescriptContext: new Map(), - }; - }, - ), - G.asyncMap( - async(context) => { - asserts( - await SF.writeTextFile( - Path.resolveRelative([ - generateDataParserParams.outputFolder, - context.fileName, - ]), - DataParserToDataParser.printer( - context, - ), - ), - E.isRight, - ); - }, + (contextValue) => `export * from "./${contextValue.identifier.text}";`, ), + A.from, + A.join("\n"), + async(values) => { + asserts( + await SF.writeTextFile( + Path.resolveRelative([ + generateDataParserParams.outputFolder, + "index.ts", + ]), + values, + ), + E.isRight, + ); + }, ); } }, diff --git a/tests/core/functionsBuilders/steps/defaults/handlerStep.test.ts b/tests/core/functionsBuilders/steps/defaults/handlerStep.test.ts index 7c386ad..1c4f62e 100644 --- a/tests/core/functionsBuilders/steps/defaults/handlerStep.test.ts +++ b/tests/core/functionsBuilders/steps/defaults/handlerStep.test.ts @@ -8,6 +8,7 @@ describe("handler step function builder", () => { beforeEach(() => { spyResponse.mockClear(); + vi.spyOn(console, "error").mockClear(); }); it("response from handler", async() => { diff --git a/tests/plugins/codeGenerator/__snapshots__/plugin.test.ts.snap b/tests/plugins/codeGenerator/__snapshots__/plugin.test.ts.snap index 63017a3..20a26ba 100644 --- a/tests/plugins/codeGenerator/__snapshots__/plugin.test.ts.snap +++ b/tests/plugins/codeGenerator/__snapshots__/plugin.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`plugin implementation > generate API type 1`] = ` -"import type { TheDate, SerializedTheDate, TheTime, SerializedTheTime } from "@duplojs/utils/date"; +"import { TheDate, SerializedTheDate, TheTime, SerializedTheTime } from "@duplojs/utils/date"; export type Routes = { method: "GET"; @@ -29,3 +29,111 @@ export type Routes = { }; };" `; + +exports[`plugin implementation > generate data parser files from identified route data parsers 1`] = ` +[ + "routes.d.ts", + "export type ChildParser = string; + +export type Routes = { + method: "GET"; + path: "/generated"; + body: { + child: ChildParser; + count: number; + }; + responses: { + code: "422"; + information: "extract-error"; + body?: undefined; + } | { + code: "200"; + information: "generated.success"; + body: ChildParser; + }; +};", +] +`; + +exports[`plugin implementation > generate data parser files from identified route data parsers 2`] = ` +[ + "generated/types.ts", + "import * as DP from "@duplojs/utils/dataParser";", +] +`; + +exports[`plugin implementation > generate data parser files from identified route data parsers 3`] = ` +[ + "generated/childParserDataParser.ts", + "import * as DP from "@duplojs/utils/dataParser"; + +export const childParserDataParser = DP.string();", +] +`; + +exports[`plugin implementation > generate data parser files from identified route data parsers 4`] = ` +[ + "generated/bodyParserDataParser.ts", + "import * as DP from "@duplojs/utils/dataParser"; + +import { childParserDataParser } from "./childParserDataParser"; + +export const bodyParserDataParser = DP.object({ + child: childParserDataParser, + count: DP.number() +});", +] +`; + +exports[`plugin implementation > generate data parser files from identified route data parsers 5`] = ` +[ + "generated/index.ts", + "export * from "./childParserDataParser"; +export * from "./bodyParserDataParser";", +] +`; + +exports[`plugin implementation > generate data parser files only from explicit data parsers when disabledFromRoute is enabled 1`] = ` +[ + "routes.d.ts", + "export type RouteParser = string; + +export type Routes = { + method: "GET"; + path: "/generated"; + body: RouteParser; + responses: { + code: "422"; + information: "extract-error"; + body?: undefined; + } | { + code: "204"; + information: "generated.success"; + body?: undefined; + }; +};", +] +`; + +exports[`plugin implementation > generate data parser files only from explicit data parsers when disabledFromRoute is enabled 2`] = ` +[ + "generated/types.ts", + "import * as DP from "@duplojs/utils/dataParser";", +] +`; + +exports[`plugin implementation > generate data parser files only from explicit data parsers when disabledFromRoute is enabled 3`] = ` +[ + "generated/explicitParserDataParser.ts", + "import * as DP from "@duplojs/utils/dataParser"; + +export const explicitParserDataParser = DP.number();", +] +`; + +exports[`plugin implementation > generate data parser files only from explicit data parsers when disabledFromRoute is enabled 4`] = ` +[ + "generated/index.ts", + "export * from "./explicitParserDataParser";", +] +`; diff --git a/tests/plugins/codeGenerator/__snapshots__/routeToDataParser.test.ts.snap b/tests/plugins/codeGenerator/__snapshots__/routeToDataParser.test.ts.snap index 4446418..0c6167a 100644 --- a/tests/plugins/codeGenerator/__snapshots__/routeToDataParser.test.ts.snap +++ b/tests/plugins/codeGenerator/__snapshots__/routeToDataParser.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`routeToDataParser > bodyAsFormData 1`] = ` -"import type { TheFormData } from "@duplojs/utils"; +"import { TheFormData } from "@duplojs/utils"; export type ArrayString = TheFormData;" `; diff --git a/tests/plugins/codeGenerator/__snapshots__/typescriptTransfomer.test.ts.snap b/tests/plugins/codeGenerator/__snapshots__/typescriptTransfomer.test.ts.snap index 0c57b06..1f5ff91 100644 --- a/tests/plugins/codeGenerator/__snapshots__/typescriptTransfomer.test.ts.snap +++ b/tests/plugins/codeGenerator/__snapshots__/typescriptTransfomer.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`typescript transformer > date 1`] = ` -"import type { TheDate, SerializedTheDate } from "@duplojs/utils/date"; +"import { TheDate, SerializedTheDate } from "@duplojs/utils/date"; export type ArrayString = (SerializedTheDate | TheDate)[];" `; @@ -9,7 +9,7 @@ export type ArrayString = (SerializedTheDate | TheDate)[];" exports[`typescript transformer > file 1`] = `"export type ArrayString = File[];"`; exports[`typescript transformer > time 1`] = ` -"import type { TheTime, SerializedTheTime } from "@duplojs/utils/date"; +"import { TheTime, SerializedTheTime } from "@duplojs/utils/date"; export type ArrayString = (SerializedTheTime | TheTime)[];" `; diff --git a/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts b/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts new file mode 100644 index 0000000..a1397dc --- /dev/null +++ b/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts @@ -0,0 +1,136 @@ +import { A, DP } from "@duplojs/utils"; +import { type DataParserToDataParser } from "@duplojs/data-parser-tools"; +import { createSubDataParserBuildedContext } from "@plugin-codeGenerator/createSubDataParserBuildedContext"; +import { factory } from "typescript"; + +describe("createSubDataParserBuildedContext", () => { + it("creates one isolated sub-context per data parser with inherited imports and relative dependencies", () => { + const dependencyDataParser = DP.string(); + const mainDataParser = DP.object({ + value: DP.number(), + }); + + const buildedContext: DataParserToDataParser.BuildedContext = { + context: new Map([ + [ + mainDataParser as never, + { + identifier: factory.createIdentifier("MainParser"), + expression: factory.createIdentifier("mainExpression"), + typeIdentifier: factory.createIdentifier("MainType"), + dependencies: new Set([dependencyDataParser, DP.empty()]), + }, + ], + [ + dependencyDataParser as never, + { + identifier: factory.createIdentifier("DependencyParser"), + expression: factory.createIdentifier("dependencyExpression"), + typeIdentifier: null, + dependencies: new Set(), + }, + ], + ]), + importContext: new Map([ + [ + "@duplojs/utils/dataParser", + { + namespace: ["DP"], + }, + ], + [ + "shared-package", + { + direct: ["SharedType"], + }, + ], + ]), + typescriptContext: new Map(), + importMode: "lite", + }; + + const result = A.from(createSubDataParserBuildedContext(buildedContext)); + const mainSubContext = result.find((context) => context.identifier === "MainParser"); + const dependencySubContext = result.find((context) => context.identifier === "DependencyParser"); + + expect(result).toHaveLength(2); + expect(mainSubContext).toBeDefined(); + expect(dependencySubContext).toBeDefined(); + + expect({ + identifier: mainSubContext!.identifier, + context: A.from(mainSubContext!.context.entries()), + importContext: A.from(mainSubContext!.importContext.entries()), + typescriptContext: A.from(mainSubContext!.typescriptContext.entries()), + importMode: mainSubContext!.importMode, + }).toStrictEqual({ + identifier: "MainParser", + context: [ + [ + mainDataParser, + buildedContext.context.get(mainDataParser as never), + ], + ], + importContext: [ + [ + "@duplojs/utils/dataParser", + { + namespace: ["DP"], + }, + ], + [ + "shared-package", + { + direct: ["SharedType"], + }, + ], + [ + "./DependencyParser", + { + direct: ["DependencyParser"], + }, + ], + [ + "./types", + { + direct: ["MainType"], + }, + ], + ], + typescriptContext: [], + importMode: "lite", + }); + + expect({ + identifier: dependencySubContext!.identifier, + context: A.from(dependencySubContext!.context.entries()), + importContext: A.from(dependencySubContext!.importContext.entries()), + typescriptContext: A.from(dependencySubContext!.typescriptContext.entries()), + importMode: dependencySubContext!.importMode, + }).toStrictEqual({ + identifier: "DependencyParser", + context: [ + [ + dependencyDataParser, + buildedContext.context.get(dependencyDataParser as never), + ], + ], + importContext: [ + [ + "@duplojs/utils/dataParser", + { + namespace: ["DP"], + }, + ], + [ + "shared-package", + { + direct: ["SharedType"], + }, + ], + ], + typescriptContext: [], + importMode: "lite", + }); + }); +}); diff --git a/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts index 7a1a92c..ff70a26 100644 --- a/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts +++ b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts @@ -40,6 +40,27 @@ describe("findIdentifiedDataParserInSteps", () => { ]); }); + it("finds identified data parsers from custom extract response contracts", () => { + const extractErrorBody = DPE.string().setIdentifier("extractErrorBody"); + + const route = useRouteBuilder("GET", "/test") + .extract( + { body: DPE.number() }, + ResponseContract.badRequest("extract.invalid", extractErrorBody), + ) + .handler( + ResponseContract.noContent("extract.ok"), + (__, { response }) => response("extract.ok"), + ); + + const result = findIdentifiedDataParserInSteps( + route.definition.steps, + { ignoreDataParser: new Set() }, + ); + + expect(result).toStrictEqual([extractErrorBody]); + }); + it("finds only identified data parsers from step response contracts", () => { const presetBody = DPE.string().setIdentifier("presetBody"); diff --git a/tests/plugins/codeGenerator/plugin.test.ts b/tests/plugins/codeGenerator/plugin.test.ts index 5b488e4..d37cea8 100644 --- a/tests/plugins/codeGenerator/plugin.test.ts +++ b/tests/plugins/codeGenerator/plugin.test.ts @@ -6,10 +6,19 @@ import { codeGeneratorPlugin } from "@plugin-codeGenerator"; describe("plugin implementation", () => { setEnvironment("TEST"); const spy = vi.fn((path: string, content: string) => Promise.resolve(E.ok())); + const spyExists = vi.fn((path: string) => Promise.resolve(E.left("file-system-exists"))); + const spyMakeDirectory = vi.fn((path: string) => Promise.resolve(E.ok())); + const spyRemove = vi.fn((path: string, params?: { recursive?: boolean }) => Promise.resolve(E.ok())); TESTImplementation.set("writeTextFile", spy); + TESTImplementation.set("exists", spyExists); + TESTImplementation.set("makeDirectory", spyMakeDirectory); + TESTImplementation.set("remove", spyRemove); beforeEach(() => { spy.mockClear(); + spyExists.mockClear(); + spyMakeDirectory.mockClear(); + spyRemove.mockClear(); }); const route = useRouteBuilder("GET", "/user") @@ -73,4 +82,113 @@ describe("plugin implementation", () => { expect(spy).not.toHaveBeenCalled(); }); + + it("generate data parser files from identified route data parsers", async() => { + const childDataParser = DPE.string().setIdentifier("ChildParser"); + const bodyDataParser = DPE.object({ + child: childDataParser, + count: DPE.number(), + }).setIdentifier("BodyParser"); + + const route = useRouteBuilder("GET", "/generated") + .extract({ + body: bodyDataParser, + }) + .handler( + ResponseContract.ok("generated.success", childDataParser), + (__, { response }) => response("generated.success", ""), + ); + + const hub = createHub({ environment: "DEV" }) + .plug(codeGeneratorPlugin({ + outputFile: "routes.d.ts", + generateDataParser: { + outputFolder: "generated", + }, + })) + .register(route); + + await launchHookServer( + hub.aggregatesHooksHubLifeCycle("beforeStartServer"), + hub, + {} as any, + ); + + expect(spy.mock.calls).toHaveLength(5); + expect(spy.mock.calls[0]).toMatchSnapshot(); + expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls[2]).toMatchSnapshot(); + expect(spy.mock.calls[3]).toMatchSnapshot(); + expect(spy.mock.calls[4]).toMatchSnapshot(); + }); + + it("generate data parser files only from explicit data parsers when disabledFromRoute is enabled", async() => { + const routeDataParser = DPE.string().setIdentifier("RouteParser"); + const explicitDataParser = DPE.number().setIdentifier("ExplicitParser"); + + const route = useRouteBuilder("GET", "/generated") + .extract({ + body: routeDataParser, + }) + .handler( + ResponseContract.noContent("generated.success"), + (__, { response }) => response("generated.success"), + ); + + const hub = createHub({ environment: "DEV" }) + .plug(codeGeneratorPlugin({ + outputFile: "routes.d.ts", + generateDataParser: { + outputFolder: "generated", + disabledFromRoute: true, + dataParsers: [explicitDataParser], + }, + })) + .register(route); + + await launchHookServer( + hub.aggregatesHooksHubLifeCycle("beforeStartServer"), + hub, + {} as any, + ); + + expect(spy.mock.calls).toHaveLength(4); + expect(spy.mock.calls[0]).toMatchSnapshot(); + expect(spy.mock.calls[1]).toMatchSnapshot(); + expect(spy.mock.calls[2]).toMatchSnapshot(); + expect(spy.mock.calls[3]).toMatchSnapshot(); + }); + + it("remove existing generated folder before recreating it", async() => { + spyExists.mockImplementationOnce((path: string) => Promise.resolve(E.ok())); + + const explicitDataParser = DPE.number().setIdentifier("ExplicitParser"); + + const route = useRouteBuilder("GET", "/generated") + .handler( + ResponseContract.noContent("generated.success"), + (__, { response }) => response("generated.success"), + ); + + const hub = createHub({ environment: "DEV" }) + .plug(codeGeneratorPlugin({ + outputFile: "routes.d.ts", + generateDataParser: { + outputFolder: "generated", + disabledFromRoute: true, + dataParsers: [explicitDataParser], + }, + })) + .register(route); + + await launchHookServer( + hub.aggregatesHooksHubLifeCycle("beforeStartServer"), + hub, + {} as any, + ); + + expect(spyExists).toHaveBeenCalledWith("generated"); + expect(spyRemove).toHaveBeenCalledWith("generated", { recursive: true }); + expect(spyMakeDirectory).toHaveBeenCalledWith("generated"); + }); }); From 00987f71c83a718d33fabc02fec0f6b3d0c0aee4 Mon Sep 17 00:00:00 2001 From: mathcovax Date: Thu, 4 Jun 2026 20:17:11 +0000 Subject: [PATCH 03/10] fix(39): types --- .../codeGenerator/findIdentifiedDataParserInSteps.test.ts | 8 +++++--- tests/plugins/codeGenerator/plugin.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts index ff70a26..2d72057 100644 --- a/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts +++ b/tests/plugins/codeGenerator/findIdentifiedDataParserInSteps.test.ts @@ -40,16 +40,18 @@ describe("findIdentifiedDataParserInSteps", () => { ]); }); - it("finds identified data parsers from custom extract response contracts", () => { + it("finds identified data parsers from handler response contracts", () => { const extractErrorBody = DPE.string().setIdentifier("extractErrorBody"); const route = useRouteBuilder("GET", "/test") .extract( { body: DPE.number() }, - ResponseContract.badRequest("extract.invalid", extractErrorBody), ) .handler( - ResponseContract.noContent("extract.ok"), + [ + ResponseContract.badRequest("extract.invalid", extractErrorBody), + ResponseContract.noContent("extract.ok"), + ], (__, { response }) => response("extract.ok"), ); diff --git a/tests/plugins/codeGenerator/plugin.test.ts b/tests/plugins/codeGenerator/plugin.test.ts index d37cea8..de71edf 100644 --- a/tests/plugins/codeGenerator/plugin.test.ts +++ b/tests/plugins/codeGenerator/plugin.test.ts @@ -1,12 +1,12 @@ import { createHub, launchHookServer, ResponseContract, useRouteBuilder } from "@core"; -import { DPE, E } from "@duplojs/utils"; +import { type AnyFunction, DPE, E } from "@duplojs/utils"; import { TESTImplementation, setEnvironment } from "@duplojs/server-utils"; import { codeGeneratorPlugin } from "@plugin-codeGenerator"; describe("plugin implementation", () => { setEnvironment("TEST"); const spy = vi.fn((path: string, content: string) => Promise.resolve(E.ok())); - const spyExists = vi.fn((path: string) => Promise.resolve(E.left("file-system-exists"))); + const spyExists = vi.fn((path: string) => Promise.resolve(E.left("file-system-exists"))); const spyMakeDirectory = vi.fn((path: string) => Promise.resolve(E.ok())); const spyRemove = vi.fn((path: string, params?: { recursive?: boolean }) => Promise.resolve(E.ok())); TESTImplementation.set("writeTextFile", spy); From e59feb1ed394b5f564a24a7fd28a19338fcd0c6e Mon Sep 17 00:00:00 2001 From: mathcovax Date: Fri, 5 Jun 2026 07:13:27 +0000 Subject: [PATCH 04/10] feat(39): update package --- .../examples/v0/guide/features/formData/route.ts | 6 ++++-- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- .../findIdentifiedDataParserInSteps.ts | 5 ----- .../createSubDataParserBuildedContext.test.ts | 2 +- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/docs/examples/v0/guide/features/formData/route.ts b/docs/examples/v0/guide/features/formData/route.ts index c4eeb91..27fd08b 100644 --- a/docs/examples/v0/guide/features/formData/route.ts +++ b/docs/examples/v0/guide/features/formData/route.ts @@ -1,5 +1,5 @@ import { controlBodyAsFormData, ResponseContract, useRouteBuilder } from "@duplojs/http"; -import { SDPE } from "@duplojs/server-utils"; +import { SDP, SDPE } from "@duplojs/server-utils"; import { A, asyncPipe, DPE, E, Path } from "@duplojs/utils"; useRouteBuilder("POST", "/documents", { @@ -10,7 +10,9 @@ useRouteBuilder("POST", "/documents", { userId: DPE.coerce.number(), files: DPE.object({ alt: DPE.string(), - file: SDPE.file().mimeType(["image/png", "image/jpeg"]), + file: SDPE.file().addChecker( + SDP.checkerFileMimeType(["image/png", "image/jpeg"]), + ), description: DPE.string(), }).array(), }, diff --git a/package-lock.json b/package-lock.json index bc0c389..92a985e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,8 +42,8 @@ }, "peerDependencies": { "@duplojs/data-parser-tools": ">=0.5.4 <1.0.0", - "@duplojs/server-utils": ">=0.4.0 <1.0.0", - "@duplojs/utils": ">=1.9.1 <2.0.0" + "@duplojs/server-utils": ">=0.4.1 <1.0.0", + "@duplojs/utils": ">=1.9.2 <2.0.0" } }, "docs": { @@ -1567,9 +1567,9 @@ "link": true }, "node_modules/@duplojs/server-utils": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.4.0.tgz", - "integrity": "sha512-wpsBdc/Rz4NTWQ4UywF3qo1mfosZDSzj7kMzBT2FF+Jyn5MEyUEyor4DjSHYssjakiOHxqu5zWiVaopqJpznuQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@duplojs/server-utils/-/server-utils-0.4.1.tgz", + "integrity": "sha512-1afpRSwr6fmImw+JhOQESQADtXf6qtQo43y3JgeARe0S2f95AoljAB+XtcIXE3SuxW8pXOnIhWVfXfeKT5qHxg==", "license": "MIT", "peer": true, "workspaces": [ @@ -1584,9 +1584,9 @@ } }, "node_modules/@duplojs/utils": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.9.1.tgz", - "integrity": "sha512-dQGYiobo7ZSyk1N5G1e5Acjk7nxeKsh9TiGFaHxbBtS0xXfH1h2fnk2nqCkIdSU9mJw6BdNA05uX35AMYcvMPA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@duplojs/utils/-/utils-1.9.2.tgz", + "integrity": "sha512-xCLtiK7KqyiD4hWU4HFSo7XA4MZ8Z6UM86DUIuc+L7geG806t0q2iOY/A3PhdjD/XhNidToGhJ+8pt+a/TCPiQ==", "license": "MIT", "peer": true, "workspaces": [ diff --git a/package.json b/package.json index 60e5231..fe207cb 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,8 @@ ], "peerDependencies": { "@duplojs/data-parser-tools": ">=0.5.4 <1.0.0", - "@duplojs/server-utils": ">=0.4.0 <1.0.0", - "@duplojs/utils": ">=1.9.1 <2.0.0" + "@duplojs/server-utils": ">=0.4.1 <1.0.0", + "@duplojs/utils": ">=1.9.2 <2.0.0" }, "devDependencies": { "@commitlint/cli": "19.8.1", diff --git a/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts b/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts index aa090cf..f71d326 100644 --- a/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts +++ b/scripts/plugins/codeGenerator/findIdentifiedDataParserInSteps.ts @@ -38,11 +38,6 @@ export function findIdentifiedDataParserInSteps( ), ), ), - A.concat( - extractStep.definition.responseContract?.body - ? [extractStep.definition.responseContract?.body] - : [], - ), ), ), P.when( diff --git a/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts b/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts index a1397dc..bda306a 100644 --- a/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts +++ b/tests/plugins/codeGenerator/createSubDataParserBuildedContext.test.ts @@ -27,7 +27,7 @@ describe("createSubDataParserBuildedContext", () => { identifier: factory.createIdentifier("DependencyParser"), expression: factory.createIdentifier("dependencyExpression"), typeIdentifier: null, - dependencies: new Set(), + dependencies: new Set([dependencyDataParser]), }, ], ]), From 2f7e0a4cb17e17a7cacade0e9f29a5c56a23527e Mon Sep 17 00:00:00 2001 From: mathcovax Date: Fri, 5 Jun 2026 07:56:49 +0000 Subject: [PATCH 05/10] feat(39): add type test on generate data parser --- .../__snapshots__/index.test.ts.snap | 52 +++++++++++++++++++ integration/codeGenerator/index.test.ts | 26 ++++++++++ 2 files changed, 78 insertions(+) diff --git a/integration/codeGenerator/__snapshots__/index.test.ts.snap b/integration/codeGenerator/__snapshots__/index.test.ts.snap index f507e02..7e2c4bd 100644 --- a/integration/codeGenerator/__snapshots__/index.test.ts.snap +++ b/integration/codeGenerator/__snapshots__/index.test.ts.snap @@ -206,3 +206,55 @@ export type Routes = { }; };" `; + +exports[`codeGenerator > correct generate file 2`] = ` +"export * from "./userIdDataParser"; +export * from "./userNameDataParser"; +export * from "./userDataParser";" +`; + +exports[`codeGenerator > correct generate file 3`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export type UserId = number; + +export type UserName = string; + +export type User = { + id: UserId; + name: UserName; + age: number; + friends?: User[] | undefined; +}; + +export type $userDataParser = User;" +`; + +exports[`codeGenerator > correct generate file 4`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import { userIdDataParser } from "./userIdDataParser"; + +import { userNameDataParser } from "./userNameDataParser"; + +import { $userDataParser } from "./types"; + +export const userDataParser: DP.DataParser<$userDataParser> = DP.object({ + id: userIdDataParser, + name: userNameDataParser, + age: DP.number(), + friends: DP.optional(DP.array(DP.lazy(() => userDataParser))) +});" +`; + +exports[`codeGenerator > correct generate file 5`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const userIdDataParser = DP.number();" +`; + +exports[`codeGenerator > correct generate file 6`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const userNameDataParser = DP.string();" +`; diff --git a/integration/codeGenerator/index.test.ts b/integration/codeGenerator/index.test.ts index 68f59b3..892d814 100644 --- a/integration/codeGenerator/index.test.ts +++ b/integration/codeGenerator/index.test.ts @@ -2,6 +2,9 @@ import { hub } from "@core"; import { existsSync, readFileSync, rmSync } from "fs"; import { codeGeneratorPlugin } from "@duplojs/http/codeGenerator"; import { launchHookServer } from "@duplojs/http"; +import { execSync } from "child_process"; +import { asyncPipe, E, Path } from "@duplojs/utils"; +import { SF } from "@duplojs/server-utils"; describe("codeGenerator", () => { const fileName = `${import.meta.dirname}/generateCode.generate.ts`; @@ -32,5 +35,28 @@ describe("codeGenerator", () => { ); expect(readFileSync(fileName, "utf-8")).toMatchSnapshot(); + + execSync("npx tsc -p codeGenerator/tsconfig.generate.json", { + stdio: "inherit", + cwd: import.meta.dirname, + }); + + const result = await asyncPipe( + { + index: SF.readTextFile(Path.resolveRelative([folderName, "index.ts"])), + types: SF.readTextFile(Path.resolveRelative([folderName, "types.ts"])), + userDataParser: SF.readTextFile(Path.resolveRelative([folderName, "userDataParser.ts"])), + userIdDataParser: SF.readTextFile(Path.resolveRelative([folderName, "userIdDataParser.ts"])), + userNameDataParser: SF.readTextFile(Path.resolveRelative([folderName, "userNameDataParser.ts"])), + }, + E.asyncGroup, + E.unwrapRightOrThrow, + ); + + expect(result.index).toMatchSnapshot(); + expect(result.types).toMatchSnapshot(); + expect(result.userDataParser).toMatchSnapshot(); + expect(result.userIdDataParser).toMatchSnapshot(); + expect(result.userNameDataParser).toMatchSnapshot(); }); }); From bb759344060052738ab79025793d98071293198b Mon Sep 17 00:00:00 2001 From: mathcovax Date: Fri, 5 Jun 2026 08:15:25 +0000 Subject: [PATCH 06/10] fix(39): exec tsc test --- integration/codeGenerator/index.test.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/integration/codeGenerator/index.test.ts b/integration/codeGenerator/index.test.ts index 892d814..b313c1c 100644 --- a/integration/codeGenerator/index.test.ts +++ b/integration/codeGenerator/index.test.ts @@ -2,13 +2,15 @@ import { hub } from "@core"; import { existsSync, readFileSync, rmSync } from "fs"; import { codeGeneratorPlugin } from "@duplojs/http/codeGenerator"; import { launchHookServer } from "@duplojs/http"; -import { execSync } from "child_process"; +import { execFileSync, execSync } from "child_process"; import { asyncPipe, E, Path } from "@duplojs/utils"; import { SF } from "@duplojs/server-utils"; describe("codeGenerator", () => { const fileName = `${import.meta.dirname}/generateCode.generate.ts`; const folderName = `${import.meta.dirname}/generateDataParser.generate`; + const tscPath = require.resolve("typescript/bin/tsc"); + const rootProject = Path.resolveRelative([import.meta.dirname, "../.."]); beforeAll(() => { if (existsSync(fileName)) { rmSync(fileName); @@ -36,10 +38,18 @@ describe("codeGenerator", () => { expect(readFileSync(fileName, "utf-8")).toMatchSnapshot(); - execSync("npx tsc -p codeGenerator/tsconfig.generate.json", { - stdio: "inherit", - cwd: import.meta.dirname, - }); + execFileSync( + process.execPath, + [ + tscPath, + "-p", + Path.resolveRelative([rootProject, "integration/codeGenerator/tsconfig.generate.json"]), + ], + { + stdio: "inherit", + cwd: rootProject, + }, + ); const result = await asyncPipe( { From aa01850043df75b089277a85de13405c3a5a94f2 Mon Sep 17 00:00:00 2001 From: mathcovax Date: Sun, 7 Jun 2026 13:34:35 +0000 Subject: [PATCH 07/10] fix(39): add assertTypeScriptProject --- integration/_utils/checkFilesType.ts | 61 +++++++++++++++++++++++++ integration/_utils/index.ts | 1 + integration/codeGenerator/index.test.ts | 17 +------ integration/codeGenerator/tsconfig.json | 3 +- vitest.config.js | 6 +++ 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 integration/_utils/checkFilesType.ts diff --git a/integration/_utils/checkFilesType.ts b/integration/_utils/checkFilesType.ts new file mode 100644 index 0000000..9a03127 --- /dev/null +++ b/integration/_utils/checkFilesType.ts @@ -0,0 +1,61 @@ +import { getCurrentWorkDirectoryOrThrow } from "@duplojs/server-utils"; +import { Path } from "@duplojs/utils"; +import ts from "typescript"; + +export function assertTypeScriptProject(tsconfigPath: string) { + const resolvedTsconfigPath = Path.resolveRelative([getCurrentWorkDirectoryOrThrow(), tsconfigPath]); + const tsconfigDirectory = Path.getParentFolderPath(resolvedTsconfigPath) ?? ""; + + const configFileResult = ts.readConfigFile( + resolvedTsconfigPath, + ts.sys.readFile, + ); + + if (configFileResult.error) { + throw new Error(formatTypeScriptDiagnostics([configFileResult.error])); + } + + const parsedConfig = ts.parseJsonConfigFileContent( + configFileResult.config, + ts.sys, + tsconfigDirectory, + { + noEmit: true, + }, + resolvedTsconfigPath, + ); + + const program = ts.createProgram({ + rootNames: parsedConfig.fileNames, + options: parsedConfig.options, + projectReferences: parsedConfig.projectReferences, + }); + + const diagnostics = [ + ...parsedConfig.errors, + ...ts.getPreEmitDiagnostics(program), + ]; + + if (diagnostics.length > 0) { + throw new Error(formatTypeScriptDiagnostics(diagnostics)); + } +} + +function formatTypeScriptDiagnostics( + diagnostics: readonly ts.Diagnostic[], +) { + const formatHost: ts.FormatDiagnosticsHost = { + getCanonicalFileName: (fileName) => ( + ts.sys.useCaseSensitiveFileNames + ? fileName + : fileName.toLowerCase() + ), + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => ts.sys.newLine, + }; + + return ts.formatDiagnosticsWithColorAndContext( + diagnostics, + formatHost, + ); +} diff --git a/integration/_utils/index.ts b/integration/_utils/index.ts index 5c7000b..e6fa5cd 100644 --- a/integration/_utils/index.ts +++ b/integration/_utils/index.ts @@ -1 +1,2 @@ export * from "./createFileToSend"; +export * from "./checkFilesType"; diff --git a/integration/codeGenerator/index.test.ts b/integration/codeGenerator/index.test.ts index b313c1c..d5673df 100644 --- a/integration/codeGenerator/index.test.ts +++ b/integration/codeGenerator/index.test.ts @@ -2,15 +2,13 @@ import { hub } from "@core"; import { existsSync, readFileSync, rmSync } from "fs"; import { codeGeneratorPlugin } from "@duplojs/http/codeGenerator"; import { launchHookServer } from "@duplojs/http"; -import { execFileSync, execSync } from "child_process"; import { asyncPipe, E, Path } from "@duplojs/utils"; import { SF } from "@duplojs/server-utils"; +import { assertTypeScriptProject } from "@utils"; describe("codeGenerator", () => { const fileName = `${import.meta.dirname}/generateCode.generate.ts`; const folderName = `${import.meta.dirname}/generateDataParser.generate`; - const tscPath = require.resolve("typescript/bin/tsc"); - const rootProject = Path.resolveRelative([import.meta.dirname, "../.."]); beforeAll(() => { if (existsSync(fileName)) { rmSync(fileName); @@ -38,18 +36,7 @@ describe("codeGenerator", () => { expect(readFileSync(fileName, "utf-8")).toMatchSnapshot(); - execFileSync( - process.execPath, - [ - tscPath, - "-p", - Path.resolveRelative([rootProject, "integration/codeGenerator/tsconfig.generate.json"]), - ], - { - stdio: "inherit", - cwd: rootProject, - }, - ); + assertTypeScriptProject("codeGenerator/tsconfig.generate.json"); const result = await asyncPipe( { diff --git a/integration/codeGenerator/tsconfig.json b/integration/codeGenerator/tsconfig.json index 70fe73f..acd57c7 100644 --- a/integration/codeGenerator/tsconfig.json +++ b/integration/codeGenerator/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@core": ["../core/index.ts"] + "@core": ["../core/index.ts"], + "@utils": ["../_utils/index.ts"], }, "types": ["vitest/globals", "node"] }, diff --git a/vitest.config.js b/vitest.config.js index 93bf1a5..fce3964 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -9,6 +9,12 @@ export default defineConfig({ "tests/**/*.test.ts", "integration/**/*.test.ts", ], + typecheck: { + include: [ + "tests/**/*.test-d.ts", + "integration/**/*.test-d.ts", + ], + }, coverage: { provider: "istanbul", reporter: ["text", "json", "html", "json-summary"], From 1173e5a7abcefafbd816598b96f9077931891dff Mon Sep 17 00:00:00 2001 From: mathcovax Date: Sun, 7 Jun 2026 13:45:40 +0000 Subject: [PATCH 08/10] fix(39): naming --- integration/codeGenerator/index.test.ts | 6 +++--- .../codeGenerator/tsconfig.generatedDataParser.json | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 integration/codeGenerator/tsconfig.generatedDataParser.json diff --git a/integration/codeGenerator/index.test.ts b/integration/codeGenerator/index.test.ts index d5673df..7f032ea 100644 --- a/integration/codeGenerator/index.test.ts +++ b/integration/codeGenerator/index.test.ts @@ -7,8 +7,8 @@ import { SF } from "@duplojs/server-utils"; import { assertTypeScriptProject } from "@utils"; describe("codeGenerator", () => { - const fileName = `${import.meta.dirname}/generateCode.generate.ts`; - const folderName = `${import.meta.dirname}/generateDataParser.generate`; + const fileName = `${import.meta.dirname}/type.generate.ts`; + const folderName = `${import.meta.dirname}/dataParser.generate`; beforeAll(() => { if (existsSync(fileName)) { rmSync(fileName); @@ -36,7 +36,7 @@ describe("codeGenerator", () => { expect(readFileSync(fileName, "utf-8")).toMatchSnapshot(); - assertTypeScriptProject("codeGenerator/tsconfig.generate.json"); + assertTypeScriptProject("codeGenerator/tsconfig.generatedDataParser.json"); const result = await asyncPipe( { diff --git a/integration/codeGenerator/tsconfig.generatedDataParser.json b/integration/codeGenerator/tsconfig.generatedDataParser.json new file mode 100644 index 0000000..1c2b8e8 --- /dev/null +++ b/integration/codeGenerator/tsconfig.generatedDataParser.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "paths": {}, + }, + "include": ["dataParser.generate/**/*.ts",], +} From 5c15464c1a212a15bb7386c73662c863a9b878ed Mon Sep 17 00:00:00 2001 From: mathcovax Date: Sun, 7 Jun 2026 14:08:18 +0000 Subject: [PATCH 09/10] feat(39): update http doc --- .../v0/client/unexpectedResponseError.d.ts | 2 +- docs/libs/v0/core/builders/route/extract.d.ts | 7 ++- docs/libs/v0/core/clean/constraint.cjs | 25 +++----- docs/libs/v0/core/clean/constraint.d.ts | 14 +++-- docs/libs/v0/core/clean/constraint.mjs | 27 +++----- docs/libs/v0/core/clean/constraintsSet.cjs | 28 +++------ docs/libs/v0/core/clean/constraintsSet.d.ts | 14 +++-- docs/libs/v0/core/clean/constraintsSet.mjs | 30 +++------ docs/libs/v0/core/clean/entity.cjs | 6 +- docs/libs/v0/core/clean/entity.d.ts | 17 +++--- docs/libs/v0/core/clean/entity.mjs | 6 +- docs/libs/v0/core/clean/newType.cjs | 18 +++--- docs/libs/v0/core/clean/newType.d.ts | 12 ++-- docs/libs/v0/core/clean/newType.mjs | 20 +++--- docs/libs/v0/core/clean/primitive.cjs | 15 ++--- docs/libs/v0/core/clean/primitive.d.ts | 14 +++-- docs/libs/v0/core/clean/primitive.mjs | 17 +++--- docs/libs/v0/core/response/contract.d.ts | 2 +- docs/libs/v0/core/steps/extract.d.ts | 3 +- .../core/types/extractParamsKeyFromPath.cjs | 2 + .../core/types/extractParamsKeyFromPath.d.ts | 1 + .../core/types/extractParamsKeyFromPath.mjs | 1 + docs/libs/v0/core/types/index.cjs | 1 + docs/libs/v0/core/types/index.d.ts | 1 + docs/libs/v0/core/types/index.mjs | 1 + .../node/bodyReaders/formData/error.cjs | 2 +- .../node/bodyReaders/formData/error.mjs | 2 +- .../formData/readRequestFormData.cjs | 2 +- .../formData/readRequestFormData.mjs | 2 +- .../createSubDataParserBuildedContext.cjs | 36 +++++++++++ .../createSubDataParserBuildedContext.d.ts | 5 ++ .../createSubDataParserBuildedContext.mjs | 34 +++++++++++ .../findIdentifiedDataParserInSteps.cjs | 28 +++++++++ .../findIdentifiedDataParserInSteps.d.ts | 12 ++++ .../findIdentifiedDataParserInSteps.mjs | 25 ++++++++ docs/libs/v0/plugins/codeGenerator/plugin.cjs | 57 ++++++++++++++--- .../libs/v0/plugins/codeGenerator/plugin.d.ts | 7 +++ docs/libs/v0/plugins/codeGenerator/plugin.mjs | 61 +++++++++++++++---- .../codeGenerator/routeToDataParser.d.ts | 10 +++ .../codeGenerator/typescriptTransformer.cjs | 8 +++ .../codeGenerator/typescriptTransformer.d.ts | 8 ++- .../codeGenerator/typescriptTransformer.mjs | 9 ++- docs/libs/v0/plugins/cookie/serialize.d.ts | 2 +- .../openApiGenerator/makeOpenApiRoute.d.ts | 1 + .../libs/v0/plugins/static/makeRouteFile.d.ts | 4 -- .../v0/plugins/static/makeRouteFolder.d.ts | 4 -- 46 files changed, 414 insertions(+), 189 deletions(-) create mode 100644 docs/libs/v0/core/types/extractParamsKeyFromPath.cjs create mode 100644 docs/libs/v0/core/types/extractParamsKeyFromPath.d.ts create mode 100644 docs/libs/v0/core/types/extractParamsKeyFromPath.mjs create mode 100644 docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.cjs create mode 100644 docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.d.ts create mode 100644 docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.mjs create mode 100644 docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.cjs create mode 100644 docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.d.ts create mode 100644 docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.mjs diff --git a/docs/libs/v0/client/unexpectedResponseError.d.ts b/docs/libs/v0/client/unexpectedResponseError.d.ts index 39017e3..c7142d8 100644 --- a/docs/libs/v0/client/unexpectedResponseError.d.ts +++ b/docs/libs/v0/client/unexpectedResponseError.d.ts @@ -5,7 +5,7 @@ export interface RequestErrorContent { } declare const UnexpectedInformationResponseError_base: new (params: { "@DuplojsHttpClient/unexpected-information-response-error"?: unknown; -}, parentParams: readonly [message?: string | undefined, options?: ErrorOptions | undefined]) => import("@duplojs/utils").Kind, unknown> & import("@duplojs/utils").Kind, unknown> & Error; +}, parentParams: readonly [message?: string | undefined, options?: ErrorOptions | undefined]) => Error & import("@duplojs/utils").Kind, unknown> & import("@duplojs/utils").Kind, unknown>; export declare class UnexpectedInformationResponseError extends UnexpectedInformationResponseError_base { information: string | string[]; response: RequestErrorContent | ClientResponse; diff --git a/docs/libs/v0/core/builders/route/extract.d.ts b/docs/libs/v0/core/builders/route/extract.d.ts index 4717732..df73dd5 100644 --- a/docs/libs/v0/core/builders/route/extract.d.ts +++ b/docs/libs/v0/core/builders/route/extract.d.ts @@ -5,9 +5,13 @@ import { type DP, type ObjectEntry, type O, type SimplifyTopLevel, type NeverCoa import { type ClientErrorResponseCode, type ResponseContract } from "../../response"; import { type Request } from "../../request"; import { type Metadata } from "../../metadata"; +import { type ExtractParamsKeyFromPath } from "../../types"; +type HandleParamsInference = (GenericShape & { + params?: ExtractParamsKeyFromPath extends infer InferredKey extends string ? (Partial> & Record & Record, never>) : {}; +}); declare module "./builder" { interface RouteBuilder { - extract, GenericResponseContract extends (ResponseContract.Contract | undefined) = never, const GenericMetadata extends readonly Metadata[] = readonly []>(shape: GenericShape, responseContract?: GenericResponseContract, ...metadata: GenericMetadata): RouteBuilder, GenericResponseContract extends (ResponseContract.Contract | undefined) = never, const GenericMetadata extends readonly Metadata[] = readonly []>(shape: HandleParamsInference, responseContract?: GenericResponseContract, ...metadata: GenericMetadata): RouteBuilder : never>>; } } +export {}; diff --git a/docs/libs/v0/core/clean/constraint.cjs b/docs/libs/v0/core/clean/constraint.cjs index 1739f93..717d30e 100644 --- a/docs/libs/v0/core/clean/constraint.cjs +++ b/docs/libs/v0/core/clean/constraint.cjs @@ -1,24 +1,13 @@ 'use strict'; var utils = require('@duplojs/utils'); -var clean = require('@duplojs/utils/clean'); +require('@duplojs/utils/clean'); -clean.createConstraint.overrideHandler.setMethod("toExtractParser", (self) => { - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...self.checkers); - const valueContainer = clean.constrainedTypeKind.setTo({}, { [self.name]: null }); - const dataParser = utils.DPE.transform(dataParserWithCheckers, (input) => ({ - ...valueContainer, - [utils.keyWrappedValue]: input, - })); - return dataParser; +utils.C.createConstraint.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = utils.C.toMapDataParser(self, params); + return utils.DPE.lazy(() => innerDataParser); }); -clean.createConstraint.overrideHandler.setMethod("toEndpointSchema", (self) => { - const dataParser = self - .primitiveHandler - .dataParser - .addChecker(...self.checkers); - return utils.DPE.lazy(() => dataParser); +utils.C.createConstraint.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return utils.DPE.lazy(() => innerDataParser); }); diff --git a/docs/libs/v0/core/clean/constraint.d.ts b/docs/libs/v0/core/clean/constraint.d.ts index 050bcf0..e627ce7 100644 --- a/docs/libs/v0/core/clean/constraint.d.ts +++ b/docs/libs/v0/core/clean/constraint.d.ts @@ -1,8 +1,12 @@ -import { type DP, DPE } from "@duplojs/utils"; -import { type ConstrainedType, type EligiblePrimitive } from "@duplojs/utils/clean"; +import { type DP, DPE, C } from "@duplojs/utils"; +import "@duplojs/utils/clean"; +interface ToExtractParserParams { + coerce?: boolean; +} declare module "@duplojs/utils/clean" { - interface ConstraintHandler { - toExtractParser(): DPE.ContractExtended, unknown>; - toEndpointSchema(): DPE.ContractExtended; + interface ConstraintHandler { + toExtractParser(params?: ToExtractParserParams): DPE.DataParserExtended, unknown>; + toEndpointSchema(): DPE.DataParserExtended; } } +export {}; diff --git a/docs/libs/v0/core/clean/constraint.mjs b/docs/libs/v0/core/clean/constraint.mjs index 962132c..536f92f 100644 --- a/docs/libs/v0/core/clean/constraint.mjs +++ b/docs/libs/v0/core/clean/constraint.mjs @@ -1,22 +1,11 @@ -import { DPE, keyWrappedValue } from '@duplojs/utils'; -import { createConstraint, constrainedTypeKind } from '@duplojs/utils/clean'; +import { C, DPE } from '@duplojs/utils'; +import '@duplojs/utils/clean'; -createConstraint.overrideHandler.setMethod("toExtractParser", (self) => { - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...self.checkers); - const valueContainer = constrainedTypeKind.setTo({}, { [self.name]: null }); - const dataParser = DPE.transform(dataParserWithCheckers, (input) => ({ - ...valueContainer, - [keyWrappedValue]: input, - })); - return dataParser; +C.createConstraint.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = C.toMapDataParser(self, params); + return DPE.lazy(() => innerDataParser); }); -createConstraint.overrideHandler.setMethod("toEndpointSchema", (self) => { - const dataParser = self - .primitiveHandler - .dataParser - .addChecker(...self.checkers); - return DPE.lazy(() => dataParser); +C.createConstraint.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return DPE.lazy(() => innerDataParser); }); diff --git a/docs/libs/v0/core/clean/constraintsSet.cjs b/docs/libs/v0/core/clean/constraintsSet.cjs index c9838d4..92a9e7d 100644 --- a/docs/libs/v0/core/clean/constraintsSet.cjs +++ b/docs/libs/v0/core/clean/constraintsSet.cjs @@ -1,27 +1,13 @@ 'use strict'; var utils = require('@duplojs/utils'); -var clean = require('@duplojs/utils/clean'); +require('@duplojs/utils/clean'); -clean.createConstraintsSet.overrideHandler.setMethod("toExtractParser", (self) => { - const checkers = utils.A.flatMap(self.constraints, ({ checkers }) => checkers); - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...checkers); - const constraintsKindValue = utils.pipe(self.constraints, utils.A.map(({ name }) => utils.O.entry(name, null)), utils.O.fromEntries); - const valueContainer = clean.constrainedTypeKind.setTo({}, constraintsKindValue); - const dataParser = utils.DPE.transform(dataParserWithCheckers, (input) => ({ - ...valueContainer, - [utils.keyWrappedValue]: input, - })); - return dataParser; +utils.C.createConstraintsSet.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = utils.C.toMapDataParser(self, params); + return utils.DPE.lazy(() => innerDataParser); }); -clean.createConstraintsSet.overrideHandler.setMethod("toEndpointSchema", (self) => { - const checkers = utils.A.flatMap(self.constraints, ({ checkers }) => checkers); - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...checkers); - return utils.DPE.lazy(() => dataParserWithCheckers); +utils.C.createConstraintsSet.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return utils.DPE.lazy(() => innerDataParser); }); diff --git a/docs/libs/v0/core/clean/constraintsSet.d.ts b/docs/libs/v0/core/clean/constraintsSet.d.ts index 6f6aa28..e9d5055 100644 --- a/docs/libs/v0/core/clean/constraintsSet.d.ts +++ b/docs/libs/v0/core/clean/constraintsSet.d.ts @@ -1,8 +1,12 @@ -import { DPE, type UnionToIntersection } from "@duplojs/utils"; -import { type EligiblePrimitive, type GetConstraint, type Primitive } from "@duplojs/utils/clean"; +import { DPE, type UnionToIntersection, C } from "@duplojs/utils"; +import "@duplojs/utils/clean"; +interface ToExtractParserParams { + coerce?: boolean; +} declare module "@duplojs/utils/clean" { - interface ConstraintsSetHandler { - toExtractParser(): DPE.ContractExtended<(Primitive & UnionToIntersection : never : never>), unknown>; - toEndpointSchema(): DPE.ContractExtended; + interface ConstraintsSetHandler { + toExtractParser(params?: ToExtractParserParams): DPE.DataParserExtended<(C.Primitive & UnionToIntersection : never : never>), unknown>; + toEndpointSchema(): DPE.DataParserExtended; } } +export {}; diff --git a/docs/libs/v0/core/clean/constraintsSet.mjs b/docs/libs/v0/core/clean/constraintsSet.mjs index dbbe7d4..96c2ca4 100644 --- a/docs/libs/v0/core/clean/constraintsSet.mjs +++ b/docs/libs/v0/core/clean/constraintsSet.mjs @@ -1,25 +1,11 @@ -import { A, pipe, O, DPE, keyWrappedValue } from '@duplojs/utils'; -import { createConstraintsSet, constrainedTypeKind } from '@duplojs/utils/clean'; +import { C, DPE } from '@duplojs/utils'; +import '@duplojs/utils/clean'; -createConstraintsSet.overrideHandler.setMethod("toExtractParser", (self) => { - const checkers = A.flatMap(self.constraints, ({ checkers }) => checkers); - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...checkers); - const constraintsKindValue = pipe(self.constraints, A.map(({ name }) => O.entry(name, null)), O.fromEntries); - const valueContainer = constrainedTypeKind.setTo({}, constraintsKindValue); - const dataParser = DPE.transform(dataParserWithCheckers, (input) => ({ - ...valueContainer, - [keyWrappedValue]: input, - })); - return dataParser; +C.createConstraintsSet.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = C.toMapDataParser(self, params); + return DPE.lazy(() => innerDataParser); }); -createConstraintsSet.overrideHandler.setMethod("toEndpointSchema", (self) => { - const checkers = A.flatMap(self.constraints, ({ checkers }) => checkers); - const dataParserWithCheckers = self - .primitiveHandler - .dataParser - .addChecker(...checkers); - return DPE.lazy(() => dataParserWithCheckers); +C.createConstraintsSet.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return DPE.lazy(() => innerDataParser); }); diff --git a/docs/libs/v0/core/clean/entity.cjs b/docs/libs/v0/core/clean/entity.cjs index 495e877..c9ca467 100644 --- a/docs/libs/v0/core/clean/entity.cjs +++ b/docs/libs/v0/core/clean/entity.cjs @@ -3,11 +3,11 @@ var utils = require('@duplojs/utils'); require('@duplojs/utils/clean'); -utils.C.createEntity.overrideHandler.setMethod("toExtractParser", (self, keys) => { +utils.C.createEntity.overrideHandler.setMethod("toExtractParser", (self, keys, params) => { if (typeof keys === "string") { - return utils.C.entityPropertyDefinitionToDataParser(self.propertiesDefinition[keys], (newTypeHandler) => newTypeHandler.toExtractParser()); + return utils.C.entityPropertyDefinitionToDataParser(self.propertiesDefinition[keys], (newTypeHandler) => newTypeHandler.toExtractParser(params)); } - return utils.pipe(self.propertiesDefinition, utils.O.entries, utils.A.filter(([key]) => keys === undefined || utils.A.includes(keys, key)), utils.A.map(([key, value]) => utils.O.entry(key, utils.C.entityPropertyDefinitionToDataParser(value, (newTypeHandler) => newTypeHandler.toExtractParser()))), utils.O.fromEntries, utils.DPE.object); + return utils.pipe(self.propertiesDefinition, utils.O.entries, utils.A.filter(([key]) => keys === undefined || utils.A.includes(keys, key)), utils.A.map(([key, value]) => utils.O.entry(key, utils.C.entityPropertyDefinitionToDataParser(value, (newTypeHandler) => newTypeHandler.toExtractParser(params)))), utils.O.fromEntries, utils.DPE.object); }); utils.C.createEntity.overrideHandler.setMethod("toEndpointSchema", (self, keys, params) => { if (typeof keys === "string") { diff --git a/docs/libs/v0/core/clean/entity.d.ts b/docs/libs/v0/core/clean/entity.d.ts index 491cbd8..84b55af 100644 --- a/docs/libs/v0/core/clean/entity.d.ts +++ b/docs/libs/v0/core/clean/entity.d.ts @@ -1,20 +1,23 @@ import { DP, DPE, type IsEqual, type SimplifyTopLevel, type IsExtends, C, type MaybeArray } from "@duplojs/utils"; import "@duplojs/utils/clean"; +interface ToExtractParserParams { + coerce?: boolean; +} interface ToEndpointSchemaParams { addEntityName?: boolean | string; } declare module "@duplojs/utils/clean" { interface EntityHandler { - toExtractParser, const GenericKey extends MaybeArray = readonly (keyof GenericEntityProperties)[]>(keys?: GenericKey): GenericKey extends readonly any[] ? ReturnType; - }>>> : DP.Contract], unknown>; + toExtractParser, const GenericKey extends MaybeArray = readonly (keyof GenericEntityProperties)[]>(keys?: GenericKey, params?: ToExtractParserParams): GenericKey extends readonly any[] ? ReturnType; + }>>> : DP.DataParser], unknown>; toEndpointSchema, const GenericKey extends MaybeArray = readonly (keyof GenericEntityRawProperties)[], const GenericParams extends (ToEndpointSchemaParams) = {}>(keys?: GenericKey, params?: (GenericParams | ToEndpointSchemaParams) & (GenericKey extends string ? never : unknown)): GenericKey extends readonly any[] ? ReturnType; + [Prop in GenericKey[number]]: DP.DataParser; } & (IsEqual extends true ? { - [Prop in "_entityName"]: DP.Contract; + [Prop in "_entityName"]: DP.DataParser; } : {}) & (IsExtends extends true ? { - [Prop in "_entityName"]: DP.Contract<`${GenericName}/${GenericParams["addEntityName"]}`, unknown>; - } : {})>>> : DP.Contract], unknown>; + [Prop in "_entityName"]: DP.DataParser<`${GenericName}/${GenericParams["addEntityName"]}`, unknown>; + } : {})>>> : DP.DataParser], unknown>; } } export {}; diff --git a/docs/libs/v0/core/clean/entity.mjs b/docs/libs/v0/core/clean/entity.mjs index 7367b1b..0eb3699 100644 --- a/docs/libs/v0/core/clean/entity.mjs +++ b/docs/libs/v0/core/clean/entity.mjs @@ -1,11 +1,11 @@ import { C, pipe, O, A, DPE, DP } from '@duplojs/utils'; import '@duplojs/utils/clean'; -C.createEntity.overrideHandler.setMethod("toExtractParser", (self, keys) => { +C.createEntity.overrideHandler.setMethod("toExtractParser", (self, keys, params) => { if (typeof keys === "string") { - return C.entityPropertyDefinitionToDataParser(self.propertiesDefinition[keys], (newTypeHandler) => newTypeHandler.toExtractParser()); + return C.entityPropertyDefinitionToDataParser(self.propertiesDefinition[keys], (newTypeHandler) => newTypeHandler.toExtractParser(params)); } - return pipe(self.propertiesDefinition, O.entries, A.filter(([key]) => keys === undefined || A.includes(keys, key)), A.map(([key, value]) => O.entry(key, C.entityPropertyDefinitionToDataParser(value, (newTypeHandler) => newTypeHandler.toExtractParser()))), O.fromEntries, DPE.object); + return pipe(self.propertiesDefinition, O.entries, A.filter(([key]) => keys === undefined || A.includes(keys, key)), A.map(([key, value]) => O.entry(key, C.entityPropertyDefinitionToDataParser(value, (newTypeHandler) => newTypeHandler.toExtractParser(params)))), O.fromEntries, DPE.object); }); C.createEntity.overrideHandler.setMethod("toEndpointSchema", (self, keys, params) => { if (typeof keys === "string") { diff --git a/docs/libs/v0/core/clean/newType.cjs b/docs/libs/v0/core/clean/newType.cjs index 135c2b6..4950a78 100644 --- a/docs/libs/v0/core/clean/newType.cjs +++ b/docs/libs/v0/core/clean/newType.cjs @@ -1,15 +1,13 @@ 'use strict'; var utils = require('@duplojs/utils'); -var clean = require('@duplojs/utils/clean'); +require('@duplojs/utils/clean'); -clean.createNewType.overrideHandler.setMethod("toExtractParser", (self) => { - const constraintsKindValue = utils.pipe(self.constraints, utils.A.map(({ name }) => utils.O.entry(name, null)), utils.O.fromEntries); - const valueContainer = clean.newTypeKind.setTo(clean.constrainedTypeKind.setTo({}, constraintsKindValue), self.name); - const dataParser = utils.DPE.transform(self.dataParser, (input) => ({ - ...valueContainer, - [utils.keyWrappedValue]: input, - })); - return dataParser; +utils.C.createNewType.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = utils.C.toMapDataParser(self, params); + return utils.DPE.lazy(() => innerDataParser); +}); +utils.C.createNewType.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return utils.DPE.lazy(() => innerDataParser); }); -clean.createNewType.overrideHandler.setMethod("toEndpointSchema", (self) => utils.DPE.lazy(() => self.dataParser)); diff --git a/docs/libs/v0/core/clean/newType.d.ts b/docs/libs/v0/core/clean/newType.d.ts index 24a94a8..89d8cb0 100644 --- a/docs/libs/v0/core/clean/newType.d.ts +++ b/docs/libs/v0/core/clean/newType.d.ts @@ -1,8 +1,12 @@ -import { DPE } from "@duplojs/utils"; -import { type NewType } from "@duplojs/utils/clean"; +import { C, DPE } from "@duplojs/utils"; +import "@duplojs/utils/clean"; +interface ToExtractParserParams { + coerce?: boolean; +} declare module "@duplojs/utils/clean" { interface NewTypeHandler { - toExtractParser(): DPE.ContractExtended, unknown>; - toEndpointSchema(): DPE.ContractExtended; + toExtractParser(params?: ToExtractParserParams): DPE.DataParserExtended, unknown>; + toEndpointSchema(): DPE.DataParserExtended; } } +export {}; diff --git a/docs/libs/v0/core/clean/newType.mjs b/docs/libs/v0/core/clean/newType.mjs index 30c3a55..9ec01e0 100644 --- a/docs/libs/v0/core/clean/newType.mjs +++ b/docs/libs/v0/core/clean/newType.mjs @@ -1,13 +1,11 @@ -import { pipe, A, O, DPE, keyWrappedValue } from '@duplojs/utils'; -import { createNewType, newTypeKind, constrainedTypeKind } from '@duplojs/utils/clean'; +import { C, DPE } from '@duplojs/utils'; +import '@duplojs/utils/clean'; -createNewType.overrideHandler.setMethod("toExtractParser", (self) => { - const constraintsKindValue = pipe(self.constraints, A.map(({ name }) => O.entry(name, null)), O.fromEntries); - const valueContainer = newTypeKind.setTo(constrainedTypeKind.setTo({}, constraintsKindValue), self.name); - const dataParser = DPE.transform(self.dataParser, (input) => ({ - ...valueContainer, - [keyWrappedValue]: input, - })); - return dataParser; +C.createNewType.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = C.toMapDataParser(self, params); + return DPE.lazy(() => innerDataParser); +}); +C.createNewType.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.internal.dataParser; + return DPE.lazy(() => innerDataParser); }); -createNewType.overrideHandler.setMethod("toEndpointSchema", (self) => DPE.lazy(() => self.dataParser)); diff --git a/docs/libs/v0/core/clean/primitive.cjs b/docs/libs/v0/core/clean/primitive.cjs index 595d4ea..279d0d2 100644 --- a/docs/libs/v0/core/clean/primitive.cjs +++ b/docs/libs/v0/core/clean/primitive.cjs @@ -1,12 +1,13 @@ 'use strict'; var utils = require('@duplojs/utils'); -var clean = require('@duplojs/utils/clean'); +require('@duplojs/utils/clean'); -clean.createPrimitive.overrideHandler.setMethod("toExtractParser", (self) => { - const dataParser = utils.DPE.transform(self.dataParser, (input) => ({ - [utils.keyWrappedValue]: input, - })); - return dataParser; +utils.C.createPrimitive.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = utils.C.toMapDataParser(self, params); + return utils.DPE.lazy(() => innerDataParser); +}); +utils.C.createPrimitive.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.dataParser; + return utils.DPE.lazy(() => innerDataParser); }); -clean.createPrimitive.overrideHandler.setMethod("toEndpointSchema", (self) => utils.DPE.lazy(() => self.dataParser)); diff --git a/docs/libs/v0/core/clean/primitive.d.ts b/docs/libs/v0/core/clean/primitive.d.ts index 2514b88..2926fe0 100644 --- a/docs/libs/v0/core/clean/primitive.d.ts +++ b/docs/libs/v0/core/clean/primitive.d.ts @@ -1,8 +1,12 @@ -import { DPE } from "@duplojs/utils"; -import { type EligiblePrimitive, type Primitive } from "@duplojs/utils/clean"; +import { DPE, C } from "@duplojs/utils"; +import "@duplojs/utils/clean"; +interface ToExtractParserParams { + coerce?: boolean; +} declare module "@duplojs/utils/clean" { - interface PrimitiveHandler { - toExtractParser(): DPE.ContractExtended, unknown>; - toEndpointSchema(): DPE.ContractExtended; + interface PrimitiveHandler { + toExtractParser(params?: ToExtractParserParams): DPE.DataParserExtended, unknown>; + toEndpointSchema(): DPE.DataParserExtended; } } +export {}; diff --git a/docs/libs/v0/core/clean/primitive.mjs b/docs/libs/v0/core/clean/primitive.mjs index 2e668c7..f8081dc 100644 --- a/docs/libs/v0/core/clean/primitive.mjs +++ b/docs/libs/v0/core/clean/primitive.mjs @@ -1,10 +1,11 @@ -import { DPE, keyWrappedValue } from '@duplojs/utils'; -import { createPrimitive } from '@duplojs/utils/clean'; +import { C, DPE } from '@duplojs/utils'; +import '@duplojs/utils/clean'; -createPrimitive.overrideHandler.setMethod("toExtractParser", (self) => { - const dataParser = DPE.transform(self.dataParser, (input) => ({ - [keyWrappedValue]: input, - })); - return dataParser; +C.createPrimitive.overrideHandler.setMethod("toExtractParser", (self, params) => { + const innerDataParser = C.toMapDataParser(self, params); + return DPE.lazy(() => innerDataParser); +}); +C.createPrimitive.overrideHandler.setMethod("toEndpointSchema", (self) => { + const innerDataParser = self.dataParser; + return DPE.lazy(() => innerDataParser); }); -createPrimitive.overrideHandler.setMethod("toEndpointSchema", (self) => DPE.lazy(() => self.dataParser)); diff --git a/docs/libs/v0/core/response/contract.d.ts b/docs/libs/v0/core/response/contract.d.ts index 22408e7..3baa7bb 100644 --- a/docs/libs/v0/core/response/contract.d.ts +++ b/docs/libs/v0/core/response/contract.d.ts @@ -305,7 +305,7 @@ export declare namespace ResponseContract { } export function stream(information: GenericInformation, schema: GenericSchema): StreamContract<"200", GenericInformation, GenericSchema, typeof defaultSchema>; export const streamTextContractKind: import("@duplojs/utils").KindHandler>; - export interface StreamTextContract = DP.Contract, GenericSchema extends SupportedDataParser = SupportedDataParser> extends Kind { + export interface StreamTextContract = DP.DataParser, GenericSchema extends SupportedDataParser = SupportedDataParser> extends Kind { code: GenericCode; information: GenericInformation; flux: GenericFlux; diff --git a/docs/libs/v0/core/steps/extract.d.ts b/docs/libs/v0/core/steps/extract.d.ts index 6378b26..29a3a26 100644 --- a/docs/libs/v0/core/steps/extract.d.ts +++ b/docs/libs/v0/core/steps/extract.d.ts @@ -6,8 +6,9 @@ import { type Metadata } from "../metadata"; export interface DisabledExtractKeysCustom { } export type DisabledExtractKeys = O.GetPropsWithValue; -export type ExtractShape = Partial | DisabledExtractKeys | "body" | "bodyReader" | symbol>, DP.DataParser | Record> & { +export type ExtractShape = Partial | DisabledExtractKeys | "body" | "bodyReader" | "params" | symbol>, DP.DataParser | Record> & { body: (DP.DataParser | Record); + params: Record; }>; export interface ExtractStepDefinition { readonly shape: ExtractShape; diff --git a/docs/libs/v0/core/types/extractParamsKeyFromPath.cjs b/docs/libs/v0/core/types/extractParamsKeyFromPath.cjs new file mode 100644 index 0000000..eb109ab --- /dev/null +++ b/docs/libs/v0/core/types/extractParamsKeyFromPath.cjs @@ -0,0 +1,2 @@ +'use strict'; + diff --git a/docs/libs/v0/core/types/extractParamsKeyFromPath.d.ts b/docs/libs/v0/core/types/extractParamsKeyFromPath.d.ts new file mode 100644 index 0000000..2aae1b6 --- /dev/null +++ b/docs/libs/v0/core/types/extractParamsKeyFromPath.d.ts @@ -0,0 +1 @@ +export type ExtractParamsKeyFromPath = GenericPath extends `${string}{${infer InferredParams}}${infer InferredRest}` ? InferredParams | ExtractParamsKeyFromPath : never; diff --git a/docs/libs/v0/core/types/extractParamsKeyFromPath.mjs b/docs/libs/v0/core/types/extractParamsKeyFromPath.mjs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/libs/v0/core/types/extractParamsKeyFromPath.mjs @@ -0,0 +1 @@ + diff --git a/docs/libs/v0/core/types/index.cjs b/docs/libs/v0/core/types/index.cjs index 28e7baf..c9e78cc 100644 --- a/docs/libs/v0/core/types/index.cjs +++ b/docs/libs/v0/core/types/index.cjs @@ -4,4 +4,5 @@ require('./environment.cjs'); require('./forbiddenBigintDataParser.cjs'); require('./httpServerParams.cjs'); require('./hosts.cjs'); +require('./extractParamsKeyFromPath.cjs'); diff --git a/docs/libs/v0/core/types/index.d.ts b/docs/libs/v0/core/types/index.d.ts index 3220226..63c64ea 100644 --- a/docs/libs/v0/core/types/index.d.ts +++ b/docs/libs/v0/core/types/index.d.ts @@ -2,3 +2,4 @@ export * from "./environment"; export * from "./forbiddenBigintDataParser"; export * from "./httpServerParams"; export * from "./hosts"; +export * from "./extractParamsKeyFromPath"; diff --git a/docs/libs/v0/core/types/index.mjs b/docs/libs/v0/core/types/index.mjs index e524262..f324ccd 100644 --- a/docs/libs/v0/core/types/index.mjs +++ b/docs/libs/v0/core/types/index.mjs @@ -2,3 +2,4 @@ import './environment.mjs'; import './forbiddenBigintDataParser.mjs'; import './httpServerParams.mjs'; import './hosts.mjs'; +import './extractParamsKeyFromPath.mjs'; diff --git a/docs/libs/v0/interfaces/node/bodyReaders/formData/error.cjs b/docs/libs/v0/interfaces/node/bodyReaders/formData/error.cjs index ac85c20..6b67da1 100644 --- a/docs/libs/v0/interfaces/node/bodyReaders/formData/error.cjs +++ b/docs/libs/v0/interfaces/node/bodyReaders/formData/error.cjs @@ -6,7 +6,7 @@ var kind = require('../../kind.cjs'); class BodyParseFormDataError extends utils.kindHeritage("body-parse-form-data-error", kind.createInterfacesNodeLibKind("body-parse-form-data-error"), Error) { information; constructor(information) { - super({}, [`Body parse form date error: ${information}`]); + super({}, [`Body parse form data error: ${information}`]); this.information = information; } } diff --git a/docs/libs/v0/interfaces/node/bodyReaders/formData/error.mjs b/docs/libs/v0/interfaces/node/bodyReaders/formData/error.mjs index 1079e07..c027319 100644 --- a/docs/libs/v0/interfaces/node/bodyReaders/formData/error.mjs +++ b/docs/libs/v0/interfaces/node/bodyReaders/formData/error.mjs @@ -4,7 +4,7 @@ import { createInterfacesNodeLibKind } from '../../kind.mjs'; class BodyParseFormDataError extends kindHeritage("body-parse-form-data-error", createInterfacesNodeLibKind("body-parse-form-data-error"), Error) { information; constructor(information) { - super({}, [`Body parse form date error: ${information}`]); + super({}, [`Body parse form data error: ${information}`]); this.information = information; } } diff --git a/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.cjs b/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.cjs index d45c2b1..7435fde 100644 --- a/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.cjs +++ b/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.cjs @@ -118,10 +118,10 @@ async function readRequestFormData(request, firstValueAccumulator, params, onRec if (!(chunk instanceof Buffer)) { return await treatError(new bodyParseWrongChunkReceived.BodyParseWrongChunkReceived("Buffer.", chunk)); } - currentBuffer = Buffer.concat([currentBuffer, chunk]); if (currentBuffer.length > params.maxBufferSize) { return await treatError(new error.BodyParseFormDataError("Buffer size exceeds limit.")); } + currentBuffer = Buffer.concat([currentBuffer, chunk]); while (true) { const startPartIndex = currentBuffer.indexOf(startPart); const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart); diff --git a/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.mjs b/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.mjs index 305438f..78e58f8 100644 --- a/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.mjs +++ b/docs/libs/v0/interfaces/node/bodyReaders/formData/readRequestFormData.mjs @@ -116,10 +116,10 @@ async function readRequestFormData(request, firstValueAccumulator, params, onRec if (!(chunk instanceof Buffer)) { return await treatError(new BodyParseWrongChunkReceived("Buffer.", chunk)); } - currentBuffer = Buffer.concat([currentBuffer, chunk]); if (currentBuffer.length > params.maxBufferSize) { return await treatError(new BodyParseFormDataError("Buffer size exceeds limit.")); } + currentBuffer = Buffer.concat([currentBuffer, chunk]); while (true) { const startPartIndex = currentBuffer.indexOf(startPart); const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart); diff --git a/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.cjs b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.cjs new file mode 100644 index 0000000..4a8beac --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.cjs @@ -0,0 +1,36 @@ +'use strict'; + +var utils = require('@duplojs/utils'); + +function createSubDataParserBuildedContext(buildedContext) { + return utils.pipe(buildedContext.context.entries(), utils.G.map(([dataParser, contextValue]) => { + const subImportContext = new Map(buildedContext.importContext); + utils.pipe(contextValue.dependencies, utils.G.map((dataParserDependencies) => { + if (dataParser === dataParserDependencies) { + return null; + } + const subContextValue = buildedContext.context.get(dataParserDependencies); + if (!subContextValue) { + return null; + } + subImportContext.set(`./${subContextValue.identifier.text}`, { + direct: [subContextValue.identifier.text], + }); + return null; + }), utils.G.execute); + if (contextValue.typeIdentifier) { + subImportContext.set("./types", { + direct: [contextValue.typeIdentifier.text], + }); + } + return { + identifier: contextValue.identifier.text, + importContext: subImportContext, + context: new Map([[dataParser, contextValue]]), + importMode: buildedContext.importMode, + typescriptContext: new Map(), + }; + })); +} + +exports.createSubDataParserBuildedContext = createSubDataParserBuildedContext; diff --git a/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.d.ts b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.d.ts new file mode 100644 index 0000000..c842b1a --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.d.ts @@ -0,0 +1,5 @@ +import { type DataParserToDataParser } from "@duplojs/data-parser-tools"; +export interface SubBuildedContext extends DataParserToDataParser.BuildedContext { + identifier: string; +} +export declare function createSubDataParserBuildedContext(buildedContext: DataParserToDataParser.BuildedContext): Generator; diff --git a/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.mjs b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.mjs new file mode 100644 index 0000000..9bee12f --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/createSubDataParserBuildedContext.mjs @@ -0,0 +1,34 @@ +import { pipe, G } from '@duplojs/utils'; + +function createSubDataParserBuildedContext(buildedContext) { + return pipe(buildedContext.context.entries(), G.map(([dataParser, contextValue]) => { + const subImportContext = new Map(buildedContext.importContext); + pipe(contextValue.dependencies, G.map((dataParserDependencies) => { + if (dataParser === dataParserDependencies) { + return null; + } + const subContextValue = buildedContext.context.get(dataParserDependencies); + if (!subContextValue) { + return null; + } + subImportContext.set(`./${subContextValue.identifier.text}`, { + direct: [subContextValue.identifier.text], + }); + return null; + }), G.execute); + if (contextValue.typeIdentifier) { + subImportContext.set("./types", { + direct: [contextValue.typeIdentifier.text], + }); + } + return { + identifier: contextValue.identifier.text, + importContext: subImportContext, + context: new Map([[dataParser, contextValue]]), + importMode: buildedContext.importMode, + typescriptContext: new Map(), + }; + })); +} + +export { createSubDataParserBuildedContext }; diff --git a/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.cjs b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.cjs new file mode 100644 index 0000000..7c32fa0 --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.cjs @@ -0,0 +1,28 @@ +'use strict'; + +require('../../core/steps/index.cjs'); +var dataParserTools = require('@duplojs/data-parser-tools'); +var utils = require('@duplojs/utils'); +var extract = require('../../core/steps/extract.cjs'); +var process = require('../../core/steps/process.cjs'); +var presetChecker = require('../../core/steps/presetChecker.cjs'); +var checker = require('../../core/steps/checker.cjs'); +var cut = require('../../core/steps/cut.cjs'); +var handler = require('../../core/steps/handler.cjs'); + +function dataParserHasIdentifier(dataParser) { + return !!dataParser.definition.identifier; +} +function findIdentifiedDataParserInSteps(steps, params) { + return utils.pipe(steps, utils.A.flatMap(utils.innerPipe(utils.P.when(extract.extractStepKind.has, (extractStep) => utils.pipe(extractStep.definition.shape, utils.O.values, utils.A.flatMap(utils.innerPipe(utils.whenNot(utils.DP.dataParserKind.has, utils.O.values))))), utils.P.when(process.processStepKind.has, utils.forward), utils.P.when(presetChecker.presetCheckerStepKind.has, (step) => [step.definition.presetChecker.definition.responseContract.body]), utils.P.when(utils.hasSomeKinds([ + checker.checkerStepKind, + cut.cutStepKind, + handler.handlerStepKind, + ]), (step) => utils.pipe(step.definition.responseContract, utils.A.coalescing, utils.A.map(({ body }) => body))), utils.P.exhaustive)), utils.A.flatMap(utils.innerPipe(utils.P.when(process.processStepKind.has, (processStep) => findIdentifiedDataParserInSteps(processStep.definition.process.definition.steps, params)), utils.P.when(utils.DP.dataParserKind.has, (dataParser) => dataParserTools.DataParserFinder.dataParserFinder(dataParser, dataParserHasIdentifier, { + researchers: dataParserTools.DataParserFinder.defaultResearchers, + ignore: params.ignoreDataParser, + })), utils.P.exhaustive))); +} + +exports.dataParserHasIdentifier = dataParserHasIdentifier; +exports.findIdentifiedDataParserInSteps = findIdentifiedDataParserInSteps; diff --git a/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.d.ts b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.d.ts new file mode 100644 index 0000000..ff71569 --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.d.ts @@ -0,0 +1,12 @@ +import { type Steps } from "../../core/steps"; +import { DP } from "@duplojs/utils"; +export type IdentifiedDataParser = (DP.DataParser & { + definition: { + identifier: string; + }; +}); +export declare function dataParserHasIdentifier(dataParser: DP.DataParser): dataParser is IdentifiedDataParser; +export interface findIdentifiedDataParserInStepsParams { + readonly ignoreDataParser: Set; +} +export declare function findIdentifiedDataParserInSteps(steps: readonly Steps[], params: findIdentifiedDataParserInStepsParams): IdentifiedDataParser[]; diff --git a/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.mjs b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.mjs new file mode 100644 index 0000000..b15ec5b --- /dev/null +++ b/docs/libs/v0/plugins/codeGenerator/findIdentifiedDataParserInSteps.mjs @@ -0,0 +1,25 @@ +import '../../core/steps/index.mjs'; +import { DataParserFinder } from '@duplojs/data-parser-tools'; +import { pipe, A, innerPipe, P, O, whenNot, DP, forward, hasSomeKinds } from '@duplojs/utils'; +import { extractStepKind } from '../../core/steps/extract.mjs'; +import { processStepKind } from '../../core/steps/process.mjs'; +import { presetCheckerStepKind } from '../../core/steps/presetChecker.mjs'; +import { checkerStepKind } from '../../core/steps/checker.mjs'; +import { cutStepKind } from '../../core/steps/cut.mjs'; +import { handlerStepKind } from '../../core/steps/handler.mjs'; + +function dataParserHasIdentifier(dataParser) { + return !!dataParser.definition.identifier; +} +function findIdentifiedDataParserInSteps(steps, params) { + return pipe(steps, A.flatMap(innerPipe(P.when(extractStepKind.has, (extractStep) => pipe(extractStep.definition.shape, O.values, A.flatMap(innerPipe(whenNot(DP.dataParserKind.has, O.values))))), P.when(processStepKind.has, forward), P.when(presetCheckerStepKind.has, (step) => [step.definition.presetChecker.definition.responseContract.body]), P.when(hasSomeKinds([ + checkerStepKind, + cutStepKind, + handlerStepKind, + ]), (step) => pipe(step.definition.responseContract, A.coalescing, A.map(({ body }) => body))), P.exhaustive)), A.flatMap(innerPipe(P.when(processStepKind.has, (processStep) => findIdentifiedDataParserInSteps(processStep.definition.process.definition.steps, params)), P.when(DP.dataParserKind.has, (dataParser) => DataParserFinder.dataParserFinder(dataParser, dataParserHasIdentifier, { + researchers: DataParserFinder.defaultResearchers, + ignore: params.ignoreDataParser, + })), P.exhaustive))); +} + +export { dataParserHasIdentifier, findIdentifiedDataParserInSteps }; diff --git a/docs/libs/v0/plugins/codeGenerator/plugin.cjs b/docs/libs/v0/plugins/codeGenerator/plugin.cjs index 05e2afb..26baa9c 100644 --- a/docs/libs/v0/plugins/codeGenerator/plugin.cjs +++ b/docs/libs/v0/plugins/codeGenerator/plugin.cjs @@ -5,6 +5,9 @@ var utils = require('@duplojs/utils'); var routeToDataParser = require('./routeToDataParser.cjs'); var serverUtils = require('@duplojs/server-utils'); var typescriptTransformer = require('./typescriptTransformer.cjs'); +var findIdentifiedDataParserInSteps = require('./findIdentifiedDataParserInSteps.cjs'); +var dataParserTools = require('@duplojs/data-parser-tools'); +var createSubDataParserBuildedContext = require('./createSubDataParserBuildedContext.cjs'); function _interopNamespaceDefault(e) { var n = Object.create(null); @@ -34,24 +37,60 @@ function codeGeneratorPlugin(pluginParams) { if (!utils.equal(hub.config.environment, ["DEV", "BUILD"])) { return; } - const routes = utils.A.from(hub.routes); - const dataParserRoutes = utils.A.flatMap(routes, (route) => routeToDataParser.routeToDataParser(route, { + const dataParserRoutes = utils.pipe(hub.routes, utils.G.map((route) => routeToDataParser.routeToDataParser(route, { defaultExtractContract: hub.defaultExtractContract, - })); + })), utils.G.flat, utils.A.from); if (!utils.A.minElements(dataParserRoutes, 1)) { return; } const output = DataParserToTypescript__namespace.render(utils.DP.union(dataParserRoutes), { identifier: "Routes", mode: "in", - transformers: [ - typescriptTransformer.fileTransformer, - typescriptTransformer.dateTransformer, - typescriptTransformer.timeTransformer, - ...DataParserToTypescript__namespace.defaultTransformers, - ], + transformers: typescriptTransformer.typescriptTransformers, }); utils.asserts(await serverUtils.SF.writeTextFile(pluginParams.outputFile, output), utils.E.isRight); + if (pluginParams.generateDataParser) { + const generateDataParserParams = pluginParams.generateDataParser; + const buildedContext = { + context: new Map(), + importContext: new Map(), + typescriptContext: new Map(), + importMode: "lite", + }; + const ignoreDataParser = new Set(); + utils.pipe([], utils.A.concat(utils.pipe(generateDataParserParams.dataParsers ?? [], utils.A.flatMap((dataParser) => dataParserTools.DataParserFinder.dataParserFinder(dataParser, findIdentifiedDataParserInSteps.dataParserHasIdentifier, { + researchers: dataParserTools.DataParserFinder.defaultResearchers, + ignore: ignoreDataParser, + })))), utils.A.concat(utils.pipe(generateDataParserParams.disabledFromRoute + ? new Set() + : hub.routes, utils.G.map((route) => findIdentifiedDataParserInSteps.findIdentifiedDataParserInSteps(route.definition.steps, { ignoreDataParser })), utils.G.flat, utils.A.from)), utils.A.map((dataParser) => dataParserTools.DataParserToDataParser.buildContext(dataParser, { + identifier: dataParser.definition.identifier, + checkerTransformers: dataParserTools.DataParserToDataParser.defaultCheckerTransformers, + dataParserTransformers: dataParserTools.DataParserToDataParser.defaultTransformers, + typescriptTransformers: DataParserToTypescript__namespace.defaultTransformers, + ...buildedContext, + }))); + await utils.asyncPipe(serverUtils.SF.exists(generateDataParserParams.outputFolder), utils.E.whenIsRight(async () => void utils.asserts(await serverUtils.SF.remove(generateDataParserParams.outputFolder, { recursive: true }), utils.E.isRight)), async () => void utils.asserts(await serverUtils.SF.makeDirectory(generateDataParserParams.outputFolder), utils.E.isRight)); + utils.asserts(await serverUtils.SF.writeTextFile(utils.Path.resolveRelative([ + generateDataParserParams.outputFolder, + "types.ts", + ]), DataParserToTypescript__namespace.printer({ + context: buildedContext.typescriptContext, + importContext: buildedContext.importContext, + })), utils.E.isRight); + for (const context of createSubDataParserBuildedContext.createSubDataParserBuildedContext(buildedContext)) { + utils.asserts(await serverUtils.SF.writeTextFile(utils.Path.resolveRelative([ + generateDataParserParams.outputFolder, + `${context.identifier}.ts`, + ]), dataParserTools.DataParserToDataParser.printer(context)), utils.E.isRight); + } + await utils.pipe(buildedContext.context.values(), utils.G.map((contextValue) => `export * from "./${contextValue.identifier.text}";`), utils.A.from, utils.A.join("\n"), async (values) => { + utils.asserts(await serverUtils.SF.writeTextFile(utils.Path.resolveRelative([ + generateDataParserParams.outputFolder, + "index.ts", + ]), values), utils.E.isRight); + }); + } }, }, ], diff --git a/docs/libs/v0/plugins/codeGenerator/plugin.d.ts b/docs/libs/v0/plugins/codeGenerator/plugin.d.ts index 617e2ee..360ab96 100644 --- a/docs/libs/v0/plugins/codeGenerator/plugin.d.ts +++ b/docs/libs/v0/plugins/codeGenerator/plugin.d.ts @@ -1,5 +1,12 @@ import { type HubPlugin } from "../../core/hub"; +import { DP } from "@duplojs/utils"; +export interface GenerateDataParserParams { + outputFolder: string; + disabledFromRoute?: boolean; + dataParsers?: DP.DataParser[]; +} export interface CodeGeneratorPluginParams { outputFile: string; + generateDataParser?: GenerateDataParserParams; } export declare function codeGeneratorPlugin(pluginParams: CodeGeneratorPluginParams): () => HubPlugin; diff --git a/docs/libs/v0/plugins/codeGenerator/plugin.mjs b/docs/libs/v0/plugins/codeGenerator/plugin.mjs index b557737..09e49c8 100644 --- a/docs/libs/v0/plugins/codeGenerator/plugin.mjs +++ b/docs/libs/v0/plugins/codeGenerator/plugin.mjs @@ -1,8 +1,11 @@ import * as DataParserToTypescript from '@duplojs/data-parser-tools/toTypescript'; -import { equal, A, DP, asserts, E } from '@duplojs/utils'; +import { equal, pipe, G, A, DP, asserts, E, asyncPipe, Path } from '@duplojs/utils'; import { routeToDataParser } from './routeToDataParser.mjs'; import { SF } from '@duplojs/server-utils'; -import { fileTransformer, dateTransformer, timeTransformer } from './typescriptTransformer.mjs'; +import { typescriptTransformers } from './typescriptTransformer.mjs'; +import { dataParserHasIdentifier, findIdentifiedDataParserInSteps } from './findIdentifiedDataParserInSteps.mjs'; +import { DataParserFinder, DataParserToDataParser } from '@duplojs/data-parser-tools'; +import { createSubDataParserBuildedContext } from './createSubDataParserBuildedContext.mjs'; function codeGeneratorPlugin(pluginParams) { return () => ({ @@ -13,24 +16,60 @@ function codeGeneratorPlugin(pluginParams) { if (!equal(hub.config.environment, ["DEV", "BUILD"])) { return; } - const routes = A.from(hub.routes); - const dataParserRoutes = A.flatMap(routes, (route) => routeToDataParser(route, { + const dataParserRoutes = pipe(hub.routes, G.map((route) => routeToDataParser(route, { defaultExtractContract: hub.defaultExtractContract, - })); + })), G.flat, A.from); if (!A.minElements(dataParserRoutes, 1)) { return; } const output = DataParserToTypescript.render(DP.union(dataParserRoutes), { identifier: "Routes", mode: "in", - transformers: [ - fileTransformer, - dateTransformer, - timeTransformer, - ...DataParserToTypescript.defaultTransformers, - ], + transformers: typescriptTransformers, }); asserts(await SF.writeTextFile(pluginParams.outputFile, output), E.isRight); + if (pluginParams.generateDataParser) { + const generateDataParserParams = pluginParams.generateDataParser; + const buildedContext = { + context: new Map(), + importContext: new Map(), + typescriptContext: new Map(), + importMode: "lite", + }; + const ignoreDataParser = new Set(); + pipe([], A.concat(pipe(generateDataParserParams.dataParsers ?? [], A.flatMap((dataParser) => DataParserFinder.dataParserFinder(dataParser, dataParserHasIdentifier, { + researchers: DataParserFinder.defaultResearchers, + ignore: ignoreDataParser, + })))), A.concat(pipe(generateDataParserParams.disabledFromRoute + ? new Set() + : hub.routes, G.map((route) => findIdentifiedDataParserInSteps(route.definition.steps, { ignoreDataParser })), G.flat, A.from)), A.map((dataParser) => DataParserToDataParser.buildContext(dataParser, { + identifier: dataParser.definition.identifier, + checkerTransformers: DataParserToDataParser.defaultCheckerTransformers, + dataParserTransformers: DataParserToDataParser.defaultTransformers, + typescriptTransformers: DataParserToTypescript.defaultTransformers, + ...buildedContext, + }))); + await asyncPipe(SF.exists(generateDataParserParams.outputFolder), E.whenIsRight(async () => void asserts(await SF.remove(generateDataParserParams.outputFolder, { recursive: true }), E.isRight)), async () => void asserts(await SF.makeDirectory(generateDataParserParams.outputFolder), E.isRight)); + asserts(await SF.writeTextFile(Path.resolveRelative([ + generateDataParserParams.outputFolder, + "types.ts", + ]), DataParserToTypescript.printer({ + context: buildedContext.typescriptContext, + importContext: buildedContext.importContext, + })), E.isRight); + for (const context of createSubDataParserBuildedContext(buildedContext)) { + asserts(await SF.writeTextFile(Path.resolveRelative([ + generateDataParserParams.outputFolder, + `${context.identifier}.ts`, + ]), DataParserToDataParser.printer(context)), E.isRight); + } + await pipe(buildedContext.context.values(), G.map((contextValue) => `export * from "./${contextValue.identifier.text}";`), A.from, A.join("\n"), async (values) => { + asserts(await SF.writeTextFile(Path.resolveRelative([ + generateDataParserParams.outputFolder, + "index.ts", + ]), values), E.isRight); + }); + } }, }, ], diff --git a/docs/libs/v0/plugins/codeGenerator/routeToDataParser.d.ts b/docs/libs/v0/plugins/codeGenerator/routeToDataParser.d.ts index 0ab8f46..b29fd2e 100644 --- a/docs/libs/v0/plugins/codeGenerator/routeToDataParser.d.ts +++ b/docs/libs/v0/plugins/codeGenerator/routeToDataParser.d.ts @@ -11,30 +11,40 @@ export declare const convertRoutePath: (path: string) => DP.DataParserLiteral<{ readonly errorMessage?: string | undefined; readonly identifier?: string | undefined; readonly overrideTypescriptTransformer?: TransformerBuildFunction | undefined; + readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly checkers: readonly []; }> | DP.DataParserTemplateLiteral<{ readonly template: [string | DP.DataParserString<{ readonly errorMessage?: string | undefined; readonly identifier?: string | undefined; readonly overrideTypescriptTransformer?: TransformerBuildFunction | undefined; + readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly coerce: boolean; readonly checkers: readonly []; }>, string | DP.DataParserString<{ readonly errorMessage?: string | undefined; readonly identifier?: string | undefined; readonly overrideTypescriptTransformer?: TransformerBuildFunction | undefined; + readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly coerce: boolean; readonly checkers: readonly []; }>, ...(string | DP.DataParserString<{ readonly errorMessage?: string | undefined; readonly identifier?: string | undefined; readonly overrideTypescriptTransformer?: TransformerBuildFunction | undefined; + readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly coerce: boolean; readonly checkers: readonly []; }>)[]]; readonly errorMessage?: string | undefined; readonly identifier?: string | undefined; readonly overrideTypescriptTransformer?: TransformerBuildFunction | undefined; + readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly pattern: RegExp; readonly checkers: readonly []; }>; diff --git a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.cjs b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.cjs index 50c2ba2..83f3462 100644 --- a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.cjs +++ b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.cjs @@ -1,5 +1,6 @@ 'use strict'; +var dataParserTools = require('@duplojs/data-parser-tools'); var DataParserToTypescript = require('@duplojs/data-parser-tools/toTypescript'); var serverUtils = require('@duplojs/server-utils'); var utils = require('@duplojs/utils'); @@ -22,7 +23,14 @@ const timeTransformer = DataParserToTypescript.createTransformer(utils.DP.timeKi typescript.factory.createTypeReferenceNode(typescript.factory.createIdentifier("TheTime")), ])); }); +const typescriptTransformers = [ + fileTransformer, + dateTransformer, + timeTransformer, + ...dataParserTools.DataParserToTypescript.defaultTransformers, +]; exports.dateTransformer = dateTransformer; exports.fileTransformer = fileTransformer; exports.timeTransformer = timeTransformer; +exports.typescriptTransformers = typescriptTransformers; diff --git a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.d.ts b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.d.ts index 092edf9..a1bfe06 100644 --- a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.d.ts +++ b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.d.ts @@ -1,4 +1,6 @@ +import { DataParserToTypescript } from "@duplojs/data-parser-tools"; import { DP } from "@duplojs/utils"; -export declare const fileTransformer: (schema: DP.DataParsers, params: import("@duplojs/data-parser-tools/toTypescript").TransformerParams) => import("@duplojs/data-parser-tools/toTypescript").MaybeTransformerEither; -export declare const dateTransformer: (schema: DP.DataParsers, params: import("@duplojs/data-parser-tools/toTypescript").TransformerParams) => import("@duplojs/data-parser-tools/toTypescript").MaybeTransformerEither; -export declare const timeTransformer: (schema: DP.DataParsers, params: import("@duplojs/data-parser-tools/toTypescript").TransformerParams) => import("@duplojs/data-parser-tools/toTypescript").MaybeTransformerEither; +export declare const fileTransformer: (schema: DP.DataParsers, params: DataParserToTypescript.TransformerParams) => DataParserToTypescript.MaybeTransformerEither; +export declare const dateTransformer: (schema: DP.DataParsers, params: DataParserToTypescript.TransformerParams) => DataParserToTypescript.MaybeTransformerEither; +export declare const timeTransformer: (schema: DP.DataParsers, params: DataParserToTypescript.TransformerParams) => DataParserToTypescript.MaybeTransformerEither; +export declare const typescriptTransformers: ((schema: DP.DataParsers, params: DataParserToTypescript.TransformerParams) => DataParserToTypescript.MaybeTransformerEither)[]; diff --git a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.mjs b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.mjs index 1af96de..cbe1986 100644 --- a/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.mjs +++ b/docs/libs/v0/plugins/codeGenerator/typescriptTransformer.mjs @@ -1,3 +1,4 @@ +import { DataParserToTypescript } from '@duplojs/data-parser-tools'; import { createTransformer } from '@duplojs/data-parser-tools/toTypescript'; import { SDP } from '@duplojs/server-utils'; import { DP } from '@duplojs/utils'; @@ -20,5 +21,11 @@ const timeTransformer = createTransformer(DP.timeKind.has, (__, { success, addIm factory.createTypeReferenceNode(factory.createIdentifier("TheTime")), ])); }); +const typescriptTransformers = [ + fileTransformer, + dateTransformer, + timeTransformer, + ...DataParserToTypescript.defaultTransformers, +]; -export { dateTransformer, fileTransformer, timeTransformer }; +export { dateTransformer, fileTransformer, timeTransformer, typescriptTransformers }; diff --git a/docs/libs/v0/plugins/cookie/serialize.d.ts b/docs/libs/v0/plugins/cookie/serialize.d.ts index 4db42f2..eb22008 100644 --- a/docs/libs/v0/plugins/cookie/serialize.d.ts +++ b/docs/libs/v0/plugins/cookie/serialize.d.ts @@ -1,7 +1,7 @@ import { D } from "@duplojs/utils"; declare const SerializeCookieError_base: new (params: { "@DuplojsCookiePlugin/serialize-cookie-error"?: unknown; -}, parentParams: readonly [message?: string | undefined, options?: ErrorOptions | undefined]) => import("@duplojs/utils").Kind, unknown> & import("@duplojs/utils").Kind, unknown> & Error; +}, parentParams: readonly [message?: string | undefined, options?: ErrorOptions | undefined]) => Error & import("@duplojs/utils").Kind, unknown> & import("@duplojs/utils").Kind, unknown>; export declare class SerializeCookieError extends SerializeCookieError_base { constructor(message: string); } diff --git a/docs/libs/v0/plugins/openApiGenerator/makeOpenApiRoute.d.ts b/docs/libs/v0/plugins/openApiGenerator/makeOpenApiRoute.d.ts index 5c036b8..feb9fb0 100644 --- a/docs/libs/v0/plugins/openApiGenerator/makeOpenApiRoute.d.ts +++ b/docs/libs/v0/plugins/openApiGenerator/makeOpenApiRoute.d.ts @@ -14,6 +14,7 @@ export declare function makeOpenApiRoute(routePath: RoutePath, openApiPage: stri readonly identifier?: string | undefined; readonly overrideJsonSchemaTransformer?: import("@duplojs/data-parser-tools/toJsonSchema").TransformerBuildFunction | undefined; readonly overrideTypescriptTransformer?: import("@duplojs/data-parser-tools/toTypescript").TransformerBuildFunction | undefined; + readonly overrideDataParserTransformer?: import("@duplojs/data-parser-tools/toDataParser").TransformerBuildFunction | undefined; readonly coerce: boolean; readonly checkers: readonly []; }>>>; diff --git a/docs/libs/v0/plugins/static/makeRouteFile.d.ts b/docs/libs/v0/plugins/static/makeRouteFile.d.ts index 233db60..9b5020f 100644 --- a/docs/libs/v0/plugins/static/makeRouteFile.d.ts +++ b/docs/libs/v0/plugins/static/makeRouteFile.d.ts @@ -33,12 +33,8 @@ export declare function makeRouteFile(params: MakeRouteFileParams): import("../. readonly bodyController: null; readonly steps: readonly [import("../../core/steps").HandlerStep<{ readonly responseContract: [NoInfer>>, NoInfer>>]; theFunction(floor: {}, params: import("../../core/steps").HandlerStepFunctionParams | import("../../core/response").PredictedResponse<"304", "resource.notModified", undefined>>): import("@duplojs/utils").MaybePromise | import("../../core/response").PredictedResponse<"304", "resource.notModified", undefined>>; diff --git a/docs/libs/v0/plugins/static/makeRouteFolder.d.ts b/docs/libs/v0/plugins/static/makeRouteFolder.d.ts index f144902..fe01745 100644 --- a/docs/libs/v0/plugins/static/makeRouteFolder.d.ts +++ b/docs/libs/v0/plugins/static/makeRouteFolder.d.ts @@ -20,12 +20,8 @@ export declare function makeRouteFolder(params: MakeRouteFolderParams): import(" readonly bodyController: null; readonly steps: readonly [import("../../core/steps").HandlerStep<{ readonly responseContract: [NoInfer>>, NoInfer Date: Sun, 7 Jun 2026 14:27:37 +0000 Subject: [PATCH 10/10] feat(39): update documentation --- docs/en/v0/guide/plugins/codeGenerator.md | 9 ++++++--- docs/examples/v0/guide/plugins/codeGenerator/hub.ts | 5 ++++- docs/fr/v0/guide/plugins/codeGenerator.md | 9 ++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/en/v0/guide/plugins/codeGenerator.md b/docs/en/v0/guide/plugins/codeGenerator.md index 2f3c214..a901e3f 100644 --- a/docs/en/v0/guide/plugins/codeGenerator.md +++ b/docs/en/v0/guide/plugins/codeGenerator.md @@ -14,10 +14,13 @@ next: ``` -To generate typing for all your routes, use the `codeGeneratorPlugin` function from `@duplojs/http/codeGenerator` in the `Hub` and start with the environment variable set to `DEV` or `BUILD`. +To generate typings for all your routes, use the `codeGeneratorPlugin` function from `@duplojs/http/codeGenerator` in the `Hub`, then start with the environment variable set to `DEV` or `BUILD`. -The `outputFile` parameter lets you define which file the code will be written to. +- The `outputFile` parameter defines the file where route typings will be written. +- The `generateDataParser.outputFolder` parameter defines the folder where identified data parsers will be generated. +- The `generateDataParser.disabledFromRoute` parameter disables data parser generation from routes. +- The `generateDataParser.dataParsers` parameter lets you provide a list of data parsers to generate. In that case, only identified data parsers will be generated. ::: warning -You must have an HTTP server implementation, because the plugin hooks into `beforeStartServer`, which only runs via interface functions like `createHttpServer`. Run with the `Hub` configured in `BUILD` mode so the HTTP server doesn’t start. +You must have an HTTP server implementation, because the plugin hooks into `beforeStartServer`, which only runs through interface functions such as `createHttpServer`. Run with the `Hub` configured in `BUILD` mode to avoid starting the HTTP server. ::: diff --git a/docs/examples/v0/guide/plugins/codeGenerator/hub.ts b/docs/examples/v0/guide/plugins/codeGenerator/hub.ts index 63715c7..563a6e5 100644 --- a/docs/examples/v0/guide/plugins/codeGenerator/hub.ts +++ b/docs/examples/v0/guide/plugins/codeGenerator/hub.ts @@ -4,7 +4,10 @@ import { codeGeneratorPlugin } from "@duplojs/http/codeGenerator"; const hub = createHub({ environment: "DEV" }) .register(routeStore.getAll()) - .plug(codeGeneratorPlugin({ outputFile: "types.d.ts" })); + .plug(codeGeneratorPlugin({ + outputFile: "types.d.ts", + generateDataParser: { outputFolder: "dataParsers" }, + })); await createHttpServer( hub, diff --git a/docs/fr/v0/guide/plugins/codeGenerator.md b/docs/fr/v0/guide/plugins/codeGenerator.md index 2a7ddd7..e81b02d 100644 --- a/docs/fr/v0/guide/plugins/codeGenerator.md +++ b/docs/fr/v0/guide/plugins/codeGenerator.md @@ -14,10 +14,13 @@ next: ``` -Pour générer le typage de toutes vos routes, il suffit d'utiliser la fonction `codeGeneratorPlugin` depuis `@duplojs/http/codeGenerator` dans le `Hub` et de démarrer avec la variable d'environnement sur `DEV` ou sur `BUILD`. +Pour générer le typage de toutes vos routes, utilisez la fonction `codeGeneratorPlugin` depuis `@duplojs/http/codeGenerator` dans le `Hub`, puis démarrez avec la variable d'environnement définie sur `DEV` ou `BUILD`. -Le paramètre `outputFile` vous permet de définir dans quel fichier sera écrit le code. +- Le paramètre `outputFile` permet de définir le fichier dans lequel le typage des routes sera écrit. +- Le paramètre `generateDataParser.outputFolder` définit le dossier dans lequel les data parsers identifiés seront générés. +- Le paramètre `generateDataParser.disabledFromRoute` désactive la génération de data parsers à partir des routes. +- Le paramètre `generateDataParser.dataParsers` permet de fournir une liste de data parsers à générer. Dans ce cas, seuls les data parsers identifiés seront générés. ::: warning -Vous êtes obligé d'avoir une implémentation de serveur HTTP, car le plugin se lie au hook `beforeStartServer` qui se lance uniquement via des fonctions d'interfaçage comme `createHttpServer`. Lancez avec le `Hub` configuré en mode `BUILD` pour que le serveur HTTP ne se lance pas. +Vous devez disposer d'une implémentation de serveur HTTP, car le plugin s'attache au hook `beforeStartServer`, qui n'est exécuté que via des fonctions d'interfaçage comme `createHttpServer`. Lancez avec un `Hub` configuré en mode `BUILD` pour éviter de démarrer le serveur HTTP. :::