@nxgt/graphql-codegen-msw 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -2
  3. package/THIRD-PARTY-NOTICES.md +71 -0
  4. package/dist/config.d.ts +66 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/imports.d.ts +17 -0
  7. package/dist/imports.d.ts.map +1 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +365 -0
  11. package/dist/index.js.map +15 -0
  12. package/dist/input-types.d.ts +11 -0
  13. package/dist/input-types.d.ts.map +1 -0
  14. package/dist/naming.d.ts +23 -0
  15. package/dist/naming.d.ts.map +1 -0
  16. package/dist/operations.d.ts +17 -0
  17. package/dist/operations.d.ts.map +1 -0
  18. package/dist/options.d.ts +31 -0
  19. package/dist/options.d.ts.map +1 -0
  20. package/dist/runtime/coercion/cross-inspect.d.ts +19 -0
  21. package/dist/runtime/coercion/cross-inspect.d.ts.map +1 -0
  22. package/dist/runtime/coercion/document.d.ts +51 -0
  23. package/dist/runtime/coercion/document.d.ts.map +1 -0
  24. package/dist/runtime/coercion/index.d.ts +30 -0
  25. package/dist/runtime/coercion/index.d.ts.map +1 -0
  26. package/dist/runtime/coercion/inspect.d.ts +9 -0
  27. package/dist/runtime/coercion/inspect.d.ts.map +1 -0
  28. package/dist/runtime/coercion/issues.d.ts +19 -0
  29. package/dist/runtime/coercion/issues.d.ts.map +1 -0
  30. package/dist/runtime/coercion/messages.d.ts +37 -0
  31. package/dist/runtime/coercion/messages.d.ts.map +1 -0
  32. package/dist/runtime/coercion/suggestions.d.ts +15 -0
  33. package/dist/runtime/coercion/suggestions.d.ts.map +1 -0
  34. package/dist/runtime/coercion/validate.d.ts +39 -0
  35. package/dist/runtime/coercion/validate.d.ts.map +1 -0
  36. package/dist/runtime/index.d.ts +6 -0
  37. package/dist/runtime/index.d.ts.map +1 -0
  38. package/dist/runtime/index.js +595 -0
  39. package/dist/runtime/index.js.map +21 -0
  40. package/dist/runtime/mock-operation.d.ts +65 -0
  41. package/dist/runtime/mock-operation.d.ts.map +1 -0
  42. package/dist/runtime/refusals.d.ts +36 -0
  43. package/dist/runtime/refusals.d.ts.map +1 -0
  44. package/dist/runtime/response.d.ts +34 -0
  45. package/dist/runtime/response.d.ts.map +1 -0
  46. package/docs/README.md +14 -0
  47. package/docs/guide/handlers.md +654 -0
  48. package/docs/roadmap.md +40 -0
  49. package/docs/troubleshooting.md +304 -0
  50. package/package.json +80 -5
@@ -0,0 +1,304 @@
1
+ # Troubleshooting
2
+
3
+ One entry per error you can hit, headed by the message you will search for.
4
+ Configuration and Operations happen while `graphql-codegen` runs; The
5
+ generated file is what `tsc` reports; At run time is what a handler throws
6
+ or answers.
7
+
8
+ - [Configuration](#configuration)
9
+ - [Operations](#operations)
10
+ - [The generated file](#the-generated-file)
11
+ - [At run time](#at-run-time)
12
+
13
+ ## Configuration
14
+
15
+ ### `@nxgt/graphql-codegen-msw: endpoint is not set; set it to the URL your client sends its operations to, e.g. endpoint: 'https://api.example.com/graphql', or a path msw matches on any origin: '*/graphql'.`
16
+
17
+ **When:** running codegen without `endpoint`, or with an empty one.
18
+ **Why:** every handler is built on msw's `graphql.link(endpoint)`, and the
19
+ plugin does not guess where your client sends its operations.
20
+ **Fix:** set it; a handler can still answer elsewhere through
21
+ `options.endpoint`.
22
+
23
+ ```ts
24
+ config: { endpoint: 'https://api.example.com/graphql', schemasImport: './zod', typesImport: './graphql' }
25
+ ```
26
+
27
+ ### `@nxgt/graphql-codegen-msw: schemasImport is not set; point it at the file @nxgt/graphql-codegen-zod writes, as this file imports it, e.g. schemasImport: './zod'.`
28
+
29
+ **When:** running codegen without `schemasImport`.
30
+ **Why:** each handler checks its variables with codegen-zod's
31
+ `z<Operation>Variables`; without it the mock would take any input.
32
+ **Fix:** generate codegen-zod's file and point the option at it, relative to
33
+ the generated file.
34
+
35
+ ```ts
36
+ generates: {
37
+ 'src/generated/zod.ts': { plugins: ['@nxgt/graphql-codegen-zod'] },
38
+ 'src/generated/msw.ts': {
39
+ plugins: ['@nxgt/graphql-codegen-msw'],
40
+ config: { endpoint: '*/graphql', schemasImport: './zod', typesImport: './graphql' },
41
+ },
42
+ },
43
+ ```
44
+
45
+ ### `@nxgt/graphql-codegen-msw: typesImport is not set; point it at the module that exports each operation's result and variables types, as this file imports it: './graphql' for the client preset, or the file typescript-operations writes.`
46
+
47
+ **When:** running codegen without `typesImport`.
48
+ **Why:** each handler's resolver is typed with the operation's result and
49
+ variables types, which the plugin imports rather than writes.
50
+ **Fix:** point it at the client preset's `graphql.ts` or
51
+ `typescript-operations`' file, relative to the generated file.
52
+
53
+ ```ts
54
+ config: { endpoint: '*/graphql', schemasImport: './zod', typesImport: './graphql' }
55
+ ```
56
+
57
+ ### `@nxgt/graphql-codegen-msw: invalidInput is "invalidInput"; write 'invalid-input', 'bad-user-input' or '<module>#<export>', e.g. './invalid-input#invalidInput'.`
58
+
59
+ **When:** running codegen with an `invalidInput` that is neither of the two
60
+ answers the plugin knows nor a `module#export`. The value in the message is
61
+ the one you wrote.
62
+ **Why:** a module needs an export name after the last `#`, and the plugin
63
+ does not guess it.
64
+ **Fix:**
65
+
66
+ ```ts
67
+ config: { …, invalidInput: '../mocks/invalid-input#invalidInput' }
68
+ ```
69
+
70
+ ### `@nxgt/graphql-codegen-msw: translate is "./i18n"; write '<module>#<export>', e.g. './i18n#translate'.`
71
+
72
+ **When:** running codegen with a `translate` that is not `module#export`.
73
+ **Why:** the generated file imports one export of that module.
74
+ **Fix:**
75
+
76
+ ```ts
77
+ config: { …, translate: '../i18n#translate' }
78
+ ```
79
+
80
+ ### `@nxgt/graphql-codegen-msw: translate words the default invalidInput's messages only, and invalidInput is "bad-user-input". Remove translate, or translate inside your own function.`
81
+
82
+ **When:** running codegen with `translate` beside `invalidInput:
83
+ 'bad-user-input'` or a function of yours.
84
+ **Why:** `translate` is handed to `invalidInputRefusal`, the default answer.
85
+ `BAD_USER_INPUT` carries Zod's messages, and your function words its own.
86
+ **Fix:** remove `translate`, or call your translator inside your function.
87
+
88
+ ```ts
89
+ import {
90
+ fieldIssueOf,
91
+ type InvalidInput,
92
+ invalidInputError,
93
+ } from '@nxgt/graphql-codegen-msw/runtime';
94
+ import { translate } from '../i18n';
95
+
96
+ export const invalidInput: InvalidInput = (issues) => ({
97
+ error: invalidInputError(issues.map(fieldIssueOf), { translate }),
98
+ status: 400,
99
+ });
100
+ ```
101
+
102
+ ### `@nxgt/graphql-codegen-msw: namingConvention "my-module#myCase" is not one this plugin reads. Use 'keep', a function, or 'change-case-all#<case>' with one of camelCase, capitalCase, constantCase, dotCase, headerCase, noCase, paramCase, pascalCase, pathCase, sentenceCase, snakeCase, lowerCase, upperCase, lowerCaseFirst, upperCaseFirst.`
103
+
104
+ **When:** running codegen with a `namingConvention` string that names
105
+ another module, or a case not in the list.
106
+ **Why:** the plugin reads the forms the typescript plugins take for type
107
+ names, but only `change-case-all` and `change-case` cases; it does not load a
108
+ module.
109
+ **Fix:** use one of the forms, or pass a function. Set it at the root of the
110
+ codegen config, so the types, the schemas and the handlers agree.
111
+
112
+ ```ts
113
+ config: { namingConvention: 'change-case-all#pascalCase' }
114
+ ```
115
+
116
+ ## Operations
117
+
118
+ ### `@nxgt/graphql-codegen-msw: the file would declare mockGetUserQuery twice: two operations give it the same name. Rename one of them.`
119
+
120
+ **When:** running codegen on documents with two operations whose names
121
+ convert to one (`getUser` and `GetUser`, or `UserQuery` and `User` under
122
+ `dedupeOperationSuffix`).
123
+ **Why:** each handler is named after its operation's converted name; two
124
+ would collide, as their types do in the typescript plugins' files.
125
+ **Fix:** rename one operation.
126
+
127
+ ### No handler for an operation
128
+
129
+ **When:** an operation is in the documents, but the generated file has no
130
+ `mock…` for it.
131
+ **Why:** it is anonymous (msw matches by name), or a subscription (msw 3 mocks
132
+ subscriptions over WebSocket only; see the [roadmap](roadmap.md)).
133
+ **Fix:** name the operation. For a subscription, use msw's own
134
+ `graphql.link(endpoint).subscription(name, resolver)`.
135
+
136
+ ## The generated file
137
+
138
+ ### `TS2305: Module '"./graphql"' has no exported member 'UserQuery'`
139
+
140
+ **When:** typechecking the generated file, with a type name in the message.
141
+ **Why:** `typesImport` points at a module that names its types otherwise:
142
+ another naming option (`typesPrefix`, `omitOperationSuffix`, …) is set on
143
+ that output only, or the module is not the operations' types.
144
+ **Fix:** point `typesImport` at the client preset's `graphql.ts` or
145
+ `typescript-operations`' file, and move the naming options to the root
146
+ `config` of `codegen.ts`, so every output reads them.
147
+
148
+ ### `TS2305: Module '"./zod"' has no exported member 'zUserQueryVariables'`
149
+
150
+ **When:** typechecking the generated file.
151
+ **Why:** `schemasImport` points at a file that is not codegen-zod's, or
152
+ codegen-zod's output names its schemas otherwise (`schemaPrefix` or a naming
153
+ option set on one output only).
154
+ **Fix:** point `schemasImport` at codegen-zod's file and set the naming
155
+ options, `schemaPrefix` included, at the root `config`.
156
+
157
+ ### `TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './zod.js'?`
158
+
159
+ **When:** typechecking the generated file under `moduleResolution:
160
+ nodenext` or `node16`.
161
+ **Why:** the plugin writes each module as you configured it.
162
+ **Fix:** give each relative module its `.js` extension: `schemasImport:
163
+ './zod.js'`, `typesImport: './graphql.js'`, `invalidInput:
164
+ '../mocks/invalid-input.js#invalidInput'`.
165
+
166
+ ### `Type '(issues: …) => …' is not assignable to type 'InvalidInput'`
167
+
168
+ **When:** typechecking the generated file, at `export const invalidInput:
169
+ InvalidInput = …`.
170
+ **Why:** the function `invalidInput` names does not take `(issues,
171
+ operation)` or does not return `{ error, status }`.
172
+ **Fix:** type it with `InvalidInput`, so the error shows in your module.
173
+
174
+ ```ts
175
+ import type { InvalidInput } from '@nxgt/graphql-codegen-msw/runtime';
176
+
177
+ export const invalidInput: InvalidInput = (issues) => ({
178
+ error: { message: 'Invalid input' },
179
+ status: 400,
180
+ });
181
+ ```
182
+
183
+ ### `Object literal may only specify known properties, and 'nickname' does not exist in type …`
184
+
185
+ **When:** typechecking a resolver whose `data` holds a field the operation
186
+ does not select.
187
+ **Why:** `data` is typed with the operation's result type.
188
+ **Fix:** answer what the operation selects, or add the field to the
189
+ operation.
190
+
191
+ ## At run time
192
+
193
+ ### `@nxgt/graphql-codegen-msw: the resolver of User returned an iterator (a generator function?). Each request's variables are checked before the resolver runs, which msw cannot do once it keeps an iterator: answer each request from a plain function, or register one handler per answer with { once: true }.`
194
+
195
+ **When:** a request reaches a handler whose resolver is a generator function
196
+ (the resolver's type refuses one; a cast lets it through). msw answers that
197
+ request with a 500.
198
+ **Why:** msw keeps a generator's iterator and answers the next requests from
199
+ it without calling the handler again, so their variables would go unchecked.
200
+ **Fix:** one handler per answer, in the order they should answer:
201
+
202
+ ```ts
203
+ server.use(
204
+ mockUserQuery(() => HttpResponse.json({ data: { user: first } }), { once: true }),
205
+ mockUserQuery(() => HttpResponse.json({ data: { user: second } })),
206
+ );
207
+ ```
208
+
209
+ ### `@nxgt/graphql-codegen-msw: the generated file was written by another version than the runtime; regenerate it`
210
+
211
+ **When:** a request reaches a handler whose generated file describes an input
212
+ type in a form this version of the runtime does not read (its `inputTypes`
213
+ entry has a `kind` the runtime does not know). msw answers that request with
214
+ a 500.
215
+ **Why:** `inputTypes` and `variableTypes` are an internal shape, written by
216
+ the plugin and read by the runtime of the same version; the file was
217
+ generated by another version of `@nxgt/graphql-codegen-msw` than the one
218
+ installed.
219
+ **Fix:** regenerate the file with the installed version:
220
+
221
+ ```sh
222
+ bunx graphql-codegen
223
+ ```
224
+
225
+ A handler you write yourself with `mockOperation` can leave `variableTypes`
226
+ and `inputTypes` out: no variable is then walked, and every refusal goes to
227
+ `invalidInput`.
228
+
229
+ ### A request goes to the network under Bun: `getaddrinfo ENOTFOUND api.example.com`
230
+
231
+ **When:** a test under `bun test` uses msw/node's `setupServer`.
232
+ **Why:** `setupServer` intercepts no request under Bun (msw 3.0.3, Bun
233
+ 1.4.2): its `fetch` interceptor works through Node's sockets, which Bun's
234
+ `fetch` does not use.
235
+ **Fix:** run that test under Node, or build msw's network on the web `fetch`
236
+ interceptor, as in [Under Bun](guide/handlers.md#under-bun).
237
+
238
+ ### Every request answers 400 `validation.errors.invalid-input`
239
+
240
+ **When:** a handler refuses variables the server takes.
241
+ **Why:** a constraint of the variables' schema refuses them;
242
+ `extensions.fields` says where, each field's `code` which kind of rule
243
+ (`too-short`, `out-of-range`, `invalid-format`, …).
244
+ **Fix:** send what the schema takes; the field's `path` and `code` name the
245
+ rule that refused. One case the server takes: an integer for an `ID`
246
+ (codegen-zod takes an `ID` as a string only, where GraphQL also takes an
247
+ integer), answered `validation.errors.invalid`; send the `ID` as a string:
248
+ `{ id: String(user.id) }`.
249
+
250
+ ### An invalid input answers 200 `BAD_USER_INPUT` with no `data`
251
+
252
+ **When:** the generated file is written with `invalidInput: 'bad-user-input'`,
253
+ and a test expects a 4xx status, or a `data` key beside the error.
254
+ **Why:** this answer stands for the error `withValidation` raises inside a
255
+ resolver: a GraphQL server answers a resolver's error with status 200. The
256
+ mock answers `{ errors: [error] }` alone, without the `data` (and the error's
257
+ `path` and `locations`) the server's execution adds, since no resolver runs.
258
+ **Fix:** expect status 200 and read the error, not the status:
259
+
260
+ ```ts
261
+ const response = await fetch(endpoint, { method: 'POST', headers, body });
262
+ expect(response.status).toBe(200);
263
+ const { errors } = await response.json();
264
+ expect(errors[0].extensions.code).toBe('BAD_USER_INPUT');
265
+ ```
266
+
267
+ For an API that answers invalid input with a 400, generate with the default
268
+ `invalidInput` (`'invalid-input'`) or your own function, which returns its
269
+ status.
270
+
271
+ ### A request answers 400 `Variable "$id" got invalid value true; ID cannot represent value: true`
272
+
273
+ **When:** the variables hold a value the API refuses while coercing them: a
274
+ missing or `null` value for a non-null type, a value of the wrong type, an
275
+ unknown enum value, an input field the type does not declare, an `Int`
276
+ beyond 32 bits, a value a custom scalar refuses.
277
+ **Why:** graphql-yoga refuses these before any resolver or validation runs,
278
+ with one plain error per problem and status 400; the mock answers the same,
279
+ in graphql 17's words, whatever `invalidInput` is.
280
+ **Fix:** send what the variable's type takes; the message names the type and,
281
+ for an enum value or an input field, graphql's closest match (`Did you mean
282
+ "tags"?`).
283
+
284
+ ```ts
285
+ await client.query(UserDocument, { id: String(user.id) });
286
+ ```
287
+
288
+ ### A message differs from the API's after an upgrade
289
+
290
+ **When:** the coercion answer names a type, or a field, the schema no longer
291
+ has.
292
+ **Why:** the generated file describes the input types from the schema at
293
+ generation time (`inputTypes`, `variableTypes`); they go stale with the
294
+ schema, and their shape may change with this package.
295
+ **Fix:** regenerate the file after a schema change and after upgrading
296
+ `@nxgt/graphql-codegen-msw`.
297
+
298
+ ### The resolver's error answers 500
299
+
300
+ **When:** the resolver throws something that is not a `GraphQLError`.
301
+ **Why:** only a `GraphQLError` (any copy of `graphql`, recognised by its name
302
+ and `toJSON`) is answered as a GraphQL error; anything else is msw's, which
303
+ answers 500.
304
+ **Fix:** throw a `GraphQLError`, or return `errorResponse(...)`.
package/package.json CHANGED
@@ -1,6 +1,81 @@
1
1
  {
2
- "name": "@nxgt/graphql-codegen-msw",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@nxgt/graphql-codegen-msw",
3
+ "version": "0.1.0",
4
+ "description": "A graphql-codegen plugin that writes one typed msw 3 handler per named query and mutation, its variables checked by the Zod schemas of @nxgt/graphql-codegen-zod and an invalid input answered as your API answers it",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "docs",
12
+ "README.md",
13
+ "package.json",
14
+ "LICENSE",
15
+ "THIRD-PARTY-NOTICES.md"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./runtime": {
24
+ "types": "./dist/runtime/index.d.ts",
25
+ "import": "./dist/runtime/index.js",
26
+ "default": "./dist/runtime/index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "keywords": [
31
+ "graphql",
32
+ "codegen",
33
+ "graphql-codegen",
34
+ "msw",
35
+ "mock",
36
+ "zod",
37
+ "validation",
38
+ "typescript"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/softistx/nxgt-graphql.git",
43
+ "directory": "packages/graphql-codegen-msw"
44
+ },
45
+ "publishConfig": {
46
+ "registry": "https://registry.npmjs.org",
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "bun run ../../build.ts",
51
+ "test": "bun test src",
52
+ "typecheck": "tsc --noEmit",
53
+ "generated:write": "bun run test/write-generated.ts"
54
+ },
55
+ "nxgt": {
56
+ "entrypoints": [
57
+ "src/index.ts",
58
+ "src/runtime/index.ts"
59
+ ]
60
+ },
61
+ "dependencies": {
62
+ "@nxgt/graphql-validation": "^0.4.0",
63
+ "change-case": "^5.4.4"
64
+ },
65
+ "devDependencies": {
66
+ "@graphql-codegen/typescript": "6.1.2",
67
+ "@graphql-codegen/typescript-operations": "6.1.10",
68
+ "@mswjs/interceptors": "0.45.7",
69
+ "@nxgt/graphql-codegen-zod": "^0.3.1",
70
+ "@types/bun": "^1.4.0",
71
+ "graphql": "16.11.0",
72
+ "msw": "3.0.3",
73
+ "zod": "4.6.5"
74
+ },
75
+ "peerDependencies": {
76
+ "graphql": "^16.11.0 || ^17.0.0",
77
+ "msw": "^3.0.3",
78
+ "typescript": "^6.0.3 || ^7.0.0",
79
+ "zod": ">=4.6.5 <5"
80
+ }
81
+ }