@nxgt/typespec 0.10.0 → 0.12.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.
package/README.md CHANGED
@@ -125,7 +125,7 @@ namespace Blog;
125
125
  @route("/posts")
126
126
  interface Posts {
127
127
  @get getPost(@path postId: string): Post | NotFound | Unauthorized;
128
- @post createPost(@body post: Create<Post>): {
128
+ @post createPost(@body post: CreateRequest<Post>): {
129
129
  @statusCode _: 201;
130
130
  @body post: Post;
131
131
  } | BadRequest | Conflict | ErrorResponse<503>;
@@ -175,8 +175,9 @@ an alias leaves out:
175
175
  interface Users {
176
176
  @get list(...PageParameters): Page<User> | ListErrors;
177
177
  @get get(@path id: uuid): User | GetErrors;
178
- @post create(@body user: User): User | CreateErrors | AuthErrors | TooManyRequests;
179
- @patch update(@path id: uuid, @body user: User): User | UpdateErrors;
178
+ @post create(@body user: CreateRequest<User>): User | CreateErrors | AuthErrors | TooManyRequests;
179
+ @put update(@path id: uuid, @body user: UpdateRequest<User>): User | UpdateErrors;
180
+ @patch patch(@path id: uuid, @body user: PatchRequest<User>): User | UpdateErrors;
180
181
  @delete delete(@path id: uuid): NoContentResponse | DeleteErrors;
181
182
  }
182
183
  ```
@@ -184,6 +185,26 @@ interface Users {
184
185
  An alias is exactly its union, so the OpenAPI it emits is the same as
185
186
  writing the responses out.
186
187
 
188
+ ### Request bodies
189
+
190
+ `CreateRequest<T>`, `UpdateRequest<T>` and `PatchRequest<T>` are TypeSpec's own
191
+ `Create`, `Update` and `MergePatchUpdate`, named so the generated types and
192
+ schemas read `CreatePostRequest`, `UpdatePostRequest` and `PatchPostRequest`:
193
+
194
+ ```tsp
195
+ @post createPost(@body post: CreateRequest<Post>): {
196
+ @statusCode _: 201;
197
+ @body post: Post;
198
+ } | BadRequest;
199
+ @put updatePost(@path postId: uuid, @body post: UpdateRequest<Post>): Post | BadRequest | NotFound;
200
+ @patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>): Post | BadRequest | NotFound;
201
+ ```
202
+
203
+ A `PUT` body keeps each property as optional as the model declares it; a
204
+ `PATCH` body is a JSON Merge Patch, `application/merge-patch+json`, with
205
+ every property optional. `Create<Post, "New{name}">` names a body your own
206
+ way. See [Request bodies](docs/guide/requests.md).
207
+
187
208
  ### Pagination
188
209
 
189
210
  Spread the query of a page into the operation's parameters, and answer the
@@ -293,9 +314,9 @@ model Post {
293
314
  | --- | --- | --- | --- |
294
315
  | `uuid` | the schema `UUID`, `format: uuid`, a pattern for versions 1–8 | `UUID`, `z.guid().regex(…)` | `id()` |
295
316
  | `email` | `emailAddress`, the schema `EmailAddress`, `format: email` | `EmailAddress`, `z.string().regex(…)` | |
296
- | `...Timestamps` | `createdAt`, `updatedAt`, read-only | left out of `Create<T>` and updates | `timestamps()` |
317
+ | `...Timestamps` | `createdAt`, `updatedAt`, read-only | left out of `CreateRequest<T>` and updates | `timestamps()` |
297
318
  | `...SoftDelete` | `deletedAt: utcDateTime \| null`, read-only | | `softDelete()` |
298
- | `...Versioned` | `version: integer`, read and update | kept in `MergePatchUpdate<T>` | `version()` |
319
+ | `...Versioned` | `version: integer`, read and update | kept in `UpdateRequest<T>` and `PatchRequest<T>` | `version()` |
299
320
  | `...Actors<Id = uuid>` | `createdBy`, `updatedBy`, `deletedBy`, `Id \| null`, read-only | | `actors()` |
300
321
 
301
322
  Every scalar of [`@nxgt/graphql-scalars`](https://www.npmjs.com/package/@nxgt/graphql-scalars)
@@ -326,7 +347,7 @@ Spread the headers of an idempotent write and of a rate limit into an
326
347
  operation and its replies:
327
348
 
328
349
  ```tsp
329
- @post createPost(...IdempotencyKeyHeader, @body post: Create<Post>): {
350
+ @post createPost(...IdempotencyKeyHeader, @body post: CreateRequest<Post>): {
330
351
  @statusCode _: 201;
331
352
  ...IdempotentReplayedHeader;
332
353
  ...RateLimitHeaders;
@@ -435,6 +456,29 @@ The verbs `verbs` adds are not checked, nor an operation with its own
435
456
  `@operationId`. More, with each verb's methods, in
436
457
  [Operation ids](docs/guide/operation-ids.md).
437
458
 
459
+ ### A search with a body
460
+
461
+ `QUERY` is the HTTP method for a safe request with a body, and
462
+ `@typespec/http` 1.17 declares none. Mark a `@post` with `@queryMethod`: it is
463
+ still sent as a `POST`, which every server, proxy, `fetch` and CORS policy
464
+ accepts, and the document says what it is with `x-nxgt-method: query`, which
465
+ `@nxgt/openapi-codegen` reads:
466
+
467
+ ```tsp
468
+ @route("/authors")
469
+ interface Authors {
470
+ @post @queryMethod @route("/search")
471
+ search(@body criteria: AuthorCriteria): Page<Author> | BadRequest; // searchAuthors
472
+ }
473
+ ```
474
+
475
+ `search` and `query` accept a `GET`, a `POST` or a marked `POST` without the
476
+ `verb-method-mismatch` warning; `findById` or `create` marked warn, as a
477
+ `QUERY` is not their method. The decorator on anything but a `@post` is the
478
+ error `query-method-not-post`. When `@typespec/http` declares `QUERY`, the
479
+ decorator is what changes, not your spec. More, in
480
+ [Operation ids](docs/guide/operation-ids.md#a-query-sent-as-a-post).
481
+
438
482
  ### Linter
439
483
 
440
484
  Extend the library's ruleset in `tspconfig.yaml`, and `tsp compile` warns
@@ -469,12 +513,15 @@ passing example of each rule, in [Linter](docs/guide/linter.md).
469
513
 
470
514
  | Decorator | On | What it does |
471
515
  | --- | --- | --- |
516
+ | `@queryMethod` | an operation | marks a `@post` as a `QUERY`: it stays a `POST` on the wire and the document gets `x-nxgt-method: query`; `@operationIds` checks its verb as a `QUERY`'s |
472
517
  | `@operationIds(options?: OperationIdsOptions)` | a namespace or an interface | makes each operation's id its name, as written, or a known verb with the interface's resource (`list` in `Users` is `listUsers`), in the namespace however deep or in the interface, unless it has an `@operationId` |
473
518
 
474
519
  `OperationIdsOptions` is `#{ singular?: string, plural?: string, verbs?: Record<"singular" | "plural"> }`:
475
520
  `singular` and `plural` name an interface's resource, and `verbs` adds or
476
521
  overrides verbs, on a namespace or an interface.
477
522
 
523
+ `@queryMethod` has no options.
524
+
478
525
  ### Diagnostics
479
526
 
480
527
  | Code | Reported when |
@@ -483,7 +530,8 @@ overrides verbs, on a namespace or an interface.
483
530
  | `resource-name-on-namespace` | error: `@operationIds` on a namespace is given `singular` or `plural`, which name an interface's resource |
484
531
  | `duplicate-status-reply` | error: an operation declares a reply without a body and one with a body of one status, which the emitter merges, losing the one without a body |
485
532
  | `merged-status-reply` | warning: an operation declares two replies with a body of one status code, which the emitter merges under the first one's description |
486
- | `verb-method-mismatch` | warning: an operation `@operationIds` names after one of the library's verbs, in an interface, is sent with a method that verb does not name: `create` with a `GET` |
533
+ | `verb-method-mismatch` | warning: an operation `@operationIds` names after one of the library's verbs, in an interface, is sent with a method that verb does not name: `create` with a `GET`, or marked `@queryMethod` |
534
+ | `query-method-not-post` | error: an operation marked `@queryMethod` is not a `@post` |
487
535
 
488
536
  ### Linter rules
489
537
 
@@ -507,6 +555,9 @@ In the ruleset `@nxgt/typespec/recommended`; each is a warning, and named
507
555
  | `Page<Item>` | `{ items: Item[], total, page, pageSize, pageCount }`, the schema `<Item>Page` |
508
556
  | `CursorPageParameters` | the query `after` and `limit` |
509
557
  | `CursorPage<Item>` | `{ items: Item[], nextCursor: string \| null }`, the schema `<Item>CursorPage` |
558
+ | `CreateRequest<T>` | `Create<T, "Create{name}Request">`: the body of a create, `CreatePostRequest` |
559
+ | `UpdateRequest<T>` | `Update<T, "Update{name}Request">`: the body of a `PUT`, `UpdatePostRequest` |
560
+ | `PatchRequest<T>` | `MergePatchUpdate<T, "Patch{name}Request">`: a merge patch, all optional, `PatchPostRequest` |
510
561
  | `ValidationIssue` | `{ target: ValidationTarget, path: (string \| integer)[], code: string, message: string }` |
511
562
  | `ValidationTarget` | `"param" \| "query" \| "header" \| "json" \| "form" \| "body" \| "response"` |
512
563
 
@@ -653,9 +704,11 @@ envelope and the rate limit's headers.
653
704
  `Retry-After`;
654
705
  - [Sorting](docs/guide/sorting.md): `orderBy` and `direction` beside a
655
706
  list's filters, and the `@nxgt/drizzle` call they map to;
707
+ - [Request bodies](docs/guide/requests.md): `CreateRequest`, `UpdateRequest`
708
+ and `PatchRequest`, and naming your own;
656
709
  - [Operation ids](docs/guide/operation-ids.md): `@operationIds`, each
657
710
  operation named as written or a verb with its resource, the method each
658
- verb is sent with, the options, and the ids it refuses;
711
+ verb is sent with, `@queryMethod`, the options, and the ids it refuses;
659
712
  - [Linter](docs/guide/linter.md): the ruleset `@nxgt/typespec/recommended`,
660
713
  each rule with a failing and a passing spec, and how to turn one off;
661
714
  - [troubleshooting](docs/troubleshooting.md);
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Each HTTP operation of each service, once, as the emitter sends it: a
3
+ * service nested in another lists its operations in both. The services' own
4
+ * diagnostics are the emitter's to report.
5
+ */
6
+ import type { Program } from '@typespec/compiler';
7
+ import { type HttpOperation } from '@typespec/http';
8
+ export declare function eachHttpOperation(program: Program, visit: (operation: HttpOperation) => void): void;
9
+ //# sourceMappingURL=http-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-operations.d.ts","sourceRoot":"","sources":["../src/http-operations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAExE,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,GACvC,IAAI,CAUN"}
package/dist/index.d.ts CHANGED
@@ -8,12 +8,14 @@
8
8
  */
9
9
  import type { Program } from '@typespec/compiler';
10
10
  import { operationIds } from './operation-ids';
11
+ import { queryMethod } from './query-method';
11
12
  export { $lib } from './lib';
12
13
  export { $linter } from './linter';
13
14
  export declare function $onValidate(program: Program): void;
14
15
  export declare const $decorators: {
15
16
  Nxgt: {
16
17
  operationIds: typeof operationIds;
18
+ queryMethod: typeof queryMethod;
17
19
  };
18
20
  };
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAC;AAGrE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAMlD;AAED,eAAO,MAAM,WAAW;;;;CAEvB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAwB,MAAM,gBAAgB,CAAC;AAGnE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAOlD;AAED,eAAO,MAAM,WAAW;;;;;CAEvB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,20 @@
1
1
  // src/one-reply-per-status.ts
2
2
  import { isVoidType } from "@typespec/compiler";
3
- import {
4
- getAllHttpServices
5
- } from "@typespec/http";
3
+
4
+ // src/http-operations.ts
5
+ import { getAllHttpServices } from "@typespec/http";
6
+ function eachHttpOperation(program, visit) {
7
+ const [services] = getAllHttpServices(program);
8
+ const visited = new Set;
9
+ for (const service of services) {
10
+ for (const operation of service.operations) {
11
+ if (visited.has(operation.operation))
12
+ continue;
13
+ visited.add(operation.operation);
14
+ visit(operation);
15
+ }
16
+ }
17
+ }
6
18
 
7
19
  // src/lib.ts
8
20
  import { createTypeSpecLibrary, paramMessage } from "@typespec/compiler";
@@ -27,6 +39,12 @@ var $lib = createTypeSpecLibrary({
27
39
  default: paramMessage`${"operation"} is sent with ${"method"}, where its verb ${"verb"} is sent with ${"expected"}. Give it that method, or a name that is not a verb.`
28
40
  }
29
41
  },
42
+ "query-method-not-post": {
43
+ severity: "error",
44
+ messages: {
45
+ default: paramMessage`${"operation"} is marked @queryMethod and sent with ${"method"}: a QUERY is sent as a POST until @typespec/http declares it. Make it a @post.`
46
+ }
47
+ },
30
48
  "duplicate-status-reply": {
31
49
  severity: "error",
32
50
  messages: {
@@ -41,6 +59,9 @@ var $lib = createTypeSpecLibrary({
41
59
  }
42
60
  },
43
61
  state: {
62
+ queryMethod: {
63
+ description: "The operations marked with @queryMethod"
64
+ },
44
65
  operationIds: {
45
66
  description: "The interfaces and namespaces marked with @operationIds"
46
67
  },
@@ -75,28 +96,21 @@ function mergeOf({
75
96
  return;
76
97
  }
77
98
  function validateOneReplyPerStatus(program) {
78
- const [services] = getAllHttpServices(program);
79
- const checked = new Set;
80
- for (const service of services) {
81
- for (const operation of service.operations) {
82
- if (checked.has(operation.operation))
99
+ eachHttpOperation(program, (operation) => {
100
+ for (const response of operation.responses) {
101
+ const code = mergeOf(response);
102
+ if (code === undefined)
83
103
  continue;
84
- checked.add(operation.operation);
85
- for (const response of operation.responses) {
86
- const code = mergeOf(response);
87
- if (code === undefined)
88
- continue;
89
- $lib.reportDiagnostic(program, {
90
- code,
91
- format: {
92
- operation: operation.operation.name,
93
- status: statusOf(response.statusCodes)
94
- },
95
- target: operation.operation
96
- });
97
- }
104
+ $lib.reportDiagnostic(program, {
105
+ code,
106
+ format: {
107
+ operation: operation.operation.name,
108
+ status: statusOf(response.statusCodes)
109
+ },
110
+ target: operation.operation
111
+ });
98
112
  }
99
- }
113
+ });
100
114
  }
101
115
 
102
116
  // src/operation-ids.ts
@@ -137,8 +151,8 @@ var METHODS = {
137
151
  find: ["get", "head"],
138
152
  count: ["get", "head"],
139
153
  get: ["get", "head"],
140
- search: ["get", "post"],
141
- query: ["get", "post"],
154
+ search: ["get", "post", "query"],
155
+ query: ["get", "post", "query"],
142
156
  create: ["post"],
143
157
  createMany: ["post"],
144
158
  update: ["put", "patch"],
@@ -314,42 +328,57 @@ function validateOperationIds(program) {
314
328
  });
315
329
  }
316
330
 
331
+ // src/query-method.ts
332
+ import { setExtension } from "@typespec/openapi";
333
+ function queryMethod(context, target) {
334
+ const { program } = context;
335
+ program.stateSet($lib.stateKeys.queryMethod).add(target);
336
+ setExtension(program, target, "x-nxgt-method", "query");
337
+ }
338
+ function isQueryMethod(program, operation) {
339
+ return program.stateSet($lib.stateKeys.queryMethod).has(operation);
340
+ }
341
+ function validateQueryMethods(program) {
342
+ eachHttpOperation(program, ({ operation, verb: method }) => {
343
+ if (!isQueryMethod(program, operation) || method === "post")
344
+ return;
345
+ $lib.reportDiagnostic(program, {
346
+ code: "query-method-not-post",
347
+ format: { operation: operation.name, method: method.toUpperCase() },
348
+ target: operation
349
+ });
350
+ });
351
+ }
352
+
317
353
  // src/verb-methods.ts
318
- import { getAllHttpServices as getAllHttpServices2 } from "@typespec/http";
319
354
  import { getOperationId as getOperationId2 } from "@typespec/openapi";
320
355
  function methodsOf(methods) {
321
356
  return methods.map((method) => method.toUpperCase()).join(" or ");
322
357
  }
323
358
  function validateVerbMethods(program) {
324
- const [services] = getAllHttpServices2(program);
325
- const checked = new Set;
326
- for (const service of services) {
327
- for (const { operation, verb: method } of service.operations) {
328
- if (checked.has(operation))
329
- continue;
330
- checked.add(operation);
331
- if (!isNamed(program, operation))
332
- continue;
333
- if (getOperationId2(program, operation) !== undefined)
334
- continue;
335
- const verb = verbOf(program, operation);
336
- if (verb === undefined || !Object.hasOwn(METHODS, verb))
337
- continue;
338
- const expected = METHODS[verb];
339
- if (expected.includes(method))
340
- continue;
341
- $lib.reportDiagnostic(program, {
342
- code: "verb-method-mismatch",
343
- format: {
344
- operation: operation.name,
345
- method: method.toUpperCase(),
346
- verb,
347
- expected: methodsOf(expected)
348
- },
349
- target: operation
350
- });
351
- }
352
- }
359
+ eachHttpOperation(program, ({ operation, verb: sent }) => {
360
+ if (!isNamed(program, operation))
361
+ return;
362
+ if (getOperationId2(program, operation) !== undefined)
363
+ return;
364
+ const verb = verbOf(program, operation);
365
+ if (verb === undefined || !Object.hasOwn(METHODS, verb))
366
+ return;
367
+ const method = sent === "post" && isQueryMethod(program, operation) ? "query" : sent;
368
+ const expected = METHODS[verb];
369
+ if (expected.includes(method))
370
+ return;
371
+ $lib.reportDiagnostic(program, {
372
+ code: "verb-method-mismatch",
373
+ format: {
374
+ operation: operation.name,
375
+ method: method.toUpperCase(),
376
+ verb,
377
+ expected: methodsOf(expected)
378
+ },
379
+ target: operation
380
+ });
381
+ });
353
382
  }
354
383
  // src/linter.ts
355
384
  import { defineLinter } from "@typespec/compiler";
@@ -475,12 +504,13 @@ var $linter = defineLinter({
475
504
 
476
505
  // src/index.ts
477
506
  function $onValidate(program) {
507
+ validateQueryMethods(program);
478
508
  validateVerbMethods(program);
479
509
  validateOperationIds(program);
480
510
  validateOneReplyPerStatus(program);
481
511
  }
482
512
  var $decorators = {
483
- Nxgt: { operationIds }
513
+ Nxgt: { operationIds, queryMethod }
484
514
  };
485
515
  export {
486
516
  $decorators,
@@ -489,5 +519,5 @@ export {
489
519
  $onValidate
490
520
  };
491
521
 
492
- //# debugId=FB766460AB7EEFAD64756E2164756E21
522
+ //# debugId=B949BF3E72439B1464756E2164756E21
493
523
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,21 +1,23 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/one-reply-per-status.ts", "../src/lib.ts", "../src/operation-ids.ts", "../src/verbs.ts", "../src/resource.ts", "../src/verb-methods.ts", "../src/linter.ts", "../src/rules/error-body-shape.ts", "../src/rules/written.ts", "../src/rules/list-returns-page.ts", "../src/rules/service-operation-ids.ts", "../src/index.ts"],
3
+ "sources": ["../src/one-reply-per-status.ts", "../src/http-operations.ts", "../src/lib.ts", "../src/operation-ids.ts", "../src/verbs.ts", "../src/resource.ts", "../src/query-method.ts", "../src/verb-methods.ts", "../src/linter.ts", "../src/rules/error-body-shape.ts", "../src/rules/written.ts", "../src/rules/list-returns-page.ts", "../src/rules/service-operation-ids.ts", "../src/index.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * One reply per status: `@typespec/openapi3` merges two replies of one\n * status into one without a word. A reply without a body beside one with a\n * body is lost in the merge, an error; a `void` body is none, as the emitter\n * sees it. Two bodies of one content type are merged under the first reply's\n * description, a warning where the status is a code. Under `*` or a range,\n * where undocumented `@error` models share one description, they pass.\n * Bodies of different content types are one reply, negotiated, and pass.\n */\nimport { isVoidType, type Operation, type Program } from '@typespec/compiler';\nimport {\n\tgetAllHttpServices,\n\ttype HttpOperationResponse,\n\ttype HttpStatusCodesEntry,\n} from '@typespec/http';\nimport { $lib } from './lib';\n\nfunction statusOf(code: HttpStatusCodesEntry): string {\n\treturn typeof code === 'object' ? `${code.start}-${code.end}` : `${code}`;\n}\n\n/** What the emitter's merge costs: a reply, a description, or nothing. */\nfunction mergeOf({\n\tstatusCodes,\n\tresponses,\n}: HttpOperationResponse):\n\t| 'duplicate-status-reply'\n\t| 'merged-status-reply'\n\t| undefined {\n\tconst bodies = responses.flatMap(({ body }) =>\n\t\tbody && !isVoidType(body.type) ? [body] : [],\n\t);\n\tif (bodies.length > 0 && bodies.length < responses.length) {\n\t\treturn 'duplicate-status-reply';\n\t}\n\tif (typeof statusCodes !== 'number') return undefined;\n\tconst seen = new Set<string>();\n\tfor (const body of bodies) {\n\t\tif (body.contentTypes.some((type) => seen.has(type))) {\n\t\t\treturn 'merged-status-reply';\n\t\t}\n\t\tfor (const type of body.contentTypes) seen.add(type);\n\t}\n\treturn undefined;\n}\n\n/**\n * Checks each operation of each service, as the emitter sees it, once: a\n * service nested in another is listed in both. The services' own\n * diagnostics are the emitter's to report.\n */\nexport function validateOneReplyPerStatus(program: Program): void {\n\tconst [services] = getAllHttpServices(program);\n\tconst checked = new Set<Operation>();\n\tfor (const service of services) {\n\t\tfor (const operation of service.operations) {\n\t\t\tif (checked.has(operation.operation)) continue;\n\t\t\tchecked.add(operation.operation);\n\t\t\tfor (const response of operation.responses) {\n\t\t\t\tconst code = mergeOf(response);\n\t\t\t\tif (code === undefined) continue;\n\t\t\t\t$lib.reportDiagnostic(program, {\n\t\t\t\t\tcode,\n\t\t\t\t\tformat: {\n\t\t\t\t\t\toperation: operation.operation.name,\n\t\t\t\t\t\tstatus: statusOf(response.statusCodes),\n\t\t\t\t\t},\n\t\t\t\t\ttarget: operation.operation,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n",
6
- "/** The library's definition: its name, diagnostics and state keys. */\nimport { createTypeSpecLibrary, paramMessage } from '@typespec/compiler';\n\nexport const $lib = createTypeSpecLibrary({\n\tname: '@nxgt/typespec',\n\tdiagnostics: {\n\t\t'duplicate-operation-id': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`Two operations are named ${'id'}: an OpenAPI operation id must be unique.`,\n\t\t\t},\n\t\t},\n\t\t'resource-name-on-namespace': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault:\n\t\t\t\t\t\"`singular` and `plural` name an interface's resource: a namespace has none. Put them on the interface.\",\n\t\t\t},\n\t\t},\n\t\t'verb-method-mismatch': {\n\t\t\tseverity: 'warning',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} is sent with ${'method'}, where its verb ${'verb'} is sent with ${'expected'}. Give it that method, or a name that is not a verb.`,\n\t\t\t},\n\t\t},\n\t\t'duplicate-status-reply': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} declares a reply without a body and one with a body of status ${'status'}: the emitter merges them into one, and the reply without a body is lost. Declare the one the route sends.`,\n\t\t\t},\n\t\t},\n\t\t'merged-status-reply': {\n\t\t\tseverity: 'warning',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} declares two replies with a body of status ${'status'}: the emitter merges their bodies under the first one's description. Declare the one the route sends.`,\n\t\t\t},\n\t\t},\n\t},\n\tstate: {\n\t\toperationIds: {\n\t\t\tdescription: 'The interfaces and namespaces marked with @operationIds',\n\t\t},\n\t\toperationIdsOptions: {\n\t\t\tdescription:\n\t\t\t\t\"Each marked interface's or namespace's @operationIds options\",\n\t\t},\n\t},\n});\n",
5
+ "/**\n * One reply per status: `@typespec/openapi3` merges two replies of one\n * status into one without a word. A reply without a body beside one with a\n * body is lost in the merge, an error; a `void` body is none, as the emitter\n * sees it. Two bodies of one content type are merged under the first reply's\n * description, a warning where the status is a code. Under `*` or a range,\n * where undocumented `@error` models share one description, they pass.\n * Bodies of different content types are one reply, negotiated, and pass.\n */\nimport { isVoidType, type Program } from '@typespec/compiler';\nimport type {\n\tHttpOperationResponse,\n\tHttpStatusCodesEntry,\n} from '@typespec/http';\nimport { eachHttpOperation } from './http-operations';\nimport { $lib } from './lib';\n\nfunction statusOf(code: HttpStatusCodesEntry): string {\n\treturn typeof code === 'object' ? `${code.start}-${code.end}` : `${code}`;\n}\n\n/** What the emitter's merge costs: a reply, a description, or nothing. */\nfunction mergeOf({\n\tstatusCodes,\n\tresponses,\n}: HttpOperationResponse):\n\t| 'duplicate-status-reply'\n\t| 'merged-status-reply'\n\t| undefined {\n\tconst bodies = responses.flatMap(({ body }) =>\n\t\tbody && !isVoidType(body.type) ? [body] : [],\n\t);\n\tif (bodies.length > 0 && bodies.length < responses.length) {\n\t\treturn 'duplicate-status-reply';\n\t}\n\tif (typeof statusCodes !== 'number') return undefined;\n\tconst seen = new Set<string>();\n\tfor (const body of bodies) {\n\t\tif (body.contentTypes.some((type) => seen.has(type))) {\n\t\t\treturn 'merged-status-reply';\n\t\t}\n\t\tfor (const type of body.contentTypes) seen.add(type);\n\t}\n\treturn undefined;\n}\n\n/** Checks each operation of each service, as the emitter sees it, once. */\nexport function validateOneReplyPerStatus(program: Program): void {\n\teachHttpOperation(program, (operation) => {\n\t\tfor (const response of operation.responses) {\n\t\t\tconst code = mergeOf(response);\n\t\t\tif (code === undefined) continue;\n\t\t\t$lib.reportDiagnostic(program, {\n\t\t\t\tcode,\n\t\t\t\tformat: {\n\t\t\t\t\toperation: operation.operation.name,\n\t\t\t\t\tstatus: statusOf(response.statusCodes),\n\t\t\t\t},\n\t\t\t\ttarget: operation.operation,\n\t\t\t});\n\t\t}\n\t});\n}\n",
6
+ "/**\n * Each HTTP operation of each service, once, as the emitter sends it: a\n * service nested in another lists its operations in both. The services' own\n * diagnostics are the emitter's to report.\n */\nimport type { Program } from '@typespec/compiler';\nimport { getAllHttpServices, type HttpOperation } from '@typespec/http';\n\nexport function eachHttpOperation(\n\tprogram: Program,\n\tvisit: (operation: HttpOperation) => void,\n): void {\n\tconst [services] = getAllHttpServices(program);\n\tconst visited = new Set<HttpOperation['operation']>();\n\tfor (const service of services) {\n\t\tfor (const operation of service.operations) {\n\t\t\tif (visited.has(operation.operation)) continue;\n\t\t\tvisited.add(operation.operation);\n\t\t\tvisit(operation);\n\t\t}\n\t}\n}\n",
7
+ "/** The library's definition: its name, diagnostics and state keys. */\nimport { createTypeSpecLibrary, paramMessage } from '@typespec/compiler';\n\nexport const $lib = createTypeSpecLibrary({\n\tname: '@nxgt/typespec',\n\tdiagnostics: {\n\t\t'duplicate-operation-id': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`Two operations are named ${'id'}: an OpenAPI operation id must be unique.`,\n\t\t\t},\n\t\t},\n\t\t'resource-name-on-namespace': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault:\n\t\t\t\t\t\"`singular` and `plural` name an interface's resource: a namespace has none. Put them on the interface.\",\n\t\t\t},\n\t\t},\n\t\t'verb-method-mismatch': {\n\t\t\tseverity: 'warning',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} is sent with ${'method'}, where its verb ${'verb'} is sent with ${'expected'}. Give it that method, or a name that is not a verb.`,\n\t\t\t},\n\t\t},\n\t\t'query-method-not-post': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} is marked @queryMethod and sent with ${'method'}: a QUERY is sent as a POST until @typespec/http declares it. Make it a @post.`,\n\t\t\t},\n\t\t},\n\t\t'duplicate-status-reply': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} declares a reply without a body and one with a body of status ${'status'}: the emitter merges them into one, and the reply without a body is lost. Declare the one the route sends.`,\n\t\t\t},\n\t\t},\n\t\t'merged-status-reply': {\n\t\t\tseverity: 'warning',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} declares two replies with a body of status ${'status'}: the emitter merges their bodies under the first one's description. Declare the one the route sends.`,\n\t\t\t},\n\t\t},\n\t},\n\tstate: {\n\t\tqueryMethod: {\n\t\t\tdescription: 'The operations marked with @queryMethod',\n\t\t},\n\t\toperationIds: {\n\t\t\tdescription: 'The interfaces and namespaces marked with @operationIds',\n\t\t},\n\t\toperationIdsOptions: {\n\t\t\tdescription:\n\t\t\t\t\"Each marked interface's or namespace's @operationIds options\",\n\t\t},\n\t},\n});\n",
7
8
  "/**\n * `@operationIds`: the id of each operation is its name, as written:\n * `@get findPostComments()` is `findPostComments`, where `@typespec/openapi3`\n * would write `Posts_findPostComments`. In an interface, a known verb takes\n * the interface's resource: `create` in `Users` is `createUser` (`verbs.ts`).\n */\nimport {\n\ttype DecoratorContext,\n\ttype Interface,\n\tisService,\n\tisTemplateInstance,\n\tlistServices,\n\ttype Namespace,\n\tnavigateProgram,\n\ttype Operation,\n\ttype Program,\n} from '@typespec/compiler';\nimport {\n\tgetOperationId,\n\tresolveOperationId,\n\tsetOperationId,\n} from '@typespec/openapi';\nimport { $lib } from './lib';\nimport { idOf, type OperationIdsOptions } from './resource';\n\n/**\n * Marks the interface or the namespace; its ids are set once the program is\n * checked. On a template, TypeSpec runs it on each instance, which\n * `isMarkedInterface` follows to the interfaces extending it. A namespace\n * has no resource to name: `singular` and `plural` are an interface's.\n */\nexport function operationIds(\n\tcontext: DecoratorContext,\n\ttarget: Interface | Namespace,\n\toptions: OperationIdsOptions = {},\n): void {\n\tconst { program } = context;\n\tprogram.stateSet($lib.stateKeys.operationIds).add(target);\n\tprogram.stateMap($lib.stateKeys.operationIdsOptions).set(target, options);\n\tif (\n\t\ttarget.kind === 'Namespace' &&\n\t\t(options.singular !== undefined || options.plural !== undefined)\n\t) {\n\t\t$lib.reportDiagnostic(program, {\n\t\t\tcode: 'resource-name-on-namespace',\n\t\t\ttarget: context.decoratorTarget,\n\t\t});\n\t}\n}\n\n/** Marked itself, or in a marked namespace, however deep. */\nexport function isMarkedNamespace(\n\tprogram: Program,\n\ttarget: Namespace | undefined,\n): boolean {\n\tfor (let at = target; at !== undefined; at = at.namespace) {\n\t\tif (program.stateSet($lib.stateKeys.operationIds).has(at)) return true;\n\t}\n\treturn false;\n}\n\n/** Marked itself, or extending a marked interface: an instance of a marked template included. */\nfunction isMarkedInterface(program: Program, target: Interface): boolean {\n\treturn (\n\t\tprogram.stateSet($lib.stateKeys.operationIds).has(target) ||\n\t\ttarget.sourceInterfaces.some((source) => isMarkedInterface(program, source))\n\t);\n}\n\n/**\n * Whether `@operationIds` names the operation's id, or checks it: its\n * interface or one of its namespaces is marked. An interface template's\n * instance is never emitted; only the interfaces extending it are.\n */\nexport function isNamed(program: Program, operation: Operation): boolean {\n\tconst container = operation.interface;\n\tif (container === undefined) {\n\t\treturn isMarkedNamespace(program, operation.namespace);\n\t}\n\treturn (\n\t\t!isTemplateInstance(container) &&\n\t\t(isMarkedInterface(program, container) ||\n\t\t\tisMarkedNamespace(program, container.namespace))\n\t);\n}\n\n/**\n * Whether the emitter writes the operation: not an instance of an operation\n * template, `Read<Pet>`, nor an operation of an interface template's\n * instance. An interface extending an instance copies its operations, which\n * are instances too, and emitted.\n */\nexport function isEmitted(operation: Operation): boolean {\n\tconst container = operation.interface;\n\tif (container === undefined) return !isTemplateInstance(operation);\n\treturn !isTemplateInstance(container);\n}\n\n/**\n * The services whose document the operation is in: each `@service`\n * namespace around it, a nested one and the one outside it both. None, in a\n * program without a service, is the one document the emitter writes; in a\n * program with one, no document at all.\n */\nfunction servicesOf(program: Program, operation: Operation): Namespace[] {\n\tconst services: Namespace[] = [];\n\tfor (\n\t\tlet at = operation.interface?.namespace ?? operation.namespace;\n\t\tat !== undefined;\n\t\tat = at.namespace\n\t) {\n\t\tif (isService(program, at)) services.push(at);\n\t}\n\treturn services;\n}\n\n/**\n * Sets the ids, after every decorator, so the order of `@operationId` and\n * `@operationIds` in the source does not matter. Two operations with one id\n * in one service's document, one of them named by `@operationIds`, are an\n * error: one name in two interfaces, an interface that `extends` another and\n * so copies its operations, or an `@operationId` written elsewhere.\n * `@typespec/openapi3` would emit both without a word. An operation\n * template's instance, such as `Read<Pet>`, is never emitted; only the\n * operations declared from it (`op readPet is Read<Pet>`) are.\n */\nexport function validateOperationIds(program: Program): void {\n\tconst seen = new Map<Namespace | undefined, Map<string, boolean>>();\n\tconst hasServices = listServices(program).length > 0;\n\tnavigateProgram(program, {\n\t\toperation(operation) {\n\t\t\tif (!isEmitted(operation)) return;\n\t\t\tconst named = isNamed(program, operation);\n\t\t\tconst own = getOperationId(program, operation);\n\t\t\t// Elsewhere, only checked: the id `@typespec/openapi3` gives under its\n\t\t\t// default strategy.\n\t\t\tconst id =\n\t\t\t\town ??\n\t\t\t\t(named\n\t\t\t\t\t? idOf(program, operation)\n\t\t\t\t\t: resolveOperationId(program, operation));\n\t\t\tconst services = servicesOf(program, operation);\n\t\t\t// Outside every service of a program that has one: in no document.\n\t\t\tconst emitted = services.length > 0 || !hasServices;\n\t\t\tlet duplicate = false;\n\t\t\tfor (const service of services.length > 0 ? services : [undefined]) {\n\t\t\t\tconst ids = seen.get(service) ?? new Map<string, boolean>();\n\t\t\t\tseen.set(service, ids);\n\t\t\t\tif (emitted && ids.has(id) && (named || ids.get(id))) duplicate = true;\n\t\t\t\tids.set(id, named || (ids.get(id) ?? false));\n\t\t\t}\n\t\t\tif (duplicate) {\n\t\t\t\t$lib.reportDiagnostic(program, {\n\t\t\t\t\tcode: 'duplicate-operation-id',\n\t\t\t\t\tformat: { id },\n\t\t\t\t\ttarget: operation,\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Only the ids it gives: an explicit id on any other operation would\n\t\t\t// override the emitter's `operation-id-strategy`.\n\t\t\tif (named && own === undefined) setOperationId(program, operation, id);\n\t\t},\n\t});\n}\n",
8
- "/**\n * The verbs `@operationIds` completes with an interface's resource: `create`\n * in `Users` is `createUser`, `list` is `listUsers`, and `findById` is\n * `findUserById`. Any other name is the id as written.\n */\n\n/** Whether a verb takes the resource's singular or its plural. */\nexport type Grammar = 'singular' | 'plural';\n\n/** A resource's two names: `User` and `Users`. */\nexport interface Resource {\n\treadonly singular: string;\n\treadonly plural: string;\n}\n\n/** The library's own verbs. */\nexport const VERBS: Readonly<Record<string, Grammar>> = {\n\tlist: 'plural',\n\tread: 'plural',\n\tfind: 'plural',\n\tsearch: 'plural',\n\tquery: 'plural',\n\tcount: 'plural',\n\tcreateMany: 'plural',\n\tupdateMany: 'plural',\n\tdeleteMany: 'plural',\n\tget: 'singular',\n\tcreate: 'singular',\n\tupdate: 'singular',\n\tpatch: 'singular',\n\treplace: 'singular',\n\tupsert: 'singular',\n\tdelete: 'singular',\n};\n\n/**\n * The HTTP methods each of the library's verbs is sent with: a read is a\n * `GET` (or a `HEAD`), a search may carry its criteria in a `POST` body, a\n * bulk delete its ids. `QUERY` joins `search` and `query` once\n * `@typespec/http` declares it.\n */\nexport const METHODS: Readonly<Record<string, readonly string[]>> = {\n\tlist: ['get', 'head'],\n\tread: ['get', 'head'],\n\tfind: ['get', 'head'],\n\tcount: ['get', 'head'],\n\tget: ['get', 'head'],\n\tsearch: ['get', 'post'],\n\tquery: ['get', 'post'],\n\tcreate: ['post'],\n\tcreateMany: ['post'],\n\tupdate: ['put', 'patch'],\n\tupdateMany: ['put', 'patch'],\n\tpatch: ['patch'],\n\treplace: ['put'],\n\tupsert: ['put'],\n\tdelete: ['delete'],\n\tdeleteMany: ['delete', 'post'],\n};\n\n/** Plurals the rules below get wrong, by their singular. */\nconst IRREGULAR: Readonly<Record<string, string>> = {\n\tpeople: 'person',\n\tchildren: 'child',\n\tmen: 'man',\n\twomen: 'woman',\n\tmice: 'mouse',\n\tfeet: 'foot',\n\tteeth: 'tooth',\n\tgeese: 'goose',\n\tmovies: 'movie',\n\tcookies: 'cookie',\n\tseries: 'series',\n\tspecies: 'species',\n\tnews: 'news',\n};\n\n/** `-es` plurals of a singular in `-us`: `Statuses`, not `Houses`. */\nconst US_ES = /(stat|bon|camp|vir|b|foc|cens|corp|radi|syllab)uses$/i;\n\n/** `-es` plurals of a singular in a sibilant: `Boxes`, `Matches`, `Wishes`. */\nconst SIBILANT_ES = /(ss|x|zz|sh|tch|nch|rch|oach)es$/i;\n\n/**\n * The singular of a plural name, by a short English rule on its last word:\n * `Users`, `BlogPosts`, `Categories`, `Addresses`, `Statuses`, `People`. A\n * guess: it misses some (`Heroes`, `Analysis`), which `singular` names.\n */\nexport function singularOf(plural: string): string {\n\tconst word = /[A-Z]?[a-z0-9]*$/.exec(plural)?.[0] ?? '';\n\tconst head = plural.slice(0, plural.length - word.length);\n\tconst key = word.toLowerCase();\n\tconst irregular = Object.hasOwn(IRREGULAR, key) ? IRREGULAR[key] : undefined;\n\tif (irregular !== undefined) {\n\t\tconst cased = word[0] === word[0]?.toUpperCase();\n\t\treturn `${head}${cased ? irregular[0]?.toUpperCase() + irregular.slice(1) : irregular}`;\n\t}\n\tif (/ies$/.test(word) && word.length > 4)\n\t\treturn `${head}${word.slice(0, -3)}y`;\n\tif (US_ES.test(word) || SIBILANT_ES.test(word)) {\n\t\treturn `${head}${word.slice(0, -2)}`;\n\t}\n\tif (/[^su]s$/.test(word)) return `${head}${word.slice(0, -1)}`;\n\treturn plural;\n}\n\n/**\n * The verb a name starts with, and the rest: the whole name, `list`, or the\n * verb before a `By`, `findById`. `undefined` when the name is neither.\n */\nexport function verbIn(\n\tname: string,\n\tverbs: Readonly<Record<string, Grammar>>,\n): { readonly verb: string; readonly rest: string } | undefined {\n\tif (Object.hasOwn(verbs, name)) return { verb: name, rest: '' };\n\tfor (const by of name.matchAll(/By[A-Z]/g)) {\n\t\tconst verb = name.slice(0, by.index);\n\t\tif (verb !== '' && Object.hasOwn(verbs, verb)) {\n\t\t\treturn { verb, rest: name.slice(by.index) };\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * The id of an operation of a resource: the verb and the resource's singular\n * or plural, or the verb, the resource and the rest of a `By` name:\n * `findById` to `findUserById`, the singular, but `deleteManyByTeam` to\n * `deleteManyUsersByTeam`, the plural of a `*Many` verb. `undefined` when\n * the name is none of them. The resource is taken only then.\n */\nexport function idWithVerb(\n\tname: string,\n\tresource: () => Resource,\n\tverbs: Readonly<Record<string, Grammar>>,\n): string | undefined {\n\tconst found = verbIn(name, verbs);\n\tif (found === undefined) return undefined;\n\tconst { verb, rest } = found;\n\tconst names = resource();\n\tconst noun =\n\t\trest === ''\n\t\t\t? names[verbs[verb] as Grammar]\n\t\t\t: verb.endsWith('Many')\n\t\t\t\t? names.plural\n\t\t\t\t: names.singular;\n\treturn `${verb}${noun}${rest}`;\n}\n",
9
+ "/**\n * The verbs `@operationIds` completes with an interface's resource: `create`\n * in `Users` is `createUser`, `list` is `listUsers`, and `findById` is\n * `findUserById`. Any other name is the id as written.\n */\n\n/** Whether a verb takes the resource's singular or its plural. */\nexport type Grammar = 'singular' | 'plural';\n\n/** A resource's two names: `User` and `Users`. */\nexport interface Resource {\n\treadonly singular: string;\n\treadonly plural: string;\n}\n\n/** The library's own verbs. */\nexport const VERBS: Readonly<Record<string, Grammar>> = {\n\tlist: 'plural',\n\tread: 'plural',\n\tfind: 'plural',\n\tsearch: 'plural',\n\tquery: 'plural',\n\tcount: 'plural',\n\tcreateMany: 'plural',\n\tupdateMany: 'plural',\n\tdeleteMany: 'plural',\n\tget: 'singular',\n\tcreate: 'singular',\n\tupdate: 'singular',\n\tpatch: 'singular',\n\treplace: 'singular',\n\tupsert: 'singular',\n\tdelete: 'singular',\n};\n\n/**\n * The HTTP methods each of the library's verbs is sent with: a read is a\n * `GET` (or a `HEAD`), a search may carry its criteria in a `POST` body, a\n * bulk delete its ids. A `QUERY`, a `POST` marked `@queryMethod`, is\n * a search too.\n */\nexport const METHODS: Readonly<Record<string, readonly string[]>> = {\n\tlist: ['get', 'head'],\n\tread: ['get', 'head'],\n\tfind: ['get', 'head'],\n\tcount: ['get', 'head'],\n\tget: ['get', 'head'],\n\tsearch: ['get', 'post', 'query'],\n\tquery: ['get', 'post', 'query'],\n\tcreate: ['post'],\n\tcreateMany: ['post'],\n\tupdate: ['put', 'patch'],\n\tupdateMany: ['put', 'patch'],\n\tpatch: ['patch'],\n\treplace: ['put'],\n\tupsert: ['put'],\n\tdelete: ['delete'],\n\tdeleteMany: ['delete', 'post'],\n};\n\n/** Plurals the rules below get wrong, by their singular. */\nconst IRREGULAR: Readonly<Record<string, string>> = {\n\tpeople: 'person',\n\tchildren: 'child',\n\tmen: 'man',\n\twomen: 'woman',\n\tmice: 'mouse',\n\tfeet: 'foot',\n\tteeth: 'tooth',\n\tgeese: 'goose',\n\tmovies: 'movie',\n\tcookies: 'cookie',\n\tseries: 'series',\n\tspecies: 'species',\n\tnews: 'news',\n};\n\n/** `-es` plurals of a singular in `-us`: `Statuses`, not `Houses`. */\nconst US_ES = /(stat|bon|camp|vir|b|foc|cens|corp|radi|syllab)uses$/i;\n\n/** `-es` plurals of a singular in a sibilant: `Boxes`, `Matches`, `Wishes`. */\nconst SIBILANT_ES = /(ss|x|zz|sh|tch|nch|rch|oach)es$/i;\n\n/**\n * The singular of a plural name, by a short English rule on its last word:\n * `Users`, `BlogPosts`, `Categories`, `Addresses`, `Statuses`, `People`. A\n * guess: it misses some (`Heroes`, `Analysis`), which `singular` names.\n */\nexport function singularOf(plural: string): string {\n\tconst word = /[A-Z]?[a-z0-9]*$/.exec(plural)?.[0] ?? '';\n\tconst head = plural.slice(0, plural.length - word.length);\n\tconst key = word.toLowerCase();\n\tconst irregular = Object.hasOwn(IRREGULAR, key) ? IRREGULAR[key] : undefined;\n\tif (irregular !== undefined) {\n\t\tconst cased = word[0] === word[0]?.toUpperCase();\n\t\treturn `${head}${cased ? irregular[0]?.toUpperCase() + irregular.slice(1) : irregular}`;\n\t}\n\tif (/ies$/.test(word) && word.length > 4)\n\t\treturn `${head}${word.slice(0, -3)}y`;\n\tif (US_ES.test(word) || SIBILANT_ES.test(word)) {\n\t\treturn `${head}${word.slice(0, -2)}`;\n\t}\n\tif (/[^su]s$/.test(word)) return `${head}${word.slice(0, -1)}`;\n\treturn plural;\n}\n\n/**\n * The verb a name starts with, and the rest: the whole name, `list`, or the\n * verb before a `By`, `findById`. `undefined` when the name is neither.\n */\nexport function verbIn(\n\tname: string,\n\tverbs: Readonly<Record<string, Grammar>>,\n): { readonly verb: string; readonly rest: string } | undefined {\n\tif (Object.hasOwn(verbs, name)) return { verb: name, rest: '' };\n\tfor (const by of name.matchAll(/By[A-Z]/g)) {\n\t\tconst verb = name.slice(0, by.index);\n\t\tif (verb !== '' && Object.hasOwn(verbs, verb)) {\n\t\t\treturn { verb, rest: name.slice(by.index) };\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * The id of an operation of a resource: the verb and the resource's singular\n * or plural, or the verb, the resource and the rest of a `By` name:\n * `findById` to `findUserById`, the singular, but `deleteManyByTeam` to\n * `deleteManyUsersByTeam`, the plural of a `*Many` verb. `undefined` when\n * the name is none of them. The resource is taken only then.\n */\nexport function idWithVerb(\n\tname: string,\n\tresource: () => Resource,\n\tverbs: Readonly<Record<string, Grammar>>,\n): string | undefined {\n\tconst found = verbIn(name, verbs);\n\tif (found === undefined) return undefined;\n\tconst { verb, rest } = found;\n\tconst names = resource();\n\tconst noun =\n\t\trest === ''\n\t\t\t? names[verbs[verb] as Grammar]\n\t\t\t: verb.endsWith('Many')\n\t\t\t\t? names.plural\n\t\t\t\t: names.singular;\n\treturn `${verb}${noun}${rest}`;\n}\n",
9
10
  "/**\n * What `@operationIds` names an operation of an interface after: the\n * interface's resource, its singular and plural, and the verbs that take\n * them, layered from the library's, the namespaces', and the interface's.\n */\nimport type {\n\tInterface,\n\tNamespace,\n\tOperation,\n\tProgram,\n} from '@typespec/compiler';\nimport { $lib } from './lib';\nimport {\n\ttype Grammar,\n\tidWithVerb,\n\ttype Resource,\n\tsingularOf,\n\tVERBS,\n\tverbIn,\n} from './verbs';\n\n/** `@operationIds`' options, as `OperationIdsOptions` declares them. */\nexport interface OperationIdsOptions {\n\treadonly singular?: string;\n\treadonly plural?: string;\n\treadonly verbs?: Readonly<Record<string, Grammar>>;\n}\n\nfunction optionsOf(\n\tprogram: Program,\n\ttarget: Interface | Namespace,\n): OperationIdsOptions | undefined {\n\treturn program.stateMap($lib.stateKeys.operationIdsOptions).get(target);\n}\n\n/** The interface, then those it extends, however deep. */\nfunction lineageOf(target: Interface): Interface[] {\n\treturn [target, ...target.sourceInterfaces.flatMap(lineageOf)];\n}\n\n/**\n * The verbs an interface's operations take: the library's, then each marked\n * namespace's around it, outermost first, then its lineage's, its own last,\n * each overriding the one before.\n */\nfunction verbsOf(program: Program, target: Interface): Record<string, Grammar> {\n\tconst namespaces: Namespace[] = [];\n\tfor (let at = target.namespace; at !== undefined; at = at.namespace) {\n\t\tnamespaces.unshift(at);\n\t}\n\tconst layers = [...namespaces, ...lineageOf(target).reverse()];\n\treturn Object.assign(\n\t\t{},\n\t\tVERBS,\n\t\t...layers.map((layer) => optionsOf(program, layer)?.verbs ?? {}),\n\t);\n}\n\n/**\n * An interface's resource: its own `plural`, or its name, and its own\n * `singular`, or the plural's singular. Never an interface it extends: two\n * interfaces extending one would share its names, and collide.\n */\nfunction resourceOf(program: Program, target: Interface): Resource {\n\tconst options = optionsOf(program, target);\n\tconst plural = options?.plural || target.name;\n\treturn { plural, singular: options?.singular || singularOf(plural) };\n}\n\n/** The id `@operationIds` gives: the name as written, or a verb with its resource. */\nexport function idOf(program: Program, operation: Operation): string {\n\tconst container = operation.interface;\n\tif (container === undefined) return operation.name;\n\treturn (\n\t\tidWithVerb(\n\t\t\toperation.name,\n\t\t\t() => resourceOf(program, container),\n\t\t\tverbsOf(program, container),\n\t\t) ?? operation.name\n\t);\n}\n\n/** The verb an operation of an interface is named with, `find` in `findById`. */\nexport function verbOf(\n\tprogram: Program,\n\toperation: Operation,\n): string | undefined {\n\tconst container = operation.interface;\n\tif (container === undefined) return undefined;\n\treturn verbIn(operation.name, verbsOf(program, container))?.verb;\n}\n",
10
- "/**\n * A verb names what an operation does, and its method says the same:\n * `create` in `Users` is `createUser`, and a `GET` would tell a cache, a\n * proxy or a retry it is safe. An operation `@operationIds` names after one\n * of the library's verbs is sent with that verb's methods (`verbs.ts`), or\n * warned of. A verb `verbs` adds has no method to check, and an operation's\n * own `@operationId` is an id that is no verb. It runs before\n * `validateOperationIds` sets the others.\n */\nimport type { Operation, Program } from '@typespec/compiler';\nimport { getAllHttpServices } from '@typespec/http';\nimport { getOperationId } from '@typespec/openapi';\nimport { $lib } from './lib';\nimport { isNamed } from './operation-ids';\nimport { verbOf } from './resource';\nimport { METHODS } from './verbs';\n\n/** `PUT or PATCH`. */\nfunction methodsOf(methods: readonly string[]): string {\n\treturn methods.map((method) => method.toUpperCase()).join(' or ');\n}\n\n/** Checks each operation of each service once, as the emitter sends it. */\nexport function validateVerbMethods(program: Program): void {\n\tconst [services] = getAllHttpServices(program);\n\tconst checked = new Set<Operation>();\n\tfor (const service of services) {\n\t\tfor (const { operation, verb: method } of service.operations) {\n\t\t\tif (checked.has(operation)) continue;\n\t\t\tchecked.add(operation);\n\t\t\tif (!isNamed(program, operation)) continue;\n\t\t\tif (getOperationId(program, operation) !== undefined) continue;\n\t\t\tconst verb = verbOf(program, operation);\n\t\t\tif (verb === undefined || !Object.hasOwn(METHODS, verb)) continue;\n\t\t\tconst expected = METHODS[verb] as readonly string[];\n\t\t\tif (expected.includes(method)) continue;\n\t\t\t$lib.reportDiagnostic(program, {\n\t\t\t\tcode: 'verb-method-mismatch',\n\t\t\t\tformat: {\n\t\t\t\t\toperation: operation.name,\n\t\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\t\tverb,\n\t\t\t\t\texpected: methodsOf(expected),\n\t\t\t\t},\n\t\t\t\ttarget: operation,\n\t\t\t});\n\t\t}\n\t}\n}\n",
11
+ "/**\n * `@queryMethod`: a `POST` that is a `QUERY`, a safe request with its\n * criteria in the body. `@typespec/http` declares no `QUERY` yet, so the\n * operation is sent as a `POST` and the document marks it,\n * `x-nxgt-method: query`, for `@nxgt/openapi-codegen`.\n */\nimport type { DecoratorContext, Operation, Program } from '@typespec/compiler';\nimport { setExtension } from '@typespec/openapi';\nimport { eachHttpOperation } from './http-operations';\nimport { $lib } from './lib';\n\nexport function queryMethod(\n\tcontext: DecoratorContext,\n\ttarget: Operation,\n): void {\n\tconst { program } = context;\n\tprogram.stateSet($lib.stateKeys.queryMethod).add(target);\n\tsetExtension(program, target, 'x-nxgt-method', 'query');\n}\n\n/** Marked with `@queryMethod`. */\nexport function isQueryMethod(program: Program, operation: Operation): boolean {\n\treturn program.stateSet($lib.stateKeys.queryMethod).has(operation);\n}\n\n/** Each marked operation is a `POST`, as the emitter sends it. */\nexport function validateQueryMethods(program: Program): void {\n\teachHttpOperation(program, ({ operation, verb: method }) => {\n\t\tif (!isQueryMethod(program, operation) || method === 'post') return;\n\t\t$lib.reportDiagnostic(program, {\n\t\t\tcode: 'query-method-not-post',\n\t\t\tformat: { operation: operation.name, method: method.toUpperCase() },\n\t\t\ttarget: operation,\n\t\t});\n\t});\n}\n",
12
+ "/**\n * A verb names what an operation does, and its method says the same:\n * `create` in `Users` is `createUser`, and a `GET` would tell a cache, a\n * proxy or a retry it is safe. An operation `@operationIds` names after one\n * of the library's verbs is sent with that verb's methods (`verbs.ts`), or\n * warned of. A verb `verbs` adds has no method to check, and an operation's\n * own `@operationId` is an id that is no verb. It runs before\n * `validateOperationIds` sets the others.\n */\nimport type { Program } from '@typespec/compiler';\nimport { getOperationId } from '@typespec/openapi';\nimport { eachHttpOperation } from './http-operations';\nimport { $lib } from './lib';\nimport { isNamed } from './operation-ids';\nimport { isQueryMethod } from './query-method';\nimport { verbOf } from './resource';\nimport { METHODS } from './verbs';\n\n/** `PUT or PATCH`. */\nfunction methodsOf(methods: readonly string[]): string {\n\treturn methods.map((method) => method.toUpperCase()).join(' or ');\n}\n\n/** Checks each operation of each service once, as the emitter sends it. */\nexport function validateVerbMethods(program: Program): void {\n\teachHttpOperation(program, ({ operation, verb: sent }) => {\n\t\tif (!isNamed(program, operation)) return;\n\t\tif (getOperationId(program, operation) !== undefined) return;\n\t\tconst verb = verbOf(program, operation);\n\t\tif (verb === undefined || !Object.hasOwn(METHODS, verb)) return;\n\t\t// A `POST` marked `@queryMethod` is checked as the `QUERY` it is.\n\t\tconst method =\n\t\t\tsent === 'post' && isQueryMethod(program, operation) ? 'query' : sent;\n\t\tconst expected = METHODS[verb] as readonly string[];\n\t\tif (expected.includes(method)) return;\n\t\t$lib.reportDiagnostic(program, {\n\t\t\tcode: 'verb-method-mismatch',\n\t\t\tformat: {\n\t\t\t\toperation: operation.name,\n\t\t\t\tmethod: method.toUpperCase(),\n\t\t\t\tverb,\n\t\t\t\texpected: methodsOf(expected),\n\t\t\t},\n\t\t\ttarget: operation,\n\t\t});\n\t});\n}\n",
11
13
  "/**\n * The library's linter: rules a spec opts into in its `tspconfig.yaml`,\n * each a warning, each one it can turn off.\n *\n * ```yaml\n * linter:\n * extends:\n * - \"@nxgt/typespec/recommended\"\n * ```\n */\nimport { defineLinter } from '@typespec/compiler';\nimport { errorBodyShape } from './rules/error-body-shape';\nimport { listReturnsPage } from './rules/list-returns-page';\nimport { serviceOperationIds } from './rules/service-operation-ids';\n\nconst rules = [listReturnsPage, serviceOperationIds, errorBodyShape];\n\nexport const $linter = defineLinter({\n\trules,\n\truleSets: {\n\t\trecommended: {\n\t\t\tenable: Object.fromEntries(\n\t\t\t\trules.map(({ name }) => [`@nxgt/typespec/${name}`, true]),\n\t\t\t),\n\t\t},\n\t},\n});\n",
12
14
  "/**\n * An error reply's body is the envelope `@nxgt/openapi-hono` sends: a\n * `status`, a `message` and a `timestamp`, as `ErrorBody<Status>` declares\n * them. A reply without a body passes: `AuthenticationRequired` has none.\n */\nimport {\n\tcreateRule,\n\tisVoidType,\n\ttype Model,\n\tparamMessage,\n\twalkPropertiesInherited,\n} from '@typespec/compiler';\nimport { getHttpOperation } from '@typespec/http';\nimport { isWritten } from './written';\n\nconst ENVELOPE = ['status', 'message', 'timestamp'];\n\n/** Its own properties and those of the models it extends. */\nfunction isEnvelope(body: Model): boolean {\n\tconst names = new Set(\n\t\t[...walkPropertiesInherited(body)].map(({ name }) => name),\n\t);\n\treturn ENVELOPE.every((name) => names.has(name));\n}\n\nfunction statusOf(code: number | { start: number; end: number } | '*'): string {\n\treturn typeof code === 'object' ? `${code.start}-${code.end}` : `${code}`;\n}\n\nexport const errorBodyShape = createRule({\n\tname: 'error-body-shape',\n\tseverity: 'warning',\n\tdescription:\n\t\t'An error reply has the nxgt envelope: status, message and timestamp.',\n\tmessages: {\n\t\tdefault: paramMessage`${'operation'} answers ${'status'} with a body that is not the nxgt envelope: declare BadRequest, NotFound or another of the library's errors, or a body that spreads or extends ErrorBody<Status>.`,\n\t},\n\tcreate: (context) => ({\n\t\toperation(operation) {\n\t\t\tif (!isWritten(operation)) return;\n\t\t\tconst [http] = getHttpOperation(context.program, operation);\n\t\t\tfor (const { statusCodes, responses } of http.responses) {\n\t\t\t\tconst error =\n\t\t\t\t\tstatusCodes === '*' ||\n\t\t\t\t\t(typeof statusCodes === 'number'\n\t\t\t\t\t\t? statusCodes >= 400\n\t\t\t\t\t\t: statusCodes.start >= 400);\n\t\t\t\tif (!error) continue;\n\t\t\t\tconst wrong = responses.some(({ body }) => {\n\t\t\t\t\tif (body === undefined || isVoidType(body.type)) return false;\n\t\t\t\t\treturn body.type.kind !== 'Model' || !isEnvelope(body.type);\n\t\t\t\t});\n\t\t\t\tif (!wrong) continue;\n\t\t\t\tcontext.reportDiagnostic({\n\t\t\t\t\tformat: { operation: operation.name, status: statusOf(statusCodes) },\n\t\t\t\t\ttarget: operation,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t}),\n});\n",
13
15
  "/**\n * Whether the emitter writes an operation: not a template's declaration,\n * nor its instance (`Read<Pet>`), nor an operation of an interface\n * template's. Only the operations declared from them are checked.\n */\nimport { isTemplateDeclaration, type Operation } from '@typespec/compiler';\nimport { isEmitted } from '../operation-ids';\n\nexport function isWritten(operation: Operation): boolean {\n\tconst container = operation.interface;\n\treturn (\n\t\tisEmitted(operation) &&\n\t\t!isTemplateDeclaration(operation) &&\n\t\t(container === undefined || !isTemplateDeclaration(container))\n\t);\n}\n",
14
16
  "/**\n * A list returns a page: `Page<Item>` or `CursorPage<Item>`, never a bare\n * array, which cannot grow a `total` or a `nextCursor` without breaking its\n * clients. A list is an operation named `list`, `search` or `query`, alone\n * or followed by more: `listUsers`, `searchPosts`.\n */\nimport { createRule, isArrayModelType, paramMessage } from '@typespec/compiler';\nimport { getHttpOperation } from '@typespec/http';\nimport { isWritten } from './written';\n\nconst LIST = /^(list|search|query)(?=[A-Z]|$)/;\n\nexport const listReturnsPage = createRule({\n\tname: 'list-returns-page',\n\tseverity: 'warning',\n\tdescription:\n\t\t'A list, search or query returns Page<Item> or CursorPage<Item>.',\n\tmessages: {\n\t\tdefault: paramMessage`${'operation'} returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.`,\n\t},\n\tcreate: (context) => ({\n\t\toperation(operation) {\n\t\t\tif (!isWritten(operation) || !LIST.test(operation.name)) return;\n\t\t\tconst [http] = getHttpOperation(context.program, operation);\n\t\t\tconst array = http.responses.some(\n\t\t\t\t({ statusCodes, responses }) =>\n\t\t\t\t\ttypeof statusCodes === 'number' &&\n\t\t\t\t\tstatusCodes >= 200 &&\n\t\t\t\t\tstatusCodes < 300 &&\n\t\t\t\t\tresponses.some(\n\t\t\t\t\t\t({ body }) =>\n\t\t\t\t\t\t\tbody?.type.kind === 'Model' && isArrayModelType(body.type),\n\t\t\t\t\t),\n\t\t\t);\n\t\t\tif (!array) return;\n\t\t\tcontext.reportDiagnostic({\n\t\t\t\tformat: { operation: operation.name },\n\t\t\t\ttarget: operation,\n\t\t\t});\n\t\t},\n\t}),\n});\n",
15
17
  "/**\n * A service without `@operationIds` gets the emitter's ids: `Users_list`,\n * which the generated client's methods are named after.\n */\nimport { createRule, isService, paramMessage } from '@typespec/compiler';\nimport { isMarkedNamespace } from '../operation-ids';\n\nexport const serviceOperationIds = createRule({\n\tname: 'service-operation-ids',\n\tseverity: 'warning',\n\tdescription:\n\t\t'A service namespace marks its operation ids with @operationIds.',\n\tmessages: {\n\t\tdefault: paramMessage`The service ${'service'} has no @operationIds: its ids are the emitter's, such as Users_list. Mark the namespace with @operationIds.`,\n\t},\n\tcreate: (context) => ({\n\t\tnamespace(namespace) {\n\t\t\tconst { program } = context;\n\t\t\tif (!isService(program, namespace)) return;\n\t\t\tif (isMarkedNamespace(program, namespace)) return;\n\t\t\tcontext.reportDiagnostic({\n\t\t\t\tformat: { service: namespace.name },\n\t\t\t\ttarget: namespace,\n\t\t\t});\n\t\t},\n\t}),\n});\n",
16
- "/**\n * The JavaScript side of the `@nxgt/typespec` library, which `lib/main.tsp`\n * imports from `dist/`: its name and diagnostics, and the decorators a\n * template cannot express. The conventions themselves are in `lib/`.\n *\n * A decorator is exported only through `$decorators`: a top-level `$name`\n * export would declare it a second time, in the global namespace.\n */\nimport type { Program } from '@typespec/compiler';\nimport { validateOneReplyPerStatus } from './one-reply-per-status';\nimport { operationIds, validateOperationIds } from './operation-ids';\nimport { validateVerbMethods } from './verb-methods';\n\nexport { $lib } from './lib';\nexport { $linter } from './linter';\n\nexport function $onValidate(program: Program): void {\n\t// Before the ids are set: only an `@operationId` written in the spec is\n\t// one then, and the operation it names is not checked.\n\tvalidateVerbMethods(program);\n\tvalidateOperationIds(program);\n\tvalidateOneReplyPerStatus(program);\n}\n\nexport const $decorators = {\n\tNxgt: { operationIds },\n};\n"
18
+ "/**\n * The JavaScript side of the `@nxgt/typespec` library, which `lib/main.tsp`\n * imports from `dist/`: its name and diagnostics, and the decorators a\n * template cannot express. The conventions themselves are in `lib/`.\n *\n * A decorator is exported only through `$decorators`: a top-level `$name`\n * export would declare it a second time, in the global namespace.\n */\nimport type { Program } from '@typespec/compiler';\nimport { validateOneReplyPerStatus } from './one-reply-per-status';\nimport { operationIds, validateOperationIds } from './operation-ids';\nimport { queryMethod, validateQueryMethods } from './query-method';\nimport { validateVerbMethods } from './verb-methods';\n\nexport { $lib } from './lib';\nexport { $linter } from './linter';\n\nexport function $onValidate(program: Program): void {\n\tvalidateQueryMethods(program);\n\t// Before the ids are set: only an `@operationId` written in the spec is\n\t// one then, and the operation it names is not checked.\n\tvalidateVerbMethods(program);\n\tvalidateOperationIds(program);\n\tvalidateOneReplyPerStatus(program);\n}\n\nexport const $decorators = {\n\tNxgt: { operationIds, queryMethod },\n};\n"
17
19
  ],
18
- "mappings": "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",
19
- "debugId": "FB766460AB7EEFAD64756E2164756E21",
20
+ "mappings": "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",
21
+ "debugId": "B949BF3E72439B1464756E2164756E21",
20
22
  "names": []
21
23
  }
package/dist/lib.d.ts CHANGED
@@ -8,11 +8,14 @@ export declare const $lib: import("@typespec/compiler").TypeSpecLibrary<{
8
8
  'verb-method-mismatch': {
9
9
  readonly default: import("@typespec/compiler").CallableMessage<["operation", "method", "verb", "expected"]>;
10
10
  };
11
+ 'query-method-not-post': {
12
+ readonly default: import("@typespec/compiler").CallableMessage<["operation", "method"]>;
13
+ };
11
14
  'duplicate-status-reply': {
12
15
  readonly default: import("@typespec/compiler").CallableMessage<["operation", "status"]>;
13
16
  };
14
17
  'merged-status-reply': {
15
18
  readonly default: import("@typespec/compiler").CallableMessage<["operation", "status"]>;
16
19
  };
17
- }, Record<string, any>, "operationIds" | "operationIdsOptions">;
20
+ }, Record<string, any>, "queryMethod" | "operationIds" | "operationIdsOptions">;
18
21
  //# sourceMappingURL=lib.d.ts.map
package/dist/lib.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;+DA4Cf,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;+EAqDf,CAAC"}
@@ -8,10 +8,6 @@
8
8
  * Bodies of different content types are one reply, negotiated, and pass.
9
9
  */
10
10
  import { type Program } from '@typespec/compiler';
11
- /**
12
- * Checks each operation of each service, as the emitter sees it, once: a
13
- * service nested in another is listed in both. The services' own
14
- * diagnostics are the emitter's to report.
15
- */
11
+ /** Checks each operation of each service, as the emitter sees it, once. */
16
12
  export declare function validateOneReplyPerStatus(program: Program): void;
17
13
  //# sourceMappingURL=one-reply-per-status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"one-reply-per-status.d.ts","sourceRoot":"","sources":["../src/one-reply-per-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAA8B,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAqC9E;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqBhE"}
1
+ {"version":3,"file":"one-reply-per-status.d.ts","sourceRoot":"","sources":["../src/one-reply-per-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAqC9D,2EAA2E;AAC3E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAehE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@queryMethod`: a `POST` that is a `QUERY`, a safe request with its
3
+ * criteria in the body. `@typespec/http` declares no `QUERY` yet, so the
4
+ * operation is sent as a `POST` and the document marks it,
5
+ * `x-nxgt-method: query`, for `@nxgt/openapi-codegen`.
6
+ */
7
+ import type { DecoratorContext, Operation, Program } from '@typespec/compiler';
8
+ export declare function queryMethod(context: DecoratorContext, target: Operation): void;
9
+ /** Marked with `@queryMethod`. */
10
+ export declare function isQueryMethod(program: Program, operation: Operation): boolean;
11
+ /** Each marked operation is a `POST`, as the emitter sends it. */
12
+ export declare function validateQueryMethods(program: Program): void;
13
+ //# sourceMappingURL=query-method.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-method.d.ts","sourceRoot":"","sources":["../src/query-method.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAK/E,wBAAgB,WAAW,CAC1B,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,SAAS,GACf,IAAI,CAIN;AAED,kCAAkC;AAClC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAE7E;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"verb-methods.d.ts","sourceRoot":"","sources":["../src/verb-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAa,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAa7D,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyB1D"}
1
+ {"version":3,"file":"verb-methods.d.ts","sourceRoot":"","sources":["../src/verb-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAclD,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsB1D"}
package/dist/verbs.d.ts CHANGED
@@ -15,8 +15,8 @@ export declare const VERBS: Readonly<Record<string, Grammar>>;
15
15
  /**
16
16
  * The HTTP methods each of the library's verbs is sent with: a read is a
17
17
  * `GET` (or a `HEAD`), a search may carry its criteria in a `POST` body, a
18
- * bulk delete its ids. `QUERY` joins `search` and `query` once
19
- * `@typespec/http` declares it.
18
+ * bulk delete its ids. A `QUERY`, a `POST` marked `@queryMethod`, is
19
+ * a search too.
20
20
  */
21
21
  export declare const METHODS: Readonly<Record<string, readonly string[]>>;
22
22
  /**
package/docs/README.md CHANGED
@@ -5,6 +5,7 @@ The [package README](../README.md) is the short version.
5
5
  | Page | Read it when |
6
6
  | --- | --- |
7
7
  | [Getting started](guide/getting-started.md) | starting a new API project from the `tsp init` template, up to a Hono route |
8
+ | [Request bodies](guide/requests.md) | naming the body of a create, a `PUT` and a `PATCH`: `CreatePostRequest`, `UpdatePostRequest`, `PatchPostRequest` |
8
9
  | [Error replies](guide/errors.md) | declaring what an operation answers when it fails |
9
10
  | [Pagination](guide/pagination.md) | listing a collection a page at a time |
10
11
  | [Authentication](guide/auth.md) | protecting an operation with a `@nxgt/janus` session |
@@ -91,7 +91,7 @@ operation can declare the guard's 401 and the handler's 403:
91
91
 
92
92
  ```tsp
93
93
  @useAuth(JanusAuth)
94
- @post createPost(@body post: Create<Post>): {
94
+ @post createPost(@body post: CreateRequest<Post>): {
95
95
  @statusCode _: 201;
96
96
  @body post: Post;
97
97
  } | BadRequest | AuthenticationRequired | Forbidden;
@@ -80,9 +80,9 @@ model Post {
80
80
  | `Versioned` | `version: integer`, at least 0 | read, update | `version()` |
81
81
  | `Actors<Id = uuid>` | `createdBy`, `updatedBy`, `deletedBy: Id \| null` | read | `actors()` |
82
82
 
83
- The server sets every read-only column, so `Create<Post>` and
84
- `MergePatchUpdate<Post>` leave them out, and the generated `CreatePost` and
85
- `PostMergePatchUpdate` have no `createdAt` a client could send.
83
+ The server sets every read-only column, so `CreateRequest<Post>` and
84
+ `PatchRequest<Post>` leave them out, and the generated `CreatePostRequest` and
85
+ `PatchPostRequest` have no `createdAt` a client could send.
86
86
  `Actors<string>` or `Actors<integer>` match `actors('text')` or
87
87
  `actors('integer')`.
88
88
 
@@ -94,7 +94,7 @@ that version, and throws `OptimisticLockError` otherwise. Declare the
94
94
  `Conflict` it becomes:
95
95
 
96
96
  ```tsp
97
- @patch updatePost(@path postId: uuid, @body post: MergePatchUpdate<Post>):
97
+ @patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>):
98
98
  Post | BadRequest | NotFound | Conflict;
99
99
  ```
100
100
 
@@ -143,7 +143,7 @@ A handler may send a 400 of its own, without `issues`. `BadRequestBody`
143
143
  describes both, with `issues` optional:
144
144
 
145
145
  ```tsp
146
- @patch updatePost(@path postId: string, @body post: MergePatchUpdate<Post>): Post | BadRequest;
146
+ @patch patchPost(@path postId: string, @body post: PatchRequest<Post>): Post | BadRequest;
147
147
  ```
148
148
 
149
149
  The generator declares the validators' 400 itself, as `ValidationErrorBody`,
@@ -99,13 +99,16 @@ interface Users {
99
99
  get(@path id: uuid): User | GetErrors;
100
100
 
101
101
  @post
102
- create(@body user: Create<User>): {
102
+ create(@body user: CreateRequest<User>): {
103
103
  @statusCode _: 201;
104
104
  @body user: User;
105
105
  } | CreateErrors;
106
106
 
107
+ @put
108
+ update(@path id: uuid, @body user: UpdateRequest<User>): User | UpdateErrors;
109
+
107
110
  @patch
108
- update(@path id: uuid, @body user: MergePatchUpdate<User>): User | UpdateErrors;
111
+ patch(@path id: uuid, @body user: PatchRequest<User>): User | UpdateErrors;
109
112
 
110
113
  @delete
111
114
  delete(@path id: uuid): NoContentResponse | DeleteErrors;
@@ -113,7 +116,10 @@ interface Users {
113
116
  ```
114
117
 
115
118
  `@operationIds` names the operations `listUsers`, `getUser`, `createUser`,
116
- `updateUser` and `deleteUser` ([Operation ids](operation-ids.md)).
119
+ `updateUser`, `patchUser` and `deleteUser` ([Operation ids](operation-ids.md)).
120
+ `update` replaces the whole user with a `PUT` (`UpdateRequest`), `patch`
121
+ changes some of it with a merge patch (`PatchRequest`); see
122
+ [Request bodies](requests.md).
117
123
 
118
124
  ```ts
119
125
  // openapi-codegen.config.ts
@@ -250,13 +256,16 @@ interface Pets {
250
256
  get(@path id: uuid): Pet | GetErrors;
251
257
 
252
258
  @post
253
- create(@body pet: Create<Pet>): {
259
+ create(@body pet: CreateRequest<Pet>): {
254
260
  @statusCode _: 201;
255
261
  @body pet: Pet;
256
262
  } | CreateErrors;
257
263
 
264
+ @put
265
+ update(@path id: uuid, @body pet: UpdateRequest<Pet>): Pet | UpdateErrors;
266
+
258
267
  @patch
259
- update(@path id: uuid, @body pet: MergePatchUpdate<Pet>): Pet | UpdateErrors;
268
+ patch(@path id: uuid, @body pet: PatchRequest<Pet>): Pet | UpdateErrors;
260
269
 
261
270
  @delete
262
271
  delete(@path id: uuid): NoContentResponse | DeleteErrors;
@@ -264,8 +273,8 @@ interface Pets {
264
273
  ```
265
274
 
266
275
  The operation names stay; `@operationIds` takes the resource from the
267
- interface, so the ids become `listPets`, `getPet`, `createPet`, `updatePet`
268
- and `deletePet`. Run `tsp compile .` and `nxgt-openapi generate` again, and
276
+ interface, so the ids become `listPets`, `getPet`, `createPet`, `updatePet`,
277
+ `patchPet` and `deletePet`. Run `tsp compile .` and `nxgt-openapi generate` again, and
269
278
  the generated types, `Pet` and `PetPage`, follow. A name the singular rule
270
279
  does not know takes `@operationIds(#{ singular: "…" })` on the interface
271
280
  ([Operation ids](operation-ids.md)).
@@ -19,7 +19,7 @@ namespace Blog;
19
19
 
20
20
  @route("/posts")
21
21
  interface Posts {
22
- @post createPost(...IdempotencyKeyHeader, @body post: Create<Post>): {
22
+ @post createPost(...IdempotencyKeyHeader, @body post: CreateRequest<Post>): {
23
23
  @statusCode _: 201;
24
24
  ...IdempotentReplayedHeader;
25
25
  @body post: Post;
@@ -48,7 +48,7 @@ lock, gets one 409 with the description of the first, and the library warns,
48
48
  ## A rate limit
49
49
 
50
50
  ```tsp
51
- @post createPost(@body post: Create<Post>): {
51
+ @post createPost(@body post: CreateRequest<Post>): {
52
52
  @statusCode _: 201;
53
53
  ...RateLimitHeaders;
54
54
  @body post: Post;
@@ -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-sent-as-a-post), for criteria too large for a query string |
120
120
  | `create`, `createMany` | `POST` |
121
121
  | `update`, `updateMany` | `PUT` or `PATCH` |
122
122
  | `patch` | `PATCH` |
@@ -147,7 +147,7 @@ Give the operation its verb's method, or a name that is not a verb, which
147
147
  is then its id as written:
148
148
 
149
149
  ```tsp
150
- @patch @route("/{id}") update(@path id: uuid, @body user: User): User; // updateUser
150
+ @put @route("/{id}") update(@path id: uuid, @body user: User): User; // updateUser
151
151
  @post @route("/lookup") lookUpUser(@body id: uuid): User; // lookUpUser
152
152
  ```
153
153
 
@@ -171,10 +171,64 @@ 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 sent as a POST
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
+ On the wire it is a `POST`, which every server, proxy, `fetch` and CORS
203
+ policy accepts. The OpenAPI document says what it is, on the operation:
204
+
205
+ ```yaml
206
+ /users/search:
207
+ post:
208
+ operationId: searchUsers
209
+ x-nxgt-method: query
210
+ ```
211
+
212
+ `@nxgt/openapi-codegen` reads `x-nxgt-method` and marks the operation in the
213
+ generated `operations` table
214
+ ([TypeSpec](https://github.com/softistx/nxgt-http/blob/develop/packages/openapi-codegen/docs/guide/typespec.md)). When
215
+ `@typespec/http` declares `QUERY`, the decorator is what changes, not your
216
+ spec ([roadmap](../roadmap.md#later)).
217
+
218
+ | Case | Result |
219
+ | --- | --- |
220
+ | `search` or `query` marked | no warning: a `QUERY` is one of their methods |
221
+ | `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` |
222
+ | a name that is not a verb, marked | nothing to check |
223
+ | marked and not a `@post` | the error `query-method-not-post` |
224
+
225
+ ```text
226
+ error @nxgt/typespec/query-method-not-post: list is marked @queryMethod and sent with GET: a QUERY is sent as a POST until @typespec/http declares it. Make it a @post.
227
+ ```
228
+
229
+ A search that answers an array also meets the linter's `list-returns-page`;
230
+ return a `Page<Item>`, as `search` in the blog fixture does
231
+ (`Page<Author> | BadRequest`).
178
232
 
179
233
  ## The resource
180
234
 
@@ -0,0 +1,145 @@
1
+ # Request bodies
2
+
3
+ `CreateRequest<T>`, `UpdateRequest<T>` and `PatchRequest<T>` are TypeSpec's own
4
+ `Create`, `Update` and `MergePatchUpdate` with a name template, so the body of
5
+ each write is a component with a name you can read: `CreatePostRequest`.
6
+
7
+ ## Why names
8
+
9
+ TypeSpec's templates name what they emit from the model: `Create<Post>` is
10
+ the component `CreatePost` and `MergePatchUpdate<Post>` is
11
+ `PostMergePatchUpdate`. Two conventions in one spec, and a type in the
12
+ generated client that reads unlike the rest. The aliases give each body the
13
+ name `<Verb><Model>Request`, and `@nxgt/openapi-codegen` generates the types
14
+ and the Zod schemas under it.
15
+
16
+ ## The smallest example
17
+
18
+ ```tsp
19
+ import "@typespec/http";
20
+ import "@nxgt/typespec";
21
+
22
+ using Http;
23
+ using Nxgt;
24
+
25
+ namespace Blog;
26
+
27
+ model Post {
28
+ @visibility(Lifecycle.Read) id: uuid;
29
+ @minLength(1) title: string;
30
+ body: string;
31
+ tags?: string[];
32
+ ...Timestamps;
33
+ ...Versioned;
34
+ }
35
+
36
+ @route("/posts")
37
+ interface Posts {
38
+ @post createPost(@body post: CreateRequest<Post>): {
39
+ @statusCode _: 201;
40
+ @body post: Post;
41
+ } | BadRequest;
42
+
43
+ @put updatePost(@path postId: uuid, @body post: UpdateRequest<Post>):
44
+ Post | BadRequest | NotFound | Conflict;
45
+
46
+ @patch patchPost(@path postId: uuid, @body post: PatchRequest<Post>):
47
+ Post | BadRequest | NotFound | Conflict;
48
+ }
49
+ ```
50
+
51
+ | Alias | Is | Component | Content type | Properties |
52
+ | --- | --- | --- | --- | --- |
53
+ | `CreateRequest<T>` | `Create<T, "Create{name}Request">` | `CreatePostRequest` | `application/json` | those visible on create, as `T` declares them |
54
+ | `UpdateRequest<T>` | `Update<T, "Update{name}Request">` | `UpdatePostRequest` | `application/json` | those visible on update, as `T` declares them |
55
+ | `PatchRequest<T>` | `MergePatchUpdate<T, "Patch{name}Request">` | `PatchPostRequest` | `application/merge-patch+json` | those visible on update, all optional |
56
+
57
+ ```ts
58
+ import type {
59
+ CreatePostRequest,
60
+ PatchPostRequest,
61
+ UpdatePostRequest,
62
+ } from './generated/types';
63
+ ```
64
+
65
+ ```tsp
66
+ alias CreateRequest<T extends Reflection.Model> = Create<T, "Create{name}Request">;
67
+ alias UpdateRequest<T extends Reflection.Model> = Update<T, "Update{name}Request">;
68
+ alias PatchRequest<T extends Reflection.Model> = MergePatchUpdate<T, "Patch{name}Request">;
69
+ ```
70
+
71
+ Read-only properties (`id`, `Timestamps`, `Actors`) are in none of the three.
72
+ `version` from `Versioned` is read and update, so it is in
73
+ `UpdatePostRequest` and `PatchPostRequest`, and not in `CreatePostRequest`.
74
+
75
+ ## `PUT` replaces, `PATCH` merges
76
+
77
+ `UpdateRequest<Post>` is the whole resource: each property is as optional as
78
+ `Post` declares it, so `title` is required and `tags?` is not. Send it with
79
+ `@put`.
80
+
81
+ `PatchRequest<Post>` is a JSON Merge Patch: every property is optional, and a
82
+ nullable one is sent as `null` to clear it. The body's content type is
83
+ `application/merge-patch+json`, where the others are `application/json`.
84
+
85
+ ```yaml
86
+ # openapi.yaml (excerpt)
87
+ PUT:
88
+ requestBody:
89
+ content:
90
+ application/json:
91
+ schema:
92
+ $ref: '#/components/schemas/UpdatePostRequest'
93
+ PATCH:
94
+ requestBody:
95
+ content:
96
+ application/merge-patch+json:
97
+ schema:
98
+ $ref: '#/components/schemas/PatchPostRequest'
99
+ ```
100
+
101
+ A client sending a patch sets the header `Content-Type:
102
+ application/merge-patch+json`. The component `UpdatePostRequest` requires
103
+ `title`; `PatchPostRequest` requires nothing.
104
+
105
+ ## Nested models
106
+
107
+ A nested model the template filters is named by the same template; one it
108
+ leaves whole keeps its name.
109
+
110
+ ```tsp
111
+ model Address {
112
+ @visibility(Lifecycle.Read) id: uuid;
113
+ street: string;
114
+ }
115
+
116
+ model Author {
117
+ @visibility(Lifecycle.Read) id: uuid;
118
+ name: string;
119
+ address: Address;
120
+ }
121
+
122
+ @post createAuthor(@body author: CreateRequest<Author>): Author;
123
+ // CreateAuthorRequest.address is CreateAddressRequest, without `id`
124
+ ```
125
+
126
+ A model with no property the filter changes, such as a `Tag` with only
127
+ `name`, is referenced under its own name, `Tag`.
128
+
129
+ ## Your own name template
130
+
131
+ The aliases fix the convention. Another one is the same template, inline, and
132
+ `Create<T>` and `MergePatchUpdate<T>` stay usable:
133
+
134
+ ```tsp
135
+ @post createPost(@body post: Create<Post, "New{name}">): Post; // NewPost
136
+ @patch patchPost(@path postId: uuid, @body post: MergePatchUpdate<Post, "{name}Changes">): Post;
137
+ ```
138
+
139
+ `{name}` is the model's name. Without a template, TypeSpec names the
140
+ components `CreatePost` and `PostMergePatchUpdate`.
141
+
142
+ ## Next
143
+
144
+ The columns a request leaves out are on [Scalars and columns](columns.md); the
145
+ errors a write answers on [Error replies](errors.md).
package/docs/roadmap.md CHANGED
@@ -16,16 +16,32 @@ 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 real `QUERY`.** `@queryMethod` is sent as a `POST` and `search` and
20
+ `query` accept it; once `@typespec/http` declares the HTTP `QUERY`
21
+ method, the decorator sends a `QUERY` and a spec does not change.
22
+ `@nxgt/openapi-codegen` already reads an OpenAPI 3.2 `query` operation.
23
23
  - **`ETag` and conditional requests.** `ETag`, with `If-None-Match` and
24
24
  `If-Match`, once the `@nxgt/*` packages send one the same way. The
25
25
  optimistic lock is the body's `version` today (`Versioned`).
26
26
 
27
27
  ## Shipped
28
28
 
29
+ ### 0.12.0
30
+
31
+ - **`@queryMethod`, a `QUERY` sent as a `POST`.** The decorator marks a
32
+ `@post` with `x-nxgt-method: query`, which `@nxgt/openapi-codegen` reads,
33
+ and `search` and `query` accept it: a safe request with a body, today, with
34
+ every server, proxy and `fetch`. On anything but a `@post` it is the error
35
+ `query-method-not-post`.
36
+
37
+ ### 0.11.0
38
+
39
+ - **Request body aliases.** `CreateRequest<T>`, `UpdateRequest<T>` and
40
+ `PatchRequest<T>` are TypeSpec's `Create`, `Update` and `MergePatchUpdate`
41
+ named `Create<T>Request`, `Update<T>Request` and `Patch<T>Request`, so the
42
+ generated types and schemas share one convention. `Create<T>` and
43
+ `MergePatchUpdate<T>` stay usable.
44
+
29
45
  ### 0.10.0
30
46
 
31
47
  - **Every scalar of `@nxgt/graphql-scalars`.** The scalars, under the same
@@ -13,7 +13,7 @@ one name with `name_collision`.
13
13
  optional, and the generator adds its own 400 beside it.
14
14
 
15
15
  ```tsp
16
- @patch updatePost(@path postId: string, @body post: MergePatchUpdate<Post>): Post | BadRequest;
16
+ @patch patchPost(@path postId: string, @body post: PatchRequest<Post>): Post | BadRequest;
17
17
  ```
18
18
 
19
19
  ## `Couldn't resolve import "@nxgt/typespec"`
@@ -293,6 +293,48 @@ 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: a QUERY is sent as a POST until @typespec/http declares it. 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` sent as a `POST`, because
310
+ `@typespec/http` declares no `QUERY`; on a `GET`, a `PUT` or a `DELETE` it
311
+ has nothing to stand for.
312
+
313
+ **Fix:** make it a `@post`, with the criteria in the body:
314
+
315
+ ```tsp
316
+ @post @queryMethod @route("/search") search(@body names: string[]): string[];
317
+ ```
318
+
319
+ Or drop `@queryMethod`: a `GET` needs no mark. More, in
320
+ [Operation ids](guide/operation-ids.md#a-query-sent-as-a-post).
321
+
322
+ ## `findById is sent with QUERY, where its verb find is sent with GET or HEAD`
323
+
324
+ **When:** a warning, `verb-method-mismatch`: an operation named after one of
325
+ the library's verbs other than `search` and `query` is marked `@queryMethod`.
326
+
327
+ **Why:** a marked `POST` is checked as the `QUERY` it is, and only `search`
328
+ and `query` are sent with `QUERY`.
329
+
330
+ **Fix:** name it `search` or `query`, a name that is not a verb, or take off
331
+ `@queryMethod` and give the verb its own method, as in the entry on
332
+ [`findById is sent with POST`](#findbyid-is-sent-with-post-where-its-verb-find-is-sent-with-get-or-head).
333
+
334
+ ```tsp
335
+ @post @queryMethod @route("/search") searchByName(@body names: string[]): User[]; // searchUserByName
336
+ ```
337
+
296
338
  ## `declares a reply without a body and one with a body of status …`
297
339
 
298
340
  **When:** an operation declares, for one status, a reply without a body and
package/lib/main.tsp CHANGED
@@ -9,3 +9,5 @@ import "./scalars.tsp";
9
9
  import "./columns.tsp";
10
10
  import "./headers.tsp";
11
11
  import "./sorting.tsp";
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);
@@ -0,0 +1,36 @@
1
+ import "@typespec/http";
2
+
3
+ using Http;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * The body of a create: the properties of `T` visible on create, named
9
+ * `Create<T>Request` (`CreatePostRequest`). A nested model filtered the same
10
+ * way is named alike (`CreateAddressRequest`); one left whole keeps its name.
11
+ */
12
+ alias CreateRequest<T extends Reflection.Model> = Create<
13
+ T,
14
+ "Create{name}Request"
15
+ >;
16
+
17
+ /**
18
+ * The body of a full update, as a `PUT` replaces the resource: the properties
19
+ * of `T` visible on update, each as optional as `T` declares it, named
20
+ * `Update<T>Request` (`UpdatePostRequest`).
21
+ */
22
+ alias UpdateRequest<T extends Reflection.Model> = Update<
23
+ T,
24
+ "Update{name}Request"
25
+ >;
26
+
27
+ /**
28
+ * The body of a partial update, as a JSON Merge Patch
29
+ * (`application/merge-patch+json`): the properties of `T` visible on update,
30
+ * all optional, a nullable one sent as `null` to clear it, named
31
+ * `Patch<T>Request` (`PatchPostRequest`).
32
+ */
33
+ alias PatchRequest<T extends Reflection.Model> = MergePatchUpdate<
34
+ T,
35
+ "Patch{name}Request"
36
+ >;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/typespec",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,13 +52,13 @@
52
52
  "typescript": "^6.0.3 || ^7.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@nxgt/openapi-codegen": "^0.8.0",
55
+ "@nxgt/openapi-codegen": "^0.9.0",
56
56
  "@nxgt/openapi-hono": "^0.3.2",
57
57
  "@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",
58
+ "@typespec/compiler": "1.17.0",
59
+ "@typespec/http": "1.17.0",
60
+ "@typespec/openapi": "1.17.0",
61
+ "@typespec/openapi3": "1.17.0",
62
62
  "hono": "^4.13.4",
63
63
  "zod": "^4.5.4"
64
64
  }
@@ -50,16 +50,23 @@ interface Users {
50
50
 
51
51
  /** Creates a user. */
52
52
  @post
53
- create(@body user: Create<User>): {
53
+ create(@body user: CreateRequest<User>): {
54
54
  @statusCode _: 201;
55
55
  @body user: User;
56
56
  } | CreateErrors;
57
57
 
58
- /** Updates a user with a merge patch. */
59
- @patch
58
+ /** Replaces a user: the whole body, as a `PUT` sends it. */
59
+ @put
60
60
  update(
61
61
  @path id: uuid,
62
- @body user: MergePatchUpdate<User>,
62
+ @body user: UpdateRequest<User>,
63
+ ): User | UpdateErrors;
64
+
65
+ /** Updates some of a user's properties, with a merge patch. */
66
+ @patch
67
+ patch(
68
+ @path id: uuid,
69
+ @body user: PatchRequest<User>,
63
70
  ): User | UpdateErrors;
64
71
 
65
72
  /** Deletes a user. */