@nxgt/typespec 0.11.0 → 0.13.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.
@@ -116,7 +116,7 @@ tells a cache, a proxy or a retry that the request is safe. An operation
116
116
  | Verb | Sent with |
117
117
  | --- | --- |
118
118
  | `list`, `read`, `find`, `count`, `get` | `GET` or `HEAD` |
119
- | `search`, `query` | `GET` or `POST`, for criteria too large for a query string |
119
+ | `search`, `query` | `GET`, `POST`, or a `POST` marked [`@queryMethod`](#a-query-in-openapi-32), for criteria too large for a query string |
120
120
  | `create`, `createMany` | `POST` |
121
121
  | `update`, `updateMany` | `PUT` or `PATCH` |
122
122
  | `patch` | `PATCH` |
@@ -171,10 +171,67 @@ Not checked:
171
171
  whatever it says;
172
172
  - an operation `@operationIds` does not name.
173
173
 
174
- `QUERY`, the HTTP method for a safe request with a body, is not one of
175
- `search` and `query`'s methods yet: `@typespec/http` 1.16 declares only
176
- `GET`, `PUT`, `POST`, `PATCH`, `DELETE` and `HEAD`
177
- ([roadmap](../roadmap.md#later)).
174
+ ## A QUERY in OpenAPI 3.2
175
+
176
+ `QUERY` is the HTTP method for a safe request with a body, and
177
+ `@typespec/http` 1.17 declares only `GET`, `PUT`, `POST`, `PATCH`, `DELETE`
178
+ and `HEAD`. `@queryMethod` marks a `@post` as the `QUERY` it stands for:
179
+
180
+ ```tsp
181
+ import "@typespec/http";
182
+ import "@nxgt/typespec";
183
+
184
+ using Http;
185
+ using Nxgt;
186
+
187
+ @service
188
+ @operationIds
189
+ namespace Shop;
190
+
191
+ model UserCriteria {
192
+ names: string[];
193
+ }
194
+
195
+ @route("/users")
196
+ interface Users {
197
+ @post @queryMethod @route("/search")
198
+ search(@body criteria: UserCriteria): string[]; // searchUsers
199
+ }
200
+ ```
201
+
202
+ With the emitter `@nxgt/typespec` and OpenAPI 3.2 the document gets a real
203
+ `QUERY`, the only version that has one:
204
+
205
+ ```yaml
206
+ /users/search:
207
+ query:
208
+ operationId: searchUsers
209
+ ```
210
+
211
+ Anything else is an error, not a `POST` with a mark: an older version in
212
+ `openapi-versions` (or none, which defaults to 3.0) is `query-method-needs-openapi-3.2`, and the plain
213
+ `@typespec/openapi3` emitter is `query-method-needs-nxgt-emitter`
214
+ ([troubleshooting](../troubleshooting.md)). `@nxgt/openapi-codegen`
215
+ generates `method: 'query'` from it
216
+ ([TypeSpec](https://github.com/softistx/nxgt-http/blob/develop/packages/openapi-codegen/docs/guide/typespec.md)).
217
+ The emitter and what a real `QUERY` needs are in [The emitter](emitter.md).
218
+ When `@typespec/http` declares `QUERY`, the decorator is what changes, not
219
+ your spec ([roadmap](../roadmap.md#later)).
220
+
221
+ | Case | Result |
222
+ | --- | --- |
223
+ | `search` or `query` marked | no warning: a `QUERY` is one of their methods |
224
+ | `findById`, `create` or another verb marked | `verb-method-mismatch`, with the method `QUERY`: `findById is sent with QUERY, where its verb find is sent with GET or HEAD` |
225
+ | a name that is not a verb, marked | nothing to check |
226
+ | marked and not a `@post` | the error `query-method-not-post` |
227
+
228
+ ```text
229
+ error @nxgt/typespec/query-method-not-post: list is marked @queryMethod and sent with GET: @queryMethod marks a @post, which @nxgt/typespec writes as a QUERY in OpenAPI 3.2. Make it a @post.
230
+ ```
231
+
232
+ A search that answers an array also meets the linter's `list-returns-page`;
233
+ return a `Page<Item>`, as `search` above could
234
+ (`Page<User> | BadRequest`).
178
235
 
179
236
  ## The resource
180
237
 
package/docs/roadmap.md CHANGED
@@ -16,16 +16,35 @@ Nothing in progress.
16
16
 
17
17
  ## Later
18
18
 
19
- - **`QUERY` for `search` and `query`.** The HTTP `QUERY` method, a safe
20
- request with a body, joins `GET` and `POST` as a method of `search` and
21
- `query` once `@typespec/http` declares it. `@nxgt/openapi-codegen`
22
- already reads an OpenAPI 3.2 `query` operation.
19
+ - **A `QUERY` without a mark.** A real `QUERY` is done: the emitter
20
+ `@nxgt/typespec` writes a `@queryMethod` as a `query` operation in OpenAPI
21
+ 3.2. What is left is a native `@query`-like verb, once `@typespec/http`
22
+ declares `QUERY`, so a spec stops needing `@post @queryMethod` and does not
23
+ otherwise change.
23
24
  - **`ETag` and conditional requests.** `ETag`, with `If-None-Match` and
24
25
  `If-Match`, once the `@nxgt/*` packages send one the same way. The
25
26
  optimistic lock is the body's `version` today (`Versioned`).
26
27
 
27
28
  ## Shipped
28
29
 
30
+ ### 0.13.0
31
+
32
+ - **The emitter `@nxgt/typespec`.** `emit: ["@nxgt/typespec"]` runs
33
+ `@typespec/openapi3` with the same options and files, and in an OpenAPI 3.2
34
+ document writes each `@queryMethod` as a real `query` operation, which
35
+ `@nxgt/openapi-codegen` generates as `method: 'query'` and
36
+ `@nxgt/openapi-hono` serves. A marked operation with an older version, or
37
+ with plain `@typespec/openapi3`, is an error. `@typespec/openapi3` is an optional peer, and the
38
+ `tsp init` template emits with the new emitter.
39
+
40
+ ### 0.12.0
41
+
42
+ - **`@queryMethod`, a `QUERY` sent as a `POST`.** The decorator marks a
43
+ `@post` with `x-nxgt-method: query`, which `@nxgt/openapi-codegen` reads,
44
+ and `search` and `query` accept it: a safe request with a body, with every
45
+ server, proxy and `fetch`. On anything but a `@post` it is the error
46
+ `query-method-not-post`.
47
+
29
48
  ### 0.11.0
30
49
 
31
50
  - **Request body aliases.** `CreateRequest<T>`, `UpdateRequest<T>` and
@@ -293,6 +293,104 @@ Or silence that one operation with a reason:
293
293
  @post @route("/find") findById(@body id: uuid): User;
294
294
  ```
295
295
 
296
+ ## `list is marked @queryMethod and sent with GET: @queryMethod marks a @post, which @nxgt/typespec writes as a QUERY in OpenAPI 3.2. Make it a @post.`
297
+
298
+ **When:** an error, `query-method-not-post`: an operation marked
299
+ `@queryMethod` is sent with a method other than `POST`. The method may be
300
+ implicit, `@get` or none.
301
+
302
+ ```tsp
303
+ @route("/users")
304
+ interface Users {
305
+ @get @queryMethod list(): string[];
306
+ }
307
+ ```
308
+
309
+ **Why:** `@queryMethod` stands for a `QUERY` that `@typespec/http` cannot
310
+ declare, so the spec writes a `@post` and the emitter writes the `QUERY`
311
+ ([The emitter](guide/emitter.md)); on a `GET`, a `PUT` or a `DELETE` it has
312
+ nothing to stand for.
313
+
314
+ **Fix:** make it a `@post`, with the criteria in the body:
315
+
316
+ ```tsp
317
+ @post @queryMethod @route("/search") search(@body names: string[]): string[];
318
+ ```
319
+
320
+ Or drop `@queryMethod`: a `GET` needs no mark. More, in
321
+ [Operation ids](guide/operation-ids.md#a-query-in-openapi-32).
322
+
323
+ ## `search is marked @queryMethod: only OpenAPI 3.2 has a QUERY operation, and openapi-versions asks for 3.1.0. Emit 3.2 only, or take off @queryMethod.`
324
+
325
+ **When:** `tsp compile` with the emitter `@nxgt/typespec`; the error
326
+ `query-method-needs-openapi-3.2` on each marked operation. Nothing is emitted.
327
+
328
+ **Why:** OpenAPI 3.1 and older have no `QUERY` operation, and the emitter
329
+ does not turn the operation into a `POST`. `openapi-versions` holds a version
330
+ below 3.2, or is unset, which means 3.0.0; the message names the first such
331
+ version.
332
+
333
+ **Fix:** emit 3.2 only, or take off `@queryMethod` and use a plain `@post`
334
+ or `@get`:
335
+
336
+ ```yaml
337
+ options:
338
+ '@nxgt/typespec':
339
+ openapi-versions: ['3.2.0']
340
+ ```
341
+
342
+ An `openapi-versions` with 3.2 beside an older version fails the same way:
343
+ list only 3.2. More, in [The emitter](guide/emitter.md).
344
+
345
+ ## `search is marked @queryMethod: emit with @nxgt/typespec, which writes it as a QUERY; @typespec/openapi3 would write a POST.`
346
+
347
+ **When:** `tsp compile` with `@typespec/openapi3` in `emit`; the error
348
+ `query-method-needs-nxgt-emitter` on each marked operation. Since 0.13.0; in
349
+ 0.12.0 this compiled to a `post` with `x-nxgt-method: query`.
350
+
351
+ **Why:** only the emitter `@nxgt/typespec` writes a `@queryMethod` as a
352
+ `QUERY`.
353
+
354
+ **Fix:** emit with it, and ask for 3.2:
355
+
356
+ ```yaml
357
+ emit:
358
+ - '@nxgt/typespec'
359
+ options:
360
+ '@nxgt/typespec':
361
+ openapi-versions: ['3.2.0']
362
+ ```
363
+
364
+ ## `The emitter @nxgt/typespec runs @typespec/openapi3, which is not installed: add it beside @nxgt/typespec.`
365
+
366
+ **When:** `tsp compile` with `@nxgt/typespec` in `emit`; the error
367
+ `openapi3-missing`. Nothing is emitted.
368
+
369
+ **Why:** `@typespec/openapi3` is an optional peer of `@nxgt/typespec`, loaded
370
+ only when the spec is emitted, so installing the library does not install it.
371
+
372
+ **Fix:**
373
+
374
+ ```sh
375
+ bun add -d @typespec/openapi3
376
+ ```
377
+
378
+ ## `findById is sent with QUERY, where its verb find is sent with GET or HEAD`
379
+
380
+ **When:** a warning, `verb-method-mismatch`: an operation named after one of
381
+ the library's verbs other than `search` and `query` is marked `@queryMethod`.
382
+
383
+ **Why:** a marked `POST` is checked as the `QUERY` it is, and only `search`
384
+ and `query` are sent with `QUERY`.
385
+
386
+ **Fix:** name it `search` or `query`, a name that is not a verb, or take off
387
+ `@queryMethod` and give the verb its own method, as in the entry on
388
+ [`findById is sent with POST`](#findbyid-is-sent-with-post-where-its-verb-find-is-sent-with-get-or-head).
389
+
390
+ ```tsp
391
+ @post @queryMethod @route("/search") searchByName(@body names: string[]): User[]; // searchUserByName
392
+ ```
393
+
296
394
  ## `declares a reply without a body and one with a body of status …`
297
395
 
298
396
  **When:** an operation declares, for one status, a reply without a body and
@@ -600,7 +698,9 @@ using OpenAPI;
600
698
 
601
699
  **When:** you generate from what `tsp compile` emitted.
602
700
 
603
- **Why:** `@typespec/openapi3` emits OpenAPI 3.0 by default.
701
+ **Why:** `@typespec/openapi3`, and so `@nxgt/typespec` which runs it, emits
702
+ OpenAPI 3.0 by default.
604
703
 
605
704
  **Fix:** in `tspconfig.yaml`, set `openapi-versions: ['3.1.0']` or
606
- `['3.2.0']` under the `@typespec/openapi3` options.
705
+ `['3.2.0']` under the options of the emitter you use, `@nxgt/typespec`
706
+ or `@typespec/openapi3`.
package/lib/main.tsp CHANGED
@@ -10,3 +10,4 @@ import "./columns.tsp";
10
10
  import "./headers.tsp";
11
11
  import "./sorting.tsp";
12
12
  import "./requests.tsp";
13
+ import "./query-method.tsp";
@@ -0,0 +1,26 @@
1
+ import "@typespec/openapi";
2
+
3
+ using TypeSpec.Reflection;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * Marks a `POST` as a `QUERY`: a safe request, which changes nothing on the
9
+ * server, with its criteria in the body. It is sent as a `POST`, which every
10
+ * server, proxy and `fetch` accepts, and the OpenAPI document says what it is
11
+ * with `x-nxgt-method: query`: `@nxgt/openapi-codegen` reads it. Once
12
+ * `@typespec/http` declares `QUERY`, this is the decorator that changes, not
13
+ * the spec. Only on a `POST`: anything else is the error
14
+ * `query-method-not-post`. `@operationIds` checks its verb as a `QUERY`'s:
15
+ * `search` and `query` take one.
16
+ *
17
+ * @example
18
+ * ```typespec
19
+ * @route("/users")
20
+ * interface Users {
21
+ * @post @queryMethod @route("/search")
22
+ * search(@body criteria: UserCriteria): User[]; // searchUsers
23
+ * }
24
+ * ```
25
+ */
26
+ extern dec queryMethod(target: Operation);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/typespec",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,20 +45,29 @@
45
45
  "src/index.ts"
46
46
  ]
47
47
  },
48
+ "dependencies": {
49
+ "yaml": "^2.9.0"
50
+ },
48
51
  "peerDependencies": {
49
52
  "@typespec/compiler": "^1.16.0",
50
53
  "@typespec/http": "^1.16.0",
51
54
  "@typespec/openapi": "^1.16.0",
52
- "typescript": "^6.0.3 || ^7.0.0"
55
+ "typescript": "^6.0.3 || ^7.0.0",
56
+ "@typespec/openapi3": "^1.16.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "@typespec/openapi3": {
60
+ "optional": true
61
+ }
53
62
  },
54
63
  "devDependencies": {
55
- "@nxgt/openapi-codegen": "^0.8.0",
64
+ "@nxgt/openapi-codegen": "^0.9.1",
56
65
  "@nxgt/openapi-hono": "^0.3.2",
57
66
  "@types/bun": "^1.4.0",
58
- "@typespec/compiler": "1.16.0",
59
- "@typespec/http": "1.16.0",
60
- "@typespec/openapi": "1.16.0",
61
- "@typespec/openapi3": "1.16.0",
67
+ "@typespec/compiler": "1.17.0",
68
+ "@typespec/http": "1.17.0",
69
+ "@typespec/openapi": "1.17.0",
70
+ "@typespec/openapi3": "1.17.0",
62
71
  "hono": "^4.13.4",
63
72
  "zod": "^4.5.4"
64
73
  }
@@ -6,6 +6,7 @@
6
6
  "libraries": [
7
7
  "@typespec/http",
8
8
  "@typespec/openapi",
9
+ "@typespec/openapi3",
9
10
  "@nxgt/typespec",
10
11
  "@nxgt/openapi-codegen"
11
12
  ],
@@ -15,9 +16,9 @@
15
16
  }
16
17
  },
17
18
  "emitters": {
18
- "@typespec/openapi3": {
19
+ "@nxgt/typespec": {
19
20
  "selected": true,
20
- "label": "OpenAPI 3.1 document",
21
+ "label": "OpenAPI 3.1 document (@typespec/openapi3; in 3.2, a @queryMethod is a QUERY)",
21
22
  "options": {
22
23
  "emitter-output-dir": "{project-root}/openapi",
23
24
  "openapi-versions": ["3.1.0"],