diff --git a/README.md b/README.md index f6fd5c9..acfb289 100644 --- a/README.md +++ b/README.md @@ -77,3 +77,7 @@ Chrome Extension is not yet published to Chrome Web Store, because it's in waiti You can only install it downloading the build and loading it as an unpacked extension. [![Download Chrome Extension](https://img.shields.io/badge/download-chrome_extension_dist-ff6a33)](https://github.com/vkruglikov/msw-devtools-extension/releases/tag/%40msw-devtools%2Fextension%40latest) + +### Upload JSON Config to extension + +[@msw-devtools/json-config](./packages/json-config/README.md) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d35f584..8d92fe2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2214,6 +2214,23 @@ "node": ">=14.17.0" } }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@inquirer/confirm": { "version": "5.1.1", "license": "MIT", @@ -4308,6 +4325,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bundle-require": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.2.1.tgz", + "integrity": "sha512-7Q/6vkyYAwOmQNRw75x+4yRtZCZJXUDmHHlFdkiV0wgv/reNjtJwpu1jPJ0w2kbEpIM0uoKI3S4/f39dU7AjSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.17" + } + }, "node_modules/bytes": { "version": "3.1.2", "dev": true, @@ -5524,6 +5557,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/escalade": { "version": "3.2.0", "license": "MIT", @@ -7381,6 +7453,16 @@ "dev": true, "license": "MIT" }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "dev": true, @@ -11730,6 +11812,45 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod2md": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/zod2md/-/zod2md-0.1.4.tgz", + "integrity": "sha512-ZEW9TZd4M9PHB/UeZcLXIjlCbzPUESGvzEN+Ttye18quh4Afap8DYd/zpIPfw+DrVsSSWoNU40HVnfE9UcpmPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^12.0.0", + "bundle-require": "^4.0.2", + "commander": "^12.0.0", + "esbuild": "^0.19.11" + }, + "bin": { + "zod2md": "dist/bin.js" + }, + "peerDependencies": { + "zod": "^3.22.0" + } + }, + "node_modules/zod2md/node_modules/@commander-js/extra-typings": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-12.1.0.tgz", + "integrity": "sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "commander": "~12.1.0" + } + }, + "node_modules/zod2md/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "packages/connect": { "name": "@msw-devtools/connect", "version": "0.0.3", @@ -11778,6 +11899,9 @@ "license": "MIT", "dependencies": { "zod": "^3.24.1" + }, + "devDependencies": { + "zod2md": "^0.1.4" } }, "packages/wds-extension-client": { diff --git a/package.json b/package.json index 3730cbc..10f6a47 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "build": "turbo check-types build", "changeset": "changeset", "dev": "turbo dev", + "docs": "turbo docs", "clean": "rimraf packages/*/{node_modules,dist,.turbo} node_modules .turbo" }, "author": "Valentin Kruglikov", diff --git a/packages/extension/README.md b/packages/extension/README.md index 857e1ff..02758d1 100644 --- a/packages/extension/README.md +++ b/packages/extension/README.md @@ -1,3 +1,4 @@ +![Private](https://img.shields.io/badge/status-private-red?) ### Start dev server ```bash diff --git a/packages/json-config/README.md b/packages/json-config/README.md index 0df217e..dc3842c 100644 --- a/packages/json-config/README.md +++ b/packages/json-config/README.md @@ -1,3 +1,44 @@ -### @msw-devtools/json-config +![Private](https://img.shields.io/badge/status-private-red?) +## @msw-devtools/json-config + +JSON config used by the extension to configure the responses. + +### Example config +```json +{ + "version": 1, + "name": "example", + "handlers": { + "/profile": { + "method": "GET", + "body": { + "name": "Valentin", + "surname": "Kruglikov", + "location": "World" + }, + "init": { + "headers": { + "Content-Type": "application/json" + } + } + }, + "/forbidden": { + "method": "GET", + "body": "403 Forbidden", + "init": { + "status": 403, + "headers": { + "Content-Type": "application/json" + } + } + } + } +} +``` + +### ZOD Schema +[MSW JSON Config reference](./docs/README.md) + +### Create your own config +[DEMO page](https://vkruglikov.github.io/msw-devtools-extension/) -WIP \ No newline at end of file diff --git a/packages/json-config/docs/README.md b/packages/json-config/docs/README.md new file mode 100644 index 0000000..d50243f --- /dev/null +++ b/packages/json-config/docs/README.md @@ -0,0 +1,29 @@ +# MSW JSON Config reference + +## JsonConfigHandler + +Response configuration. + +_Object containing the following properties:_ + +| Property | Description | Type | +| :---------------- | :------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`method`** (\*) | HTTP method to intercept. | `'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'` | +| **`body`** (\*) | Response's body. Can be a string or a JSON object. | _Object with properties:_ _or_ `string` | +| **`init`** (\*) | Response init object. | _Object with properties:_ | + +_(\*) Required._ + +## JsonConfig + +JSON configuration schema. + +_Object containing the following properties:_ + +| Property | Description | Type | +| :------------------ | :--------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | +| **`version`** (\*) | Major version of the config. If it changes, the config should not be compatible with the previous version. | `1` | +| **`name`** (\*) | Name of the config. This name displayed in the extension's list of configs. Should be unique per host. | `string` (_min length: 3, max length: 50_) | +| **`handlers`** (\*) | List of handlers to intercept. | _Object with dynamic keys of type_ `string` _and values of type_ [JsonConfigHandler](#jsonconfighandler) | + +_(\*) Required._ diff --git a/packages/json-config/package.json b/packages/json-config/package.json index 22588fd..151c914 100644 --- a/packages/json-config/package.json +++ b/packages/json-config/package.json @@ -11,10 +11,14 @@ "dist" ], "scripts": { + "docs": "npx zod2md --entry src/index.ts --title \"MSW JSON Config reference\" --output docs/README.md", "build": "microbundle --no-pkg-main -f modern", "dev": "microbundle watch --no-pkg-main -f modern" }, "dependencies": { "zod": "^3.24.1" + }, + "devDependencies": { + "zod2md": "^0.1.4" } } diff --git a/packages/json-config/src/index.ts b/packages/json-config/src/index.ts index c5b79fa..1d11a14 100644 --- a/packages/json-config/src/index.ts +++ b/packages/json-config/src/index.ts @@ -1,36 +1,5 @@ -import { z } from 'zod' - -export const MAJOR_CONFIG_VERSION = 1 - -export const jsonConfigSchema = z.object({ - version: z.literal(MAJOR_CONFIG_VERSION), - name: z.string().min(3).max(50), - handlers: z.record( - z.string(), - z.object({ - method: z.enum([ - 'GET', - 'POST', - 'PUT', - 'DELETE', - 'PATCH', - 'HEAD', - 'OPTIONS' - ]), - body: z.union([z.object({}).catchall(z.any()), z.string()]), - init: z.object({ - headers: z - .object({ - 'Content-Type': z.string() - }) - .catchall(z.union([z.string(), z.null()])), - status: z.number().int().min(100).max(599).optional() - }) - }) - ) -}) - -export type JsonConfig = z.infer +export * from './schemas' +import { jsonConfigSchema, JsonConfig } from './schemas' export const validateJsonConfig = (data: any): JsonConfig => jsonConfigSchema.parse(data) diff --git a/packages/json-config/src/schemas.ts b/packages/json-config/src/schemas.ts new file mode 100644 index 0000000..a5dd648 --- /dev/null +++ b/packages/json-config/src/schemas.ts @@ -0,0 +1,66 @@ +import { z } from 'zod' + +const MAJOR_CONFIG_VERSION = 1 + +export type JsonConfig = z.infer + +/** + * Every time, don't forget to run `npm run docs` + */ +export const jsonConfigHandler = z + .object({ + method: z + .enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']) + .describe('HTTP method to intercept.'), + body: z + .union([z.object({}).catchall(z.any()), z.string()]) + .describe("Response's body. Can be a string or a JSON object."), + init: z + .object({ + headers: z + .object({ + 'Content-Type': z.string() + }) + .catchall(z.union([z.string(), z.null()])) + .describe('Response headers.'), + status: z + .number() + .int() + .min(100) + .max(599) + .optional() + .describe('Response status code.') + }) + .describe('Response init object.') + }) + .describe('Response configuration.') + +export const jsonConfigSchema = z + .object({ + version: z + .literal(MAJOR_CONFIG_VERSION) + .describe( + 'Major version of the config. If it changes, the config should ' + + 'not be compatible with the previous version.' + ), + name: z + .string() + .min(3) + .max(50) + .describe( + "Name of the config. This name displayed in the extension's list " + + 'of configs. Should be unique per host.' + ), + handlers: z + .record( + z + .string() + .describe( + 'Url path or full URL to intercept. ' + + 'Example: "/api", "http://localhost:3000/api"' + ), + jsonConfigHandler + ) + .describe('List of handlers to intercept.') + }) + .describe('JSON configuration schema.') diff --git a/packages/wds-extension-client/README.md b/packages/wds-extension-client/README.md index 2d57351..b0bfd47 100644 --- a/packages/wds-extension-client/README.md +++ b/packages/wds-extension-client/README.md @@ -1,3 +1,4 @@ +![Private](https://img.shields.io/badge/status-private-red?) ## Webpack dev server chrome extension client Calls `chrome.runtime.reload()` every time the dev server sends a reload message. diff --git a/turbo.json b/turbo.json index 216a62a..633454b 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,9 @@ "dependsOn": ["^build"], "persistent": true, "cache": false + }, + "docs": { + "cache": false } } }