From 9f65a0d3f4a76644ac17692dc1ceb963cba8f4ed Mon Sep 17 00:00:00 2001 From: Alexander Ryzhikov Date: Sat, 23 Dec 2023 08:20:43 +0200 Subject: [PATCH] fix/chore cleanup (#117) --- .prettierignore | 2 + docs/Earthfile | 10 ----- docs/topics/CLI-Reference.md | 1 - docs/topics/First-schema.md | 46 ++++++++------------ docs/topics/Installation.md | 1 - docs/topics/Overview.md | 1 - src/type-utils.ts | 82 ++++++++++++++++++------------------ src/typed-fastify.ts | 44 +++++++++---------- test/tsconfig.test.json | 2 +- 9 files changed, 85 insertions(+), 104 deletions(-) delete mode 100644 docs/Earthfile diff --git a/.prettierignore b/.prettierignore index 62a3042..392b29f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ tap-snapshots/**/*.js +test/**/*.gen.json +CHANGELOG.md diff --git a/docs/Earthfile b/docs/Earthfile deleted file mode 100644 index d81933d..0000000 --- a/docs/Earthfile +++ /dev/null @@ -1,10 +0,0 @@ -VERSION 0.6 -FROM registry.jetbrains.team/p/writerside/builder/writerside-builder:232.10165.1 - -docs_build: - COPY docs docs - ENV DISPLAY :99 - RUN Xvfb :99 & /opt/builder/bin/idea.sh helpbuilderinspect -source-dir=docs -product docs/typed-fastify -output-dir artifacts - -docs_test: - diff --git a/docs/topics/CLI-Reference.md b/docs/topics/CLI-Reference.md index b11321c..dad60ab 100644 --- a/docs/topics/CLI-Reference.md +++ b/docs/topics/CLI-Reference.md @@ -49,4 +49,3 @@ Glob pattern with watch ```shell %cli% gen 'src/**/*_schema.ts' -w ``` - diff --git a/docs/topics/First-schema.md b/docs/topics/First-schema.md index af62703..d2daf6b 100644 --- a/docs/topics/First-schema.md +++ b/docs/topics/First-schema.md @@ -16,7 +16,6 @@ Use [CodeSanbox](https://codesandbox.io/p/github/Coobaha/typed-fastify-example?f Try different requests `GET /`, `GET /?name=error` and `GET /?name=1&name=2`. Also try changing the schema and use different types and implement new routes 🤓 - ## Define schema Here is an example of an imaginary API schema: @@ -24,7 +23,7 @@ Here is an example of an imaginary API schema: ```typescript // example_schema.ts -import type { Schema } from "@coobaha/typed-fastify"; +import type { Schema } from '@coobaha/typed-fastify'; interface User { name: string; @@ -38,7 +37,7 @@ interface ResponseError { export interface ExampleSchema extends Schema { paths: { - "GET /": { + 'GET /': { request: { querystring: { name?: string; @@ -64,23 +63,23 @@ Create a service implementation that matches the schema: ```typescript // example_service.ts -import { Service } from "@coobaha/typed-fastify"; +import { Service } from '@coobaha/typed-fastify'; -import type { ExampleSchema } from "./example_schema"; +import type { ExampleSchema } from './example_schema'; const exampleService: Service = { - "GET /": (req, reply) => { - const name = req.query.name ?? "John"; - if (name === "error") { + 'GET /': (req, reply) => { + const name = req.query.name ?? 'John'; + if (name === 'error') { return reply.status(404).send({ code: 404, - message: "Not Found", + message: 'Not Found', }); } return reply.status(200).send({ id: 1, name: name, - }); + }); }, }; ``` @@ -109,10 +108,7 @@ npx tfs gen example_schema.ts "properties": { "User": { "type": "object", - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "additionalProperties": false, "properties": { "name": { @@ -125,10 +121,7 @@ npx tfs gen example_schema.ts }, "ResponseError": { "type": "object", - "required": [ - "code", - "message" - ], + "required": ["code", "message"], "additionalProperties": false, "properties": { "message": { @@ -146,9 +139,7 @@ npx tfs gen example_schema.ts "GET /": { "request": { "type": "object", - "required": [ - "querystring" - ], + "required": ["querystring"], "additionalProperties": false, "properties": { "querystring": { @@ -177,15 +168,14 @@ npx tfs gen example_schema.ts {collapsible="true" collapsed-title="Generated JSON Schema"} - ## Add service to Fastify instance We now have a JSON schema file `example_schema.gen.json` that we can use to validate requests and responses. To wire up the service to Fastify, we need to add the schema and service to Fastify instance -> You can add this schema to version control and commit it to your repository. +> You can add this schema to version control and commit it to your repository. > This way you can also verify diffs and always have the latest version of the schema in your repository. -> +> > You can also configure file nesting in your Editor to hide generated files from the project view. In the same file where you defined `exampleService`, add the following code: @@ -193,7 +183,7 @@ In the same file where you defined `exampleService`, add the following code: ```typescript // example_service.ts -import jsonSchema from './example_schema.gen.json' +import jsonSchema from './example_schema.gen.json'; // ... other imports and exampleService implementation @@ -210,16 +200,16 @@ addSchema(app, { You can now run the server and try out the API. It will have a: + 1. Type safe implementation of the service, that matches the schema 2. Runtime validation of request and response 3. Generated JSON schema that allows you to use it for documentation and verification. - + You can find the full example code here: -- Interactive playground [CodeSanbox](https://codesandbox.io/p/github/Coobaha/typed-fastify-example?file=%2Fsrc%2Fexample_schema.ts) +- Interactive playground [CodeSanbox](https://codesandbox.io/p/github/Coobaha/typed-fastify-example?file=%2Fsrc%2Fexample_schema.ts) - [typed-fastify-example](https://github.com/coobaha/typed-fastify-example) on GitHub - CodeSanbox playground diff --git a/docs/topics/Installation.md b/docs/topics/Installation.md index 3a28105..aa4581a 100644 --- a/docs/topics/Installation.md +++ b/docs/topics/Installation.md @@ -49,7 +49,6 @@ You can also add `--watch` flag to watch for changes in your schema files > **To learn more about the cli** > > For more information about `tfs` command, see [CLI](CLI-Reference.md) page. -> diff --git a/docs/topics/Overview.md b/docs/topics/Overview.md index 4fa3281..9b67ba6 100644 --- a/docs/topics/Overview.md +++ b/docs/topics/Overview.md @@ -17,7 +17,6 @@ Type safety end-to-end : Package generates runtime validations based on your schema and enforces returning correct data at the type level. - ## What are the benefits? - `%pkg%` adds strong TypeScript support to Fastify request handlers diff --git a/src/type-utils.ts b/src/type-utils.ts index 2a1070d..cc87de4 100644 --- a/src/type-utils.ts +++ b/src/type-utils.ts @@ -26,51 +26,53 @@ type JsonCastBehavior = 'cast' | 'combine'; type JsonlikeList = T extends [] ? [] : T extends [infer F, ...infer R] - ? [NeverToNull>, ...JsonlikeList] - : IsUnknown extends true - ? [] - : Array>; + ? [NeverToNull>, ...JsonlikeList] + : IsUnknown extends true + ? [] + : Array>; // tweaked version of Jsonify from type-fest export type Jsonlike = T extends PositiveInfinity | NegativeInfinity ? null : T extends NotJsonable - ? IsNotJsonableError<'Passed value'> - : T extends JsonPrimitive - ? T - : // Any object with toJSON is special case - T extends { - toJSON(): infer J; - } - ? (() => J) extends () => JsonValue // Is J assignable to JsonValue? - ? CastBehavior extends 'combine' - ? T | J - : J // Then T is Jsonable and its Jsonable value is J - : Jsonlike // Maybe if we look a level deeper we'll find a JsonValue - : // Instanced primitives are objects - T extends Number - ? number - : T extends String - ? string - : T extends Boolean - ? boolean - : T extends Map | Set - ? EmptyObject - : T extends TypedArray - ? Record // Non-JSONable type union was found not empty - : T extends [] - ? [] - : T extends unknown[] - ? JsonlikeList - : T extends readonly unknown[] - ? JsonlikeList, CastBehavior> - : T extends object - ? { - [K in keyof T]: [T[K]] extends [NotJsonable] | [never] ? IsNotJsonableError : Jsonlike; // JsonifyObject recursive call for its children - } - : T extends undefined - ? T - : IsNotJsonableError<'Passed value'>; + ? IsNotJsonableError<'Passed value'> + : T extends JsonPrimitive + ? T + : // Any object with toJSON is special case + T extends { + toJSON(): infer J; + } + ? (() => J) extends () => JsonValue // Is J assignable to JsonValue? + ? CastBehavior extends 'combine' + ? T | J + : J // Then T is Jsonable and its Jsonable value is J + : Jsonlike // Maybe if we look a level deeper we'll find a JsonValue + : // Instanced primitives are objects + T extends Number + ? number + : T extends String + ? string + : T extends Boolean + ? boolean + : T extends Map | Set + ? EmptyObject + : T extends TypedArray + ? Record // Non-JSONable type union was found not empty + : T extends [] + ? [] + : T extends unknown[] + ? JsonlikeList + : T extends readonly unknown[] + ? JsonlikeList, CastBehavior> + : T extends object + ? { + [K in keyof T]: [T[K]] extends [NotJsonable] | [never] + ? IsNotJsonableError + : Jsonlike; // JsonifyObject recursive call for its children + } + : T extends undefined + ? T + : IsNotJsonableError<'Passed value'>; export interface Invalid { readonly __INVALID__: unique symbol; diff --git a/src/typed-fastify.ts b/src/typed-fastify.ts index 119f9cb..dd42d2d 100644 --- a/src/typed-fastify.ts +++ b/src/typed-fastify.ts @@ -132,10 +132,10 @@ export default addSchema; type Missing = [Candidate, MaybeRequired] extends [never, never] ? false : [Candidate] extends [never] - ? true - : [Candidate] extends [MaybeRequired] - ? false - : true; + ? true + : [Candidate] extends [MaybeRequired] + ? false + : true; type ExtractMethodPath = T extends `${M} ${infer P}` ? [M, P] @@ -153,8 +153,8 @@ type MP = type ExtractParams = T extends `${infer _}:${infer P}/${infer R}` ? ExtractParams : T extends `${infer _}:${infer P}` - ? Id - : Acc; + ? Id + : Acc; type ArrayTOrT = T | T[]; @@ -217,15 +217,15 @@ interface Reply< ...payload: [MissingStatus] extends [true] ? [Invalid<`Missing status`>] : [MissingHeaders] extends [true] - ? [ - Invalid<`Missing headers: [ ${Extract< - keyof Omit, - string - >} ]. Please provide required headers before sending reply.`>, - ] - : [Get2] extends [never] - ? [] - : [Jsonlike, 'combine'>] + ? [ + Invalid<`Missing headers: [ ${Extract< + keyof Omit, + string + >} ]. Please provide required headers before sending reply.`>, + ] + : [Get2] extends [never] + ? [] + : [Jsonlike, 'combine'>] ): AsReply; readonly request: Request; @@ -360,11 +360,11 @@ type GetInvalidParamsValidation< > = Router['Params'] extends never ? false : IsEqual extends false - ? Invalid<`request.params keys doesn't match params from router path, probably due to typo in [ ${Extract< - keyof DifferentKeys, - string - >} ] in path: [ ${Extract[1], string>} ]`> - : false; + ? Invalid<`request.params keys doesn't match params from router path, probably due to typo in [ ${Extract< + keyof DifferentKeys, + string + >} ] in path: [ ${Extract[1], string>} ]`> + : false; type Handler< Op extends Operation, @@ -381,8 +381,8 @@ type Handler< ValidSchema = [Op['response'][keyof Op['response']]] extends [never] ? Invalid<`${Extract} - has no response, every path should have at least one response defined`> : InvalidParams extends Invalid - ? InvalidParams - : true, + ? InvalidParams + : true, > = ValidSchema extends true ? ( this: F.FastifyInstance, diff --git a/test/tsconfig.test.json b/test/tsconfig.test.json index 1ce7693..55afd04 100644 --- a/test/tsconfig.test.json +++ b/test/tsconfig.test.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "baseUrl": ".", - "noEmit": true, + "noEmit": true }, "include": ["./**/*.ts", "../src"] }