diff --git a/modules/api-definition/.gitignore b/modules/api-definition/.gitignore new file mode 100644 index 0000000..dd20760 --- /dev/null +++ b/modules/api-definition/.gitignore @@ -0,0 +1,4 @@ +v8-compile-cache-* +dist +*.tsbuildinfo +.*-audit.json \ No newline at end of file diff --git a/modules/api-definition/package.json b/modules/api-definition/package.json new file mode 100644 index 0000000..3c77b7f --- /dev/null +++ b/modules/api-definition/package.json @@ -0,0 +1,55 @@ +{ + "name": "@myrepo/api-definition", + "version": "0.0.0", + "scripts": { + "test": "vitest run --coverage", + "compile": "tsc --noEmit", + "test:watch": "vitest", + "changeset": "echo 'Please run `pnpm changeset` from the monorepo root'", + "prepublishOnly": "pnpm run build", + "build": "vite build" + }, + "type": "module", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "@myrepo": "./src/index.ts", + "types": "./dist/api-definition/src/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/api-definition/src/index.d.ts", + "default": "./dist/index.cjs" + } + } + }, + "dependencies": { + "@myrepo/sum": "workspace:^", + "@zodios/core": "^10.9.6", + "zod": "^3.24.3" + }, + "files": [ + "README.md", + "dist", + "src" + ], + "repository": { + "type": "git", + "url": "https://github.com/cefn/nextjs-typescript-monorepo.git", + "directory": "modules/api-definition" + }, + "homepage": "https://github.com/cefn/nextjs-typescript-monorepo/tree/main/modules/api-definition/README.md", + "bugs": { + "url": "https://github.com/cefn/nextjs-typescript-monorepo/issues" + }, + "devDependencies": { + "@rollup/plugin-typescript": "^11.1.6", + "@vitest/coverage-v8": "^2.1.8", + "typescript": "^5.8.3", + "vite": "^6.0.6", + "vite-plugin-dts": "^4.4.0", + "vite-plugin-externalize-deps": "^0.8.0", + "vitest": "^2.1.8" + } +} \ No newline at end of file diff --git a/modules/api-definition/src/api.ts b/modules/api-definition/src/api.ts new file mode 100644 index 0000000..07ddfb6 --- /dev/null +++ b/modules/api-definition/src/api.ts @@ -0,0 +1,60 @@ +import { makeApi } from "@zodios/core"; +import { z } from "zod"; + +const DEFINITION_SCHEMA = z.object({ + definition: z.string(), + synonyms: z.array(z.string()), + antonyms: z.array(z.string()), +}); + +const WORD_SCHEMA = z.object({ + word: z.string(), + phonetic: z.string(), + meanings: z.object({ + partOfSpeech: z.string(), + definitions: z.array(DEFINITION_SCHEMA), + }), + sourceUrls: z.array(z.string().url()), +}); + +export const DICTIONARY_DOWNSTREAM_RESPONSE_SCHEMA = z.array(WORD_SCHEMA); + +export type DictionaryDownstreamResponse = z.infer< + typeof DICTIONARY_DOWNSTREAM_RESPONSE_SCHEMA +>; + +const DICTIONARY_BFF_SUCCESS_SCHEMA = z.object({ + httpStatus: z.literal(200), + data: DICTIONARY_DOWNSTREAM_RESPONSE_SCHEMA, +}); + +const DICTIONARY_BFF_FAILURE_SCHEMA = z.object({ + httpStatus: z.union([z.literal(400), z.literal(500), z.literal(503)]), + message: z.string(), +}); + +const DICTIONARY_BFF_RESPONSE_SCHEMA = z.union([ + DICTIONARY_BFF_SUCCESS_SCHEMA, + DICTIONARY_BFF_FAILURE_SCHEMA, +]); + +export type DictionaryBffResponse = z.infer< + typeof DICTIONARY_BFF_RESPONSE_SCHEMA +>; + +export const API = makeApi([ + { + method: "get", + path: "/dictionary/:query", + description: "Lookup a word", + parameters: [ + { + name: "query", + type: "Path", + schema: z.string(), + description: "The word to look up", + }, + ], + response: DICTIONARY_BFF_RESPONSE_SCHEMA, + }, +]); diff --git a/modules/api-definition/src/client.ts b/modules/api-definition/src/client.ts new file mode 100644 index 0000000..f89fee4 --- /dev/null +++ b/modules/api-definition/src/client.ts @@ -0,0 +1,7 @@ +import { Zodios } from "@zodios/core"; + +import { API } from "./api.ts"; + +export function createClient(endpoint: string) { + return new Zodios(endpoint, API); +} diff --git a/modules/api-definition/src/index.ts b/modules/api-definition/src/index.ts new file mode 100644 index 0000000..3ad3504 --- /dev/null +++ b/modules/api-definition/src/index.ts @@ -0,0 +1,2 @@ +export * from "./api.ts"; +export * from "./client.ts"; diff --git a/modules/api-definition/tsconfig.json b/modules/api-definition/tsconfig.json new file mode 100644 index 0000000..4f65f89 --- /dev/null +++ b/modules/api-definition/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "outDir": "./dist", + "declaration": true + } +} diff --git a/modules/api-definition/vite.config.ts b/modules/api-definition/vite.config.ts new file mode 100644 index 0000000..1fd5ba0 --- /dev/null +++ b/modules/api-definition/vite.config.ts @@ -0,0 +1,39 @@ +import typescript from "@rollup/plugin-typescript"; +import path from "path"; +import dts from "vite-plugin-dts"; +import { externalizeDeps } from "vite-plugin-externalize-deps"; +import { defineConfig } from "vitest/config"; + +import { coverage } from "./vite.coverage.ts"; + +export default defineConfig({ + resolve: { + conditions: ["@myrepo"], + }, + build: { + rollupOptions: { + plugins: [ + typescript({ + project: "./tsconfig.json", + sourceMap: true, + declaration: true, + outDir: "dist", + module: "nodeNext", + }), + ], + }, + sourcemap: "inline", + minify: false, + lib: { + entry: path.resolve(__dirname, "src/index.ts"), + fileName: "index", + formats: ["es", "cjs"], + }, + }, + plugins: [dts(), externalizeDeps()], + test: { + reporters: ["verbose"], + disableConsoleIntercept: true, + coverage, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index beeb89d..5ec6021 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,40 @@ importers: specifier: ^8.3.0 version: 8.3.0 + modules/api-definition: + dependencies: + '@myrepo/sum': + specifier: workspace:^ + version: link:../sum + '@zodios/core': + specifier: ^10.9.6 + version: 10.9.6(axios@1.7.9)(zod@3.24.3) + zod: + specifier: ^3.24.3 + version: 3.24.3 + devDependencies: + '@rollup/plugin-typescript': + specifier: ^11.1.6 + version: 11.1.6(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3) + '@vitest/coverage-v8': + specifier: ^2.1.8 + version: 2.1.9(vitest@2.1.9(@types/node@22.10.10)) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + vite: + specifier: ^6.0.6 + version: 6.3.4(@types/node@22.10.10)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.7.0) + vite-plugin-dts: + specifier: ^4.4.0 + version: 4.5.3(@types/node@22.10.10)(rollup@4.40.1)(typescript@5.8.3)(vite@6.3.4(@types/node@22.10.10)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-externalize-deps: + specifier: ^0.8.0 + version: 0.8.0(vite@6.3.4(@types/node@22.10.10)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.7.0)) + vitest: + specifier: ^2.1.8 + version: 2.1.9(@types/node@22.10.10) + modules/multiply: dependencies: '@myrepo/sum': @@ -90,12 +124,21 @@ importers: servers/express-with-dependencies: dependencies: + '@myrepo/api-definition': + specifier: workspace:^ + version: link:../../modules/api-definition '@myrepo/multiply': specifier: workspace:^ version: link:../../modules/multiply '@myrepo/sum': specifier: workspace:^ version: link:../../modules/sum + '@zodios/core': + specifier: ^10.9.6 + version: 10.9.6(axios@1.7.9)(zod@3.24.3) + '@zodios/express': + specifier: ^10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.9)(zod@3.24.3))(express@4.21.2)(zod@3.24.3) express: specifier: ^4.21.2 version: 4.21.2 @@ -1292,6 +1335,19 @@ packages: '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@zodios/core@10.9.6': + resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + + '@zodios/express@10.6.1': + resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} + peerDependencies: + '@zodios/core': '>=10.4.4 <11.0.0' + express: 4.x + zod: ^3.x + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -3271,6 +3327,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod@3.24.3: + resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + zx@8.3.0: resolution: {integrity: sha512-L8mY3yfJwo3a8ZDD6f9jZzAcRWJZYcV8GauZmBxLB/aSTwaMzMIEVpPp2Kyx+7yF0gdvuxKnMxAZRft9UCawiw==} engines: {node: '>= 12.17.0'} @@ -4159,6 +4218,17 @@ snapshots: '@vue/shared@3.5.13': {} + '@zodios/core@10.9.6(axios@1.7.9)(zod@3.24.3)': + dependencies: + axios: 1.7.9 + zod: 3.24.3 + + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.9)(zod@3.24.3))(express@4.21.2)(zod@3.24.3)': + dependencies: + '@zodios/core': 10.9.6(axios@1.7.9)(zod@3.24.3) + express: 4.21.2 + zod: 3.24.3 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -6501,6 +6571,8 @@ snapshots: yocto-queue@0.1.0: {} + zod@3.24.3: {} + zx@8.3.0: optionalDependencies: '@types/fs-extra': 11.0.4 diff --git a/servers/express-with-dependencies/package.json b/servers/express-with-dependencies/package.json index 4cd59a8..68d5fa0 100644 --- a/servers/express-with-dependencies/package.json +++ b/servers/express-with-dependencies/package.json @@ -10,16 +10,19 @@ "private": true, "type": "module", "dependencies": { + "@myrepo/api-definition": "workspace:^", "@myrepo/multiply": "workspace:^", "@myrepo/sum": "workspace:^", + "@zodios/core": "^10.9.6", + "@zodios/express": "^10.6.1", "express": "^4.21.2", "tsx": "^4.19.2" }, "devDependencies": { "@types/express": "^5.0.0", - "wait-on": "^8.0.2", "typescript": "^5.8.3", - "vitest": "^2.1.8" + "vitest": "^2.1.8", + "wait-on": "^8.0.2" }, "repository": { "type": "git", @@ -30,4 +33,4 @@ "bugs": { "url": "https://github.com/cefn/nextjs-typescript-monorepo/issues" } -} \ No newline at end of file +} diff --git a/servers/express-with-dependencies/src/api.ts b/servers/express-with-dependencies/src/api.ts new file mode 100644 index 0000000..e69de29