@nxgt/typespec 0.12.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.
package/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  nxgt's HTTP conventions as a [TypeSpec](https://typespec.io) library, so an
4
4
  API spec states them in one word instead of rewriting them. Compile the spec to
5
- OpenAPI 3.1 or 3.2 with `@typespec/openapi3`, then generate the code with
5
+ OpenAPI 3.1 or 3.2 with its emitter, `@nxgt/typespec` (`@typespec/openapi3`, and a
6
+ real `QUERY` in 3.2), then generate the code with
6
7
  [`@nxgt/openapi-codegen`](https://github.com/softistx/nxgt-http/blob/develop/packages/openapi-codegen/README.md).
7
8
  Each shape here is one the `@nxgt/*` packages already send on the wire.
8
9
 
@@ -18,20 +19,22 @@ bun add -d @nxgt/typespec @typespec/compiler @typespec/http @typespec/openapi @t
18
19
 
19
20
  `@typespec/compiler`, `@typespec/http` and `@typespec/openapi` 1.16 or later
20
21
  are peer dependencies,
21
- and `typescript` 6 or 7 too, as for every `@nxgt` package. `@typespec/openapi3`
22
- compiles the spec, and `@nxgt/openapi-codegen` generates the code from it.
22
+ and `typescript` 6 or 7 too, as for every `@nxgt` package.
23
+ `@typespec/openapi3` 1.16 or later is an optional peer, loaded only when the
24
+ spec is emitted: install it to compile the spec to OpenAPI.
25
+ `@nxgt/openapi-codegen` generates the code from the document.
23
26
  The generator's CLI, `nxgt-openapi`, runs on [Bun](https://bun.sh): it needs
24
27
  `bun` on the PATH, even when called through `npx` or an npm script.
25
- Emit OpenAPI 3.1 or 3.2 in `tspconfig.yaml`: `@typespec/openapi3` emits 3.0
26
- by default, and the generator refuses it. Every convention here is compiled
28
+ Emit OpenAPI 3.1 or 3.2 in `tspconfig.yaml` with this package's emitter:
29
+ `@typespec/openapi3` emits 3.0 by default, and the generator refuses it. Every convention here is compiled
27
30
  to both in CI, and generates the same code from either.
28
31
 
29
32
  ```yaml
30
33
  # api/tspconfig.yaml
31
34
  emit:
32
- - '@typespec/openapi3'
35
+ - '@nxgt/typespec'
33
36
  options:
34
- '@typespec/openapi3':
37
+ '@nxgt/typespec':
35
38
  openapi-versions: ['3.1.0']
36
39
  emitter-output-dir: '{project-root}/../openapi'
37
40
  output-file: openapi.yaml
@@ -56,7 +59,7 @@ It installs the compiler, `@typespec/http`, `@typespec/openapi`,
56
59
  `@typespec/openapi3`, this package and `@nxgt/openapi-codegen`, and writes:
57
60
 
58
61
  - `tspconfig.yaml`: the linter's recommended rules, and OpenAPI 3.1 written
59
- to `openapi/openapi.yaml`;
62
+ to `openapi/openapi.yaml` by the emitter `@nxgt/typespec`;
60
63
  - `main.tsp`: the service in the namespace `Api`, with `@operationIds`, and a
61
64
  `Users` resource with a paged, sorted list, `get`, `create`, `update` and
62
65
  `delete`, and their error aliases;
@@ -459,10 +462,12 @@ The verbs `verbs` adds are not checked, nor an operation with its own
459
462
  ### A search with a body
460
463
 
461
464
  `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:
465
+ `@typespec/http` 1.17 declares none. Mark a `@post` with `@queryMethod`, and
466
+ emit OpenAPI 3.2 with the emitter [`@nxgt/typespec`](#the-emitter): the
467
+ document gets a real `query` operation. Only 3.2 has one, so the mark with
468
+ any older version in `openapi-versions`, or with the plain `@typespec/openapi3`
469
+ emitter, is an error, not a `POST`: `query-method-needs-openapi-3.2`,
470
+ `query-method-needs-nxgt-emitter`.
466
471
 
467
472
  ```tsp
468
473
  @route("/authors")
@@ -477,7 +482,34 @@ interface Authors {
477
482
  `QUERY` is not their method. The decorator on anything but a `@post` is the
478
483
  error `query-method-not-post`. When `@typespec/http` declares `QUERY`, the
479
484
  decorator is what changes, not your spec. More, in
480
- [Operation ids](docs/guide/operation-ids.md#a-query-sent-as-a-post).
485
+ [Operation ids](docs/guide/operation-ids.md#a-query-in-openapi-32) and
486
+ [The emitter](docs/guide/emitter.md).
487
+
488
+ ### The emitter
489
+
490
+ `@nxgt/typespec` is also a TypeSpec emitter. It runs `@typespec/openapi3` with
491
+ the options and the files you give that one, and in an OpenAPI 3.2 document
492
+ moves each `@post` marked `@queryMethod` to a `query` operation, in place and
493
+ without `x-nxgt-method`. Without a marked operation its output is
494
+ byte-identical to `@typespec/openapi3`'s. With a marked operation and an
495
+ older version in `openapi-versions` (unset means 3.0), it reports
496
+ `query-method-needs-openapi-3.2` and emits nothing.
497
+
498
+ ```yaml
499
+ # api/tspconfig.yaml
500
+ emit:
501
+ - '@nxgt/typespec'
502
+ options:
503
+ '@nxgt/typespec':
504
+ openapi-versions: ['3.2.0'] # 3.1.0 is an error beside a @queryMethod
505
+ emitter-output-dir: '{project-root}/../openapi'
506
+ output-file: openapi.yaml
507
+ ```
508
+
509
+ `@nxgt/openapi-codegen` then generates `method: 'query'`, `@nxgt/openapi-hono`
510
+ serves `.query(...)`, and the `@nxgt/httpyz` client sends `QUERY`. A real
511
+ `QUERY` needs a server stack that accepts the method, and a preflight under
512
+ CORS: [The emitter](docs/guide/emitter.md#a-query-on-the-wire).
481
513
 
482
514
  ### Linter
483
515
 
@@ -513,7 +545,7 @@ passing example of each rule, in [Linter](docs/guide/linter.md).
513
545
 
514
546
  | Decorator | On | What it does |
515
547
  | --- | --- | --- |
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 |
548
+ | `@queryMethod` | an operation | marks a `@post` as a `QUERY`: needs the emitter `@nxgt/typespec` and OpenAPI 3.2, where the document gets a `query` operation, and is an error otherwise; `@operationIds` checks its verb as a `QUERY`'s |
517
549
  | `@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` |
518
550
 
519
551
  `OperationIdsOptions` is `#{ singular?: string, plural?: string, verbs?: Record<"singular" | "plural"> }`:
@@ -522,6 +554,12 @@ overrides verbs, on a namespace or an interface.
522
554
 
523
555
  `@queryMethod` has no options.
524
556
 
557
+ ### Emitter
558
+
559
+ | Name | In | What it does |
560
+ | --- | --- | --- |
561
+ | `@nxgt/typespec` | `emit:` in `tspconfig.yaml` | `@typespec/openapi3` with its options and files; each marked `@post` becomes a `query` operation in a 3.2 document, and a marked one with an older version is an error. Needs the optional peer `@typespec/openapi3` |
562
+
525
563
  ### Diagnostics
526
564
 
527
565
  | Code | Reported when |
@@ -532,6 +570,9 @@ overrides verbs, on a namespace or an interface.
532
570
  | `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 |
533
571
  | `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
572
  | `query-method-not-post` | error: an operation marked `@queryMethod` is not a `@post` |
573
+ | `query-method-needs-openapi-3.2` | error: the emitter `@nxgt/typespec` is asked for a version below 3.2 (or none, which is 3.0) beside a `@queryMethod`; nothing is emitted |
574
+ | `query-method-needs-nxgt-emitter` | error: `@typespec/openapi3` is in `emit` beside a `@queryMethod` |
575
+ | `openapi3-missing` | error: the emitter `@nxgt/typespec` runs `@typespec/openapi3`, which is not installed |
535
576
 
536
577
  ### Linter rules
537
578
 
@@ -690,6 +731,8 @@ envelope and the rate limit's headers.
690
731
 
691
732
  - [Getting started](docs/guide/getting-started.md): the `tsp init`
692
733
  template, the files it writes, and the steps from the spec to a Hono route;
734
+ - [The emitter](docs/guide/emitter.md): `@nxgt/typespec` in `tspconfig.yaml`,
735
+ a `@queryMethod` as a real `QUERY` in OpenAPI 3.2, and what that needs;
693
736
  - [Error replies](docs/guide/errors.md): the envelope, each response, the
694
737
  aliases by verb, and what the generator makes of them;
695
738
  - [Pagination](docs/guide/pagination.md): offset and cursor pages, and
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The emitter `@nxgt/typespec`: `@typespec/openapi3`, with its options,
3
+ * checked by its own schema, and its files, and each `POST` marked
4
+ * `@queryMethod` a real `QUERY` in an OpenAPI 3.2 document
5
+ * (`query-operations.ts`). Only 3.2 has a `QUERY`: a marked operation with
6
+ * an older version asked for is an error, and nothing is emitted.
7
+ * `@typespec/openapi3` is loaded only here, so a spec that only imports the
8
+ * library does not need it.
9
+ */
10
+ import { type EmitContext } from '@typespec/compiler';
11
+ export declare function $onEmit(context: EmitContext): Promise<void>;
12
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGN,KAAK,WAAW,EAEhB,MAAM,oBAAoB,CAAC;AAmE5B,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BjE"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  import type { Program } from '@typespec/compiler';
10
10
  import { operationIds } from './operation-ids';
11
11
  import { queryMethod } from './query-method';
12
+ export { $onEmit } from './emitter';
12
13
  export { $lib } from './lib';
13
14
  export { $linter } from './linter';
14
15
  export declare function $onValidate(program: Program): void;
@@ -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;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"}
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,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,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
@@ -42,7 +42,25 @@ var $lib = createTypeSpecLibrary({
42
42
  "query-method-not-post": {
43
43
  severity: "error",
44
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.`
45
+ default: paramMessage`${"operation"} is marked @queryMethod and sent with ${"method"}: @queryMethod marks a @post, which @nxgt/typespec writes as a QUERY in OpenAPI 3.2. Make it a @post.`
46
+ }
47
+ },
48
+ "query-method-needs-openapi-3.2": {
49
+ severity: "error",
50
+ messages: {
51
+ default: paramMessage`${"operation"} is marked @queryMethod: only OpenAPI 3.2 has a QUERY operation, and openapi-versions asks for ${"version"}. Emit 3.2 only, or take off @queryMethod.`
52
+ }
53
+ },
54
+ "query-method-needs-nxgt-emitter": {
55
+ severity: "error",
56
+ messages: {
57
+ default: paramMessage`${"operation"} is marked @queryMethod: emit with @nxgt/typespec, which writes it as a QUERY; @typespec/openapi3 would write a POST.`
58
+ }
59
+ },
60
+ "openapi3-missing": {
61
+ severity: "error",
62
+ messages: {
63
+ default: "The emitter @nxgt/typespec runs @typespec/openapi3, which is not installed: add it beside @nxgt/typespec."
46
64
  }
47
65
  },
48
66
  "duplicate-status-reply": {
@@ -58,6 +76,7 @@ var $lib = createTypeSpecLibrary({
58
76
  }
59
77
  }
60
78
  },
79
+ capabilities: { dryRun: true },
61
80
  state: {
62
81
  queryMethod: {
63
82
  description: "The operations marked with @queryMethod"
@@ -338,6 +357,9 @@ function queryMethod(context, target) {
338
357
  function isQueryMethod(program, operation) {
339
358
  return program.stateSet($lib.stateKeys.queryMethod).has(operation);
340
359
  }
360
+ function queryMethods(program) {
361
+ return [...program.stateSet($lib.stateKeys.queryMethod)];
362
+ }
341
363
  function validateQueryMethods(program) {
342
364
  eachHttpOperation(program, ({ operation, verb: method }) => {
343
365
  if (!isQueryMethod(program, operation) || method === "post")
@@ -348,6 +370,16 @@ function validateQueryMethods(program) {
348
370
  target: operation
349
371
  });
350
372
  });
373
+ if (!(program.compilerOptions.emit ?? []).includes("@typespec/openapi3")) {
374
+ return;
375
+ }
376
+ for (const operation of queryMethods(program)) {
377
+ $lib.reportDiagnostic(program, {
378
+ code: "query-method-needs-nxgt-emitter",
379
+ format: { operation: operation.name },
380
+ target: operation
381
+ });
382
+ }
351
383
  }
352
384
 
353
385
  // src/verb-methods.ts
@@ -380,6 +412,119 @@ function validateVerbMethods(program) {
380
412
  });
381
413
  });
382
414
  }
415
+
416
+ // src/emitter.ts
417
+ import {
418
+ NoTarget
419
+ } from "@typespec/compiler";
420
+
421
+ // src/query-operations.ts
422
+ import { parse, stringify } from "yaml";
423
+ function moveQueries(document) {
424
+ if (!String(document["openapi"]).startsWith("3.2"))
425
+ return false;
426
+ const paths = document["paths"];
427
+ if (typeof paths !== "object" || paths === null)
428
+ return false;
429
+ let moved = false;
430
+ for (const [path, item] of Object.entries(paths)) {
431
+ const post = item["post"];
432
+ if (post?.["x-nxgt-method"] !== "query" || item["query"] !== undefined) {
433
+ continue;
434
+ }
435
+ const { "x-nxgt-method": _, ...operation } = post;
436
+ paths[path] = Object.fromEntries(Object.entries(item).map(([key, value]) => key === "post" ? ["query", operation] : [key, value]));
437
+ moved = true;
438
+ }
439
+ return moved;
440
+ }
441
+ function withQueryOperations(content) {
442
+ const json = content.trimStart().startsWith("{");
443
+ const document = json ? JSON.parse(content) : parse(content);
444
+ if (typeof document !== "object" || document === null)
445
+ return content;
446
+ if (!moveQueries(document))
447
+ return content;
448
+ const text = json ? `${JSON.stringify(document, null, 2)}
449
+ ` : stringify(document, {
450
+ singleQuote: true,
451
+ aliasDuplicateObjects: false,
452
+ lineWidth: 0,
453
+ compat: "yaml-1.1"
454
+ });
455
+ return content.includes(`\r
456
+ `) ? text.replace(/\n/g, `\r
457
+ `) : text;
458
+ }
459
+
460
+ // src/emitter.ts
461
+ var DEFAULT_VERSIONS = ["3.0.0"];
462
+ function invalidOptions(openapi3, options) {
463
+ const lib = openapi3.$lib;
464
+ const validator = "emitterOptionValidator" in lib ? lib.emitterOptionValidator : undefined;
465
+ if (typeof validator !== "object" || validator === null || !("validate" in validator) || typeof validator.validate !== "function") {
466
+ return [];
467
+ }
468
+ return validator.validate(options, NoTarget);
469
+ }
470
+ async function loadOpenAPI3(context) {
471
+ try {
472
+ return await import("@typespec/openapi3");
473
+ } catch {
474
+ $lib.reportDiagnostic(context.program, {
475
+ code: "openapi3-missing",
476
+ target: NoTarget
477
+ });
478
+ return;
479
+ }
480
+ }
481
+ function queriesFit(context) {
482
+ const { program } = context;
483
+ const marked = queryMethods(program);
484
+ if (marked.length === 0)
485
+ return true;
486
+ const versions = context.options["openapi-versions"] ?? DEFAULT_VERSIONS;
487
+ const older = versions.find((version) => !version.startsWith("3.2"));
488
+ if (older === undefined)
489
+ return true;
490
+ for (const operation of marked) {
491
+ $lib.reportDiagnostic(program, {
492
+ code: "query-method-needs-openapi-3.2",
493
+ format: { operation: operation.name, version: older },
494
+ target: operation
495
+ });
496
+ }
497
+ return false;
498
+ }
499
+ async function $onEmit(context) {
500
+ const openapi3 = await loadOpenAPI3(context);
501
+ if (openapi3 === undefined)
502
+ return;
503
+ const { program } = context;
504
+ const invalid = invalidOptions(openapi3, context.options);
505
+ if (invalid.length > 0) {
506
+ program.reportDiagnostics(invalid);
507
+ return;
508
+ }
509
+ if (!queriesFit(context))
510
+ return;
511
+ const host = program.host;
512
+ const writing = new Proxy(host, {
513
+ get(target, key) {
514
+ if (key === "writeFile") {
515
+ return (path, content) => target.writeFile(path, withQueryOperations(content));
516
+ }
517
+ const value = Reflect.get(target, key, target);
518
+ return typeof value === "function" ? value.bind(target) : value;
519
+ }
520
+ });
521
+ program.host = writing;
522
+ try {
523
+ await openapi3.$onEmit(context);
524
+ } finally {
525
+ program.host = host;
526
+ }
527
+ }
383
528
  // src/linter.ts
384
529
  import { defineLinter } from "@typespec/compiler";
385
530
 
@@ -516,8 +661,9 @@ export {
516
661
  $decorators,
517
662
  $lib,
518
663
  $linter,
664
+ $onEmit,
519
665
  $onValidate
520
666
  };
521
667
 
522
- //# debugId=B949BF3E72439B1464756E2164756E21
668
+ //# debugId=3B14A4A66C69185964756E2164756E21
523
669
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "version": 3,
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"],
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/emitter.ts", "../src/query-operations.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
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
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
+ "/** 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'}: @queryMethod marks a @post, which @nxgt/typespec writes as a QUERY in OpenAPI 3.2. Make it a @post.`,\n\t\t\t},\n\t\t},\n\t\t'query-method-needs-openapi-3.2': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} is marked @queryMethod: only OpenAPI 3.2 has a QUERY operation, and openapi-versions asks for ${'version'}. Emit 3.2 only, or take off @queryMethod.`,\n\t\t\t},\n\t\t},\n\t\t'query-method-needs-nxgt-emitter': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault: paramMessage`${'operation'} is marked @queryMethod: emit with @nxgt/typespec, which writes it as a QUERY; @typespec/openapi3 would write a POST.`,\n\t\t\t},\n\t\t},\n\t\t'openapi3-missing': {\n\t\t\tseverity: 'error',\n\t\t\tmessages: {\n\t\t\t\tdefault:\n\t\t\t\t\t'The emitter @nxgt/typespec runs @typespec/openapi3, which is not installed: add it beside @nxgt/typespec.',\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\t// `@typespec/openapi3` returns before writing anything on a dry run.\n\tcapabilities: { dryRun: true },\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",
8
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",
9
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",
10
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",
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",
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/** The operations marked with `@queryMethod`, each once. */\nexport function queryMethods(program: Program): Operation[] {\n\treturn [...program.stateSet($lib.stateKeys.queryMethod)] as Operation[];\n}\n\n/**\n * Each marked operation is a `POST`, as the emitter sends it, and is emitted\n * by `@nxgt/typespec`: `@typespec/openapi3` alone would write the `POST`.\n */\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\tif (!(program.compilerOptions.emit ?? []).includes('@typespec/openapi3')) {\n\t\treturn;\n\t}\n\tfor (const operation of queryMethods(program)) {\n\t\t$lib.reportDiagnostic(program, {\n\t\t\tcode: 'query-method-needs-nxgt-emitter',\n\t\t\tformat: { operation: operation.name },\n\t\t\ttarget: operation,\n\t\t});\n\t}\n}\n",
12
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",
13
+ "/**\n * The emitter `@nxgt/typespec`: `@typespec/openapi3`, with its options,\n * checked by its own schema, and its files, and each `POST` marked\n * `@queryMethod` a real `QUERY` in an OpenAPI 3.2 document\n * (`query-operations.ts`). Only 3.2 has a `QUERY`: a marked operation with\n * an older version asked for is an error, and nothing is emitted.\n * `@typespec/openapi3` is loaded only here, so a spec that only imports the\n * library does not need it.\n */\nimport {\n\ttype CompilerHost,\n\ttype Diagnostic,\n\ttype EmitContext,\n\tNoTarget,\n} from '@typespec/compiler';\nimport { $lib } from './lib';\nimport { queryMethods } from './query-method';\nimport { withQueryOperations } from './query-operations';\n\n/** `@typespec/openapi3`'s default, when `openapi-versions` is not set. */\nconst DEFAULT_VERSIONS = ['3.0.0'];\n\ntype OpenAPI3 = typeof import('@typespec/openapi3');\n\n/**\n * `@typespec/openapi3`'s options checked by its own schema, as the compiler\n * checks them under its name: `createTypeSpecLibrary` gives each library an\n * `emitterOptionValidator`, which its types leave out.\n */\nfunction invalidOptions(\n\topenapi3: OpenAPI3,\n\toptions: unknown,\n): readonly Diagnostic[] {\n\tconst lib: object = openapi3.$lib;\n\tconst validator =\n\t\t'emitterOptionValidator' in lib ? lib.emitterOptionValidator : undefined;\n\tif (\n\t\ttypeof validator !== 'object' ||\n\t\tvalidator === null ||\n\t\t!('validate' in validator) ||\n\t\ttypeof validator.validate !== 'function'\n\t) {\n\t\treturn [];\n\t}\n\treturn validator.validate(options, NoTarget);\n}\n\nasync function loadOpenAPI3(\n\tcontext: EmitContext,\n): Promise<OpenAPI3 | undefined> {\n\ttry {\n\t\treturn await import('@typespec/openapi3');\n\t} catch {\n\t\t$lib.reportDiagnostic(context.program, {\n\t\t\tcode: 'openapi3-missing',\n\t\t\ttarget: NoTarget,\n\t\t});\n\t\treturn undefined;\n\t}\n}\n\n/** Whether every version asked for has a `QUERY`; each marked operation is reported otherwise. */\nfunction queriesFit(context: EmitContext): boolean {\n\tconst { program } = context;\n\tconst marked = queryMethods(program);\n\tif (marked.length === 0) return true;\n\tconst versions =\n\t\t(context.options['openapi-versions'] as string[] | undefined) ??\n\t\tDEFAULT_VERSIONS;\n\tconst older = versions.find((version) => !version.startsWith('3.2'));\n\tif (older === undefined) return true;\n\tfor (const operation of marked) {\n\t\t$lib.reportDiagnostic(program, {\n\t\t\tcode: 'query-method-needs-openapi-3.2',\n\t\t\tformat: { operation: operation.name, version: older },\n\t\t\ttarget: operation,\n\t\t});\n\t}\n\treturn false;\n}\n\nexport async function $onEmit(context: EmitContext): Promise<void> {\n\tconst openapi3 = await loadOpenAPI3(context);\n\tif (openapi3 === undefined) return;\n\tconst { program } = context;\n\t// `@typespec/openapi3`'s schema: a typo or a bad value is refused, as\n\t// under its own name, instead of reaching it unchecked.\n\tconst invalid = invalidOptions(openapi3, context.options);\n\tif (invalid.length > 0) {\n\t\tprogram.reportDiagnostics(invalid);\n\t\treturn;\n\t}\n\tif (!queriesFit(context)) return;\n\t// Every file `@typespec/openapi3` writes goes through `program.host`: a\n\t// stand-in whose every call is the real host's, on the real host.\n\tconst host = program.host;\n\tconst writing = new Proxy(host, {\n\t\tget(target, key) {\n\t\t\tif (key === 'writeFile') {\n\t\t\t\treturn (path: string, content: string) =>\n\t\t\t\t\ttarget.writeFile(path, withQueryOperations(content));\n\t\t\t}\n\t\t\tconst value = Reflect.get(target, key, target);\n\t\t\treturn typeof value === 'function' ? value.bind(target) : value;\n\t\t},\n\t}) satisfies CompilerHost;\n\tprogram.host = writing;\n\ttry {\n\t\tawait openapi3.$onEmit(context as Parameters<OpenAPI3['$onEmit']>[0]);\n\t} finally {\n\t\tprogram.host = host;\n\t}\n}\n",
14
+ "/**\n * A `POST` marked `@queryMethod` made the `QUERY` it is, in an emitted\n * OpenAPI 3.2 document: its operation moves from `post` to `query` and loses\n * `x-nxgt-method`, which says nothing more there. A 3.1 document has no\n * `query` operation and keeps the `POST` and the mark. The text is written\n * again as `@typespec/openapi3` writes it, and left alone when nothing moves.\n */\nimport { parse, stringify } from 'yaml';\n\ntype PathItem = Record<string, unknown>;\n\n/** Moves each marked `post` of a 3.2 document to `query`; whether any moved. */\nfunction moveQueries(document: Record<string, unknown>): boolean {\n\tif (!String(document['openapi']).startsWith('3.2')) return false;\n\tconst paths = document['paths'];\n\tif (typeof paths !== 'object' || paths === null) return false;\n\tlet moved = false;\n\tfor (const [path, item] of Object.entries(\n\t\tpaths as Record<string, PathItem>,\n\t)) {\n\t\tconst post = item['post'] as Record<string, unknown> | undefined;\n\t\tif (post?.['x-nxgt-method'] !== 'query' || item['query'] !== undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst { 'x-nxgt-method': _, ...operation } = post;\n\t\t// In place of the `post`, so the path item keeps its order.\n\t\t(paths as Record<string, PathItem>)[path] = Object.fromEntries(\n\t\t\tObject.entries(item).map(([key, value]) =>\n\t\t\t\tkey === 'post' ? ['query', operation] : [key, value],\n\t\t\t),\n\t\t);\n\t\tmoved = true;\n\t}\n\treturn moved;\n}\n\n/**\n * The document's text with its queries moved. The format is the text's:\n * `@typespec/openapi3` writes YAML whatever the file is named, unless\n * `file-type` says JSON. Line endings stay as written: `new-line: crlf`\n * reaches here already applied.\n */\nexport function withQueryOperations(content: string): string {\n\tconst json = content.trimStart().startsWith('{');\n\tconst document = (json ? JSON.parse(content) : parse(content)) as unknown;\n\tif (typeof document !== 'object' || document === null) return content;\n\tif (!moveQueries(document as Record<string, unknown>)) return content;\n\tconst text = json\n\t\t? `${JSON.stringify(document, null, 2)}\\n`\n\t\t: // `@typespec/openapi3`'s own options, so the rest of the text is unchanged.\n\t\t\tstringify(document, {\n\t\t\t\tsingleQuote: true,\n\t\t\t\taliasDuplicateObjects: false,\n\t\t\t\tlineWidth: 0,\n\t\t\t\tcompat: 'yaml-1.1',\n\t\t\t});\n\treturn content.includes('\\r\\n') ? text.replace(/\\n/g, '\\r\\n') : text;\n}\n",
13
15
  "/**\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",
14
16
  "/**\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",
15
17
  "/**\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",
16
18
  "/**\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",
17
19
  "/**\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",
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"
20
+ "/**\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 { $onEmit } from './emitter';\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"
19
21
  ],
20
- "mappings": "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",
21
- "debugId": "B949BF3E72439B1464756E2164756E21",
22
+ "mappings": "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",
23
+ "debugId": "3B14A4A66C69185964756E2164756E21",
22
24
  "names": []
23
25
  }
package/dist/lib.d.ts CHANGED
@@ -11,6 +11,15 @@ export declare const $lib: import("@typespec/compiler").TypeSpecLibrary<{
11
11
  'query-method-not-post': {
12
12
  readonly default: import("@typespec/compiler").CallableMessage<["operation", "method"]>;
13
13
  };
14
+ 'query-method-needs-openapi-3.2': {
15
+ readonly default: import("@typespec/compiler").CallableMessage<["operation", "version"]>;
16
+ };
17
+ 'query-method-needs-nxgt-emitter': {
18
+ readonly default: import("@typespec/compiler").CallableMessage<["operation"]>;
19
+ };
20
+ 'openapi3-missing': {
21
+ readonly default: "The emitter @nxgt/typespec runs @typespec/openapi3, which is not installed: add it beside @nxgt/typespec.";
22
+ };
14
23
  'duplicate-status-reply': {
15
24
  readonly default: import("@typespec/compiler").CallableMessage<["operation", "status"]>;
16
25
  };
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;;;;;;;;;;;;;;;;;;;+EAqDf,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;+EA0Ef,CAAC"}
@@ -8,6 +8,11 @@ import type { DecoratorContext, Operation, Program } from '@typespec/compiler';
8
8
  export declare function queryMethod(context: DecoratorContext, target: Operation): void;
9
9
  /** Marked with `@queryMethod`. */
10
10
  export declare function isQueryMethod(program: Program, operation: Operation): boolean;
11
- /** Each marked operation is a `POST`, as the emitter sends it. */
11
+ /** The operations marked with `@queryMethod`, each once. */
12
+ export declare function queryMethods(program: Program): Operation[];
13
+ /**
14
+ * Each marked operation is a `POST`, as the emitter sends it, and is emitted
15
+ * by `@nxgt/typespec`: `@typespec/openapi3` alone would write the `POST`.
16
+ */
12
17
  export declare function validateQueryMethods(program: Program): void;
13
18
  //# sourceMappingURL=query-method.d.ts.map
@@ -1 +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
+ {"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,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,CAE1D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmB3D"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The document's text with its queries moved. The format is the text's:
3
+ * `@typespec/openapi3` writes YAML whatever the file is named, unless
4
+ * `file-type` says JSON. Line endings stay as written: `new-line: crlf`
5
+ * reaches here already applied.
6
+ */
7
+ export declare function withQueryOperations(content: string): string;
8
+ //# sourceMappingURL=query-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-operations.d.ts","sourceRoot":"","sources":["../src/query-operations.ts"],"names":[],"mappings":"AAoCA;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAe3D"}
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
+ | [The emitter](guide/emitter.md) | emitting the spec with `@nxgt/typespec` in `tspconfig.yaml`, or getting a real `QUERY` from a `@queryMethod`, which needs OpenAPI 3.2 |
8
9
  | [Request bodies](guide/requests.md) | naming the body of a create, a `PUT` and a `PATCH`: `CreatePostRequest`, `UpdatePostRequest`, `PatchPostRequest` |
9
10
  | [Error replies](guide/errors.md) | declaring what an operation answers when it fails |
10
11
  | [Pagination](guide/pagination.md) | listing a collection a page at a time |
@@ -0,0 +1,159 @@
1
+ # The emitter
2
+
3
+ `@nxgt/typespec` is a TypeSpec emitter as well as a library: it compiles a
4
+ spec to OpenAPI with `@typespec/openapi3`, and writes each `@queryMethod` as a
5
+ real `QUERY` when the document is OpenAPI 3.2.
6
+
7
+ ## The smallest setup
8
+
9
+ Install the optional peer the emitter runs, then emit with this package in
10
+ place of `@typespec/openapi3`:
11
+
12
+ ```sh
13
+ bun add -d @nxgt/typespec @typespec/openapi3
14
+ ```
15
+
16
+ ```yaml
17
+ # api/tspconfig.yaml
18
+ emit:
19
+ - '@nxgt/typespec'
20
+ options:
21
+ '@nxgt/typespec':
22
+ openapi-versions: ['3.2.0']
23
+ emitter-output-dir: '{project-root}/../openapi'
24
+ output-file: openapi.yaml
25
+ ```
26
+
27
+ ```tsp
28
+ // api/main.tsp
29
+ import "@typespec/http";
30
+ import "@nxgt/typespec";
31
+
32
+ using Http;
33
+ using Nxgt;
34
+
35
+ @service(#{ title: "Library" })
36
+ @operationIds
37
+ namespace Library;
38
+
39
+ model BookCriteria {
40
+ titles: string[];
41
+ }
42
+
43
+ @route("/books")
44
+ interface Books {
45
+ @post @queryMethod @route("/search")
46
+ search(@body criteria: BookCriteria): string[]; // searchBooks
47
+ }
48
+ ```
49
+
50
+ `tsp compile api` writes `openapi.yaml`. The `tsp init` template already emits
51
+ with this emitter, with OpenAPI 3.1 by default and no `@queryMethod`; using
52
+ one requires `openapi-versions: ["3.2.0"]`
53
+ ([Getting started](getting-started.md)).
54
+
55
+ ## Why an emitter
56
+
57
+ `@typespec/http` 1.17 declares no `QUERY`, so a spec writes a `@post` and
58
+ `@queryMethod` marks it. `@typespec/openapi3` can only emit a `post`. The
59
+ emitter finishes the job the mark started: in OpenAPI 3.2, which has a
60
+ `query` operation, the document says `query`.
61
+
62
+ ## What it does
63
+
64
+ It runs `@typespec/openapi3` with the options it was given, the same ones
65
+ under the same names (`openapi-versions`, `emitter-output-dir`,
66
+ `output-file`, `file-type`…), and writes the same files. Then, in each
67
+ OpenAPI 3.2 document, each marked `post` becomes a
68
+ `query` operation, in place in its path item and without `x-nxgt-method`:
69
+
70
+ ```yaml
71
+ # openapi.yaml
72
+ /books/search:
73
+ query:
74
+ operationId: searchBooks
75
+ ```
76
+
77
+ | Case | Result |
78
+ | --- | --- |
79
+ | a marked `@post`, `openapi-versions: ['3.2.0']` | a `query` operation |
80
+ | a marked `@post`, any version below 3.2 in `openapi-versions`, or none (the default, 3.0.0) | the error `query-method-needs-openapi-3.2` on each marked operation; nothing is emitted |
81
+ | a marked `@post`, `@typespec/openapi3` in `emit` | the error `query-method-needs-nxgt-emitter` |
82
+ | no marked operation | byte-identical to `@typespec/openapi3`'s, in any version |
83
+
84
+ Both errors, with their fix, are in [troubleshooting](../troubleshooting.md).
85
+ A path item that already has a `query` is left alone. The emitter adds no
86
+ option of its own.
87
+
88
+ Its options are checked with `@typespec/openapi3`'s schema, so a typo is
89
+ `invalid-schema`, as under `@typespec/openapi3`. `tsp compile --dry-run`
90
+ works, `new-line: crlf` is kept, and the document's format (YAML or JSON) is
91
+ read from its text, not from the file name.
92
+
93
+ **The default output folder changes with the emitter.** With
94
+ `emitter-output-dir` unset, the files go to `tsp-output/@nxgt/typespec`,
95
+ where `@typespec/openapi3` wrote `tsp-output/@typespec/openapi3`. Set
96
+ `emitter-output-dir` (the template does) to keep a path.
97
+
98
+ `@typespec/openapi3` is loaded only when emitting, so a spec that only
99
+ imports the library, `tsp compile --no-emit`, does not need it installed.
100
+
101
+ ## What follows downstream
102
+
103
+ | Step | What it does with the `query` operation |
104
+ | --- | --- |
105
+ | `@nxgt/openapi-codegen` | generates `method: 'query'` in the `operations` table |
106
+ | `@nxgt/openapi-hono` | serves it with `.query(path, handler)` |
107
+ | `@nxgt/httpyz` client | sends `QUERY`; its `cache()` and retries treat it like a `GET`, keyed by its body |
108
+
109
+ A hand-written OpenAPI document may still carry a `post` with
110
+ `x-nxgt-method: query`, which the generator marks `queryMethod: true`; a
111
+ TypeSpec spec no longer emits one.
112
+
113
+ The handler of the 3.2 case, wired as the fixture's spec does:
114
+
115
+ ```ts
116
+ import { Hono } from 'hono';
117
+ import { createRoutes } from './generated/hono';
118
+
119
+ const app = new Hono();
120
+ createRoutes(app, { validateResponses: true }).query('/books/search', (c) =>
121
+ c.json(
122
+ {
123
+ items: [],
124
+ total: c.req.valid('json').titles.length,
125
+ page: 1,
126
+ pageSize: 20,
127
+ pageCount: 0,
128
+ },
129
+ 200,
130
+ ),
131
+ );
132
+
133
+ const reply = await app.request('/books/search', {
134
+ method: 'QUERY',
135
+ body: JSON.stringify({ titles: ['Dune', 'Emma'] }),
136
+ headers: { 'content-type': 'application/json' },
137
+ });
138
+ ```
139
+
140
+ ## A QUERY on the wire
141
+
142
+ A real `QUERY` is not a `POST`, so the path from the browser to the handler
143
+ has to accept the method:
144
+
145
+ - **The server's HTTP stack.** Recent Node and Bun accept `QUERY`, and Hono
146
+ routes it. Check the runtime and any framework or adapter between the
147
+ socket and Hono.
148
+ - **Proxies, gateways and CDNs.** Some refuse a method they do not know, or
149
+ do not cache it. If one in front of the service does, take off
150
+ `@queryMethod` and use a `@post`, which goes through everything.
151
+ - **CORS.** `QUERY` is not a CORS-safelisted method, so a browser always sends
152
+ a preflight, and the server must list `QUERY` in `Access-Control-Allow-Methods`.
153
+
154
+ ## Without the emitter
155
+
156
+ The library, its decorators and the linter do not depend on the emitter, but
157
+ a spec that uses `@queryMethod` must emit with it: `@typespec/openapi3` alone
158
+ reports `query-method-needs-nxgt-emitter`. More on the decorator, its checks
159
+ and its errors, in [Operation ids](operation-ids.md#a-query-in-openapi-32).
@@ -8,9 +8,9 @@ spec names them instead of describing them again.
8
8
  ```yaml
9
9
  # api/tspconfig.yaml
10
10
  emit:
11
- - '@typespec/openapi3'
11
+ - '@nxgt/typespec'
12
12
  options:
13
- '@typespec/openapi3':
13
+ '@nxgt/typespec':
14
14
  openapi-versions: ['3.1.0']
15
15
  emitter-output-dir: '{project-root}/../openapi'
16
16
  output-file: openapi.yaml
@@ -38,14 +38,18 @@ network, fails before anything is written
38
38
  | File | What it holds |
39
39
  | --- | --- |
40
40
  | `package.json` | the compiler, `@typespec/http`, `@typespec/openapi`, `@typespec/openapi3`, `@nxgt/typespec` and `@nxgt/openapi-codegen`, at their latest versions |
41
- | `tspconfig.yaml` | the linter's recommended rules, and OpenAPI 3.1 written to `openapi/openapi.yaml` |
41
+ | `tspconfig.yaml` | the linter's recommended rules, and OpenAPI 3.1 written to `openapi/openapi.yaml` by the emitter `@nxgt/typespec` |
42
42
  | `main.tsp` | the service, marked with `@operationIds`, and a `Users` resource |
43
43
  | `openapi-codegen.config.ts` | the generator's input and output, with the Hono routes on |
44
44
  | `README.md` | the commands below, and the scripts to add |
45
45
  | `.gitignore` | `node_modules/`, and TypeSpec's default output |
46
46
 
47
- `tspconfig.yaml` turns the linter on and emits OpenAPI 3.1, which
48
- `@nxgt/openapi-codegen` reads (it refuses 3.0, the emitter's default):
47
+ `tspconfig.yaml` turns the linter on and emits OpenAPI 3.1 with
48
+ `@nxgt/typespec`, which runs `@typespec/openapi3` and reads the same options.
49
+ `@nxgt/openapi-codegen` reads 3.1 (it refuses 3.0, the default of
50
+ `@typespec/openapi3`). The template has no `@queryMethod`; using one requires
51
+ `openapi-versions: ["3.2.0"]`, as 3.1 is an error beside it
52
+ ([The emitter](emitter.md)):
49
53
 
50
54
  ```yaml
51
55
  # tspconfig.yaml
@@ -53,9 +57,9 @@ linter:
53
57
  extends:
54
58
  - "@nxgt/typespec/recommended"
55
59
  emit:
56
- - "@typespec/openapi3"
60
+ - "@nxgt/typespec"
57
61
  options:
58
- "@typespec/openapi3":
62
+ "@nxgt/typespec":
59
63
  emitter-output-dir: "{project-root}/openapi"
60
64
  openapi-versions:
61
65
  - 3.1.0
@@ -11,12 +11,12 @@ Extend the ruleset in `tspconfig.yaml`, beside the emitter:
11
11
  ```yaml
12
12
  # api/tspconfig.yaml
13
13
  emit:
14
- - '@typespec/openapi3'
14
+ - '@nxgt/typespec'
15
15
  linter:
16
16
  extends:
17
17
  - '@nxgt/typespec/recommended'
18
18
  options:
19
- '@typespec/openapi3':
19
+ '@nxgt/typespec':
20
20
  openapi-versions: ['3.1.0']
21
21
  ```
22
22
 
@@ -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`, `POST`, or a `POST` marked [`@queryMethod`](#a-query-sent-as-a-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,7 +171,7 @@ Not checked:
171
171
  whatever it says;
172
172
  - an operation `@operationIds` does not name.
173
173
 
174
- ## A QUERY sent as a POST
174
+ ## A QUERY in OpenAPI 3.2
175
175
 
176
176
  `QUERY` is the HTTP method for a safe request with a body, and
177
177
  `@typespec/http` 1.17 declares only `GET`, `PUT`, `POST`, `PATCH`, `DELETE`
@@ -199,21 +199,24 @@ interface Users {
199
199
  }
200
200
  ```
201
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:
202
+ With the emitter `@nxgt/typespec` and OpenAPI 3.2 the document gets a real
203
+ `QUERY`, the only version that has one:
204
204
 
205
205
  ```yaml
206
206
  /users/search:
207
- post:
207
+ query:
208
208
  operationId: searchUsers
209
- x-nxgt-method: query
210
209
  ```
211
210
 
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)).
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)).
217
220
 
218
221
  | Case | Result |
219
222
  | --- | --- |
@@ -223,12 +226,12 @@ spec ([roadmap](../roadmap.md#later)).
223
226
  | marked and not a `@post` | the error `query-method-not-post` |
224
227
 
225
228
  ```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.
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.
227
230
  ```
228
231
 
229
232
  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`).
233
+ return a `Page<Item>`, as `search` above could
234
+ (`Page<User> | BadRequest`).
232
235
 
233
236
  ## The resource
234
237
 
package/docs/roadmap.md CHANGED
@@ -16,22 +16,33 @@ Nothing in progress.
16
16
 
17
17
  ## Later
18
18
 
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.
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
+
29
40
  ### 0.12.0
30
41
 
31
42
  - **`@queryMethod`, a `QUERY` sent as a `POST`.** The decorator marks a
32
43
  `@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
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
35
46
  `query-method-not-post`.
36
47
 
37
48
  ### 0.11.0
@@ -293,7 +293,7 @@ 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.`
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
297
 
298
298
  **When:** an error, `query-method-not-post`: an operation marked
299
299
  `@queryMethod` is sent with a method other than `POST`. The method may be
@@ -306,9 +306,10 @@ interface Users {
306
306
  }
307
307
  ```
308
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.
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.
312
313
 
313
314
  **Fix:** make it a `@post`, with the criteria in the body:
314
315
 
@@ -317,7 +318,62 @@ has nothing to stand for.
317
318
  ```
318
319
 
319
320
  Or drop `@queryMethod`: a `GET` needs no mark. More, in
320
- [Operation ids](guide/operation-ids.md#a-query-sent-as-a-post).
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
+ ```
321
377
 
322
378
  ## `findById is sent with QUERY, where its verb find is sent with GET or HEAD`
323
379
 
@@ -642,7 +698,9 @@ using OpenAPI;
642
698
 
643
699
  **When:** you generate from what `tsp compile` emitted.
644
700
 
645
- **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.
646
703
 
647
704
  **Fix:** in `tspconfig.yaml`, set `openapi-versions: ['3.1.0']` or
648
- `['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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/typespec",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,14 +45,23 @@
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.9.0",
64
+ "@nxgt/openapi-codegen": "^0.9.1",
56
65
  "@nxgt/openapi-hono": "^0.3.2",
57
66
  "@types/bun": "^1.4.0",
58
67
  "@typespec/compiler": "1.17.0",
@@ -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"],