@nxgt/typespec 0.7.0 → 0.8.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 +41 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +123 -1
- package/dist/index.js.map +10 -5
- package/dist/linter.d.ts +2 -0
- package/dist/linter.d.ts.map +1 -0
- package/dist/operation-ids.d.ts +9 -0
- package/dist/operation-ids.d.ts.map +1 -1
- package/dist/rules/error-body-shape.d.ts +4 -0
- package/dist/rules/error-body-shape.d.ts.map +1 -0
- package/dist/rules/list-returns-page.d.ts +4 -0
- package/dist/rules/list-returns-page.d.ts.map +1 -0
- package/dist/rules/service-operation-ids.d.ts +4 -0
- package/dist/rules/service-operation-ids.d.ts.map +1 -0
- package/dist/rules/written.d.ts +8 -0
- package/dist/rules/written.d.ts.map +1 -0
- package/docs/README.md +1 -0
- package/docs/guide/linter.md +257 -0
- package/docs/roadmap.md +13 -4
- package/docs/troubleshooting.md +133 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -381,6 +381,34 @@ The verbs `verbs` adds are not checked, nor an operation with its own
|
|
|
381
381
|
`@operationId`. More, with each verb's methods, in
|
|
382
382
|
[Operation ids](docs/guide/operation-ids.md).
|
|
383
383
|
|
|
384
|
+
### Linter
|
|
385
|
+
|
|
386
|
+
Extend the library's ruleset in `tspconfig.yaml`, and `tsp compile` warns
|
|
387
|
+
where the spec strays from these conventions. Nothing runs without it:
|
|
388
|
+
|
|
389
|
+
```yaml
|
|
390
|
+
# api/tspconfig.yaml
|
|
391
|
+
linter:
|
|
392
|
+
extends:
|
|
393
|
+
- '@nxgt/typespec/recommended'
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Rule | Warns when | Fix |
|
|
397
|
+
| --- | --- | --- |
|
|
398
|
+
| `list-returns-page` | an operation named `list`, `search` or `query`, alone or before a capital letter (`listUsers`, not `listing`), answers an array | return `Page<Item>` or `CursorPage<Item>` |
|
|
399
|
+
| `service-operation-ids` | a `@service` namespace has no `@operationIds`, on itself or on a namespace around it | mark the namespace with `@operationIds` |
|
|
400
|
+
| `error-body-shape` | a reply of status 400 or above, or `default`, has a body without `status`, `message` and `timestamp` | declare one of the library's errors, or a body that spreads or extends `ErrorBody<Status>` |
|
|
401
|
+
|
|
402
|
+
```text
|
|
403
|
+
warning @nxgt/typespec/list-returns-page: listAll returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
A reply without a body, such as `AuthenticationRequired`, passes
|
|
407
|
+
`error-body-shape`. Turn a rule off for the spec under `linter: disable:`
|
|
408
|
+
with a reason, or for one operation with
|
|
409
|
+
`#suppress "@nxgt/typespec/<rule>" "<reason>"`. More, with a failing and a
|
|
410
|
+
passing example of each rule, in [Linter](docs/guide/linter.md).
|
|
411
|
+
|
|
384
412
|
## API
|
|
385
413
|
|
|
386
414
|
### Decorators
|
|
@@ -403,6 +431,17 @@ overrides verbs, on a namespace or an interface.
|
|
|
403
431
|
| `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 |
|
|
404
432
|
| `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` |
|
|
405
433
|
|
|
434
|
+
### Linter rules
|
|
435
|
+
|
|
436
|
+
In the ruleset `@nxgt/typespec/recommended`; each is a warning, and named
|
|
437
|
+
`@nxgt/typespec/<rule>` in `tspconfig.yaml` and `#suppress`.
|
|
438
|
+
|
|
439
|
+
| Rule | Reported when |
|
|
440
|
+
| --- | --- |
|
|
441
|
+
| `list-returns-page` | an operation named `list`, `search` or `query`, alone or before a capital letter, has a success reply whose body is an array |
|
|
442
|
+
| `service-operation-ids` | a `@service` namespace is not marked with `@operationIds`, itself or by a namespace around it |
|
|
443
|
+
| `error-body-shape` | a reply of status 400 or above, of a range from 400 up, or `default`, has a body that is not a model with `status`, `message` and `timestamp` |
|
|
444
|
+
|
|
406
445
|
### Models
|
|
407
446
|
|
|
408
447
|
| Model | What it is |
|
|
@@ -546,6 +585,8 @@ envelope and the rate limit's headers.
|
|
|
546
585
|
- [Operation ids](docs/guide/operation-ids.md): `@operationIds`, each
|
|
547
586
|
operation named as written or a verb with its resource, the method each
|
|
548
587
|
verb is sent with, the options, and the ids it refuses;
|
|
588
|
+
- [Linter](docs/guide/linter.md): the ruleset `@nxgt/typespec/recommended`,
|
|
589
|
+
each rule with a failing and a passing spec, and how to turn one off;
|
|
549
590
|
- [troubleshooting](docs/troubleshooting.md);
|
|
550
591
|
- [the roadmap](docs/roadmap.md): what is coming.
|
|
551
592
|
|
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
|
export { $lib } from './lib';
|
|
12
|
+
export { $linter } from './linter';
|
|
12
13
|
export declare function $onValidate(program: Program): void;
|
|
13
14
|
export declare const $decorators: {
|
|
14
15
|
Nxgt: {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -351,6 +351,127 @@ function validateVerbMethods(program) {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
|
+
// src/linter.ts
|
|
355
|
+
import { defineLinter } from "@typespec/compiler";
|
|
356
|
+
|
|
357
|
+
// src/rules/error-body-shape.ts
|
|
358
|
+
import {
|
|
359
|
+
createRule,
|
|
360
|
+
isVoidType as isVoidType2,
|
|
361
|
+
paramMessage as paramMessage2,
|
|
362
|
+
walkPropertiesInherited
|
|
363
|
+
} from "@typespec/compiler";
|
|
364
|
+
import { getHttpOperation } from "@typespec/http";
|
|
365
|
+
|
|
366
|
+
// src/rules/written.ts
|
|
367
|
+
import { isTemplateDeclaration } from "@typespec/compiler";
|
|
368
|
+
function isWritten(operation) {
|
|
369
|
+
const container = operation.interface;
|
|
370
|
+
return isEmitted(operation) && !isTemplateDeclaration(operation) && (container === undefined || !isTemplateDeclaration(container));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/rules/error-body-shape.ts
|
|
374
|
+
var ENVELOPE = ["status", "message", "timestamp"];
|
|
375
|
+
function isEnvelope(body) {
|
|
376
|
+
const names = new Set([...walkPropertiesInherited(body)].map(({ name }) => name));
|
|
377
|
+
return ENVELOPE.every((name) => names.has(name));
|
|
378
|
+
}
|
|
379
|
+
function statusOf2(code) {
|
|
380
|
+
return typeof code === "object" ? `${code.start}-${code.end}` : `${code}`;
|
|
381
|
+
}
|
|
382
|
+
var errorBodyShape = createRule({
|
|
383
|
+
name: "error-body-shape",
|
|
384
|
+
severity: "warning",
|
|
385
|
+
description: "An error reply has the nxgt envelope: status, message and timestamp.",
|
|
386
|
+
messages: {
|
|
387
|
+
default: paramMessage2`${"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>.`
|
|
388
|
+
},
|
|
389
|
+
create: (context) => ({
|
|
390
|
+
operation(operation) {
|
|
391
|
+
if (!isWritten(operation))
|
|
392
|
+
return;
|
|
393
|
+
const [http] = getHttpOperation(context.program, operation);
|
|
394
|
+
for (const { statusCodes, responses } of http.responses) {
|
|
395
|
+
const error = statusCodes === "*" || (typeof statusCodes === "number" ? statusCodes >= 400 : statusCodes.start >= 400);
|
|
396
|
+
if (!error)
|
|
397
|
+
continue;
|
|
398
|
+
const wrong = responses.some(({ body }) => {
|
|
399
|
+
if (body === undefined || isVoidType2(body.type))
|
|
400
|
+
return false;
|
|
401
|
+
return body.type.kind !== "Model" || !isEnvelope(body.type);
|
|
402
|
+
});
|
|
403
|
+
if (!wrong)
|
|
404
|
+
continue;
|
|
405
|
+
context.reportDiagnostic({
|
|
406
|
+
format: { operation: operation.name, status: statusOf2(statusCodes) },
|
|
407
|
+
target: operation
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// src/rules/list-returns-page.ts
|
|
415
|
+
import { createRule as createRule2, isArrayModelType, paramMessage as paramMessage3 } from "@typespec/compiler";
|
|
416
|
+
import { getHttpOperation as getHttpOperation2 } from "@typespec/http";
|
|
417
|
+
var LIST = /^(list|search|query)(?=[A-Z]|$)/;
|
|
418
|
+
var listReturnsPage = createRule2({
|
|
419
|
+
name: "list-returns-page",
|
|
420
|
+
severity: "warning",
|
|
421
|
+
description: "A list, search or query returns Page<Item> or CursorPage<Item>.",
|
|
422
|
+
messages: {
|
|
423
|
+
default: paramMessage3`${"operation"} returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.`
|
|
424
|
+
},
|
|
425
|
+
create: (context) => ({
|
|
426
|
+
operation(operation) {
|
|
427
|
+
if (!isWritten(operation) || !LIST.test(operation.name))
|
|
428
|
+
return;
|
|
429
|
+
const [http] = getHttpOperation2(context.program, operation);
|
|
430
|
+
const array = http.responses.some(({ statusCodes, responses }) => typeof statusCodes === "number" && statusCodes >= 200 && statusCodes < 300 && responses.some(({ body }) => body?.type.kind === "Model" && isArrayModelType(body.type)));
|
|
431
|
+
if (!array)
|
|
432
|
+
return;
|
|
433
|
+
context.reportDiagnostic({
|
|
434
|
+
format: { operation: operation.name },
|
|
435
|
+
target: operation
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// src/rules/service-operation-ids.ts
|
|
442
|
+
import { createRule as createRule3, isService as isService2, paramMessage as paramMessage4 } from "@typespec/compiler";
|
|
443
|
+
var serviceOperationIds = createRule3({
|
|
444
|
+
name: "service-operation-ids",
|
|
445
|
+
severity: "warning",
|
|
446
|
+
description: "A service namespace marks its operation ids with @operationIds.",
|
|
447
|
+
messages: {
|
|
448
|
+
default: paramMessage4`The service ${"service"} has no @operationIds: its ids are the emitter's, such as Users_list. Mark the namespace with @operationIds.`
|
|
449
|
+
},
|
|
450
|
+
create: (context) => ({
|
|
451
|
+
namespace(namespace) {
|
|
452
|
+
const { program } = context;
|
|
453
|
+
if (!isService2(program, namespace))
|
|
454
|
+
return;
|
|
455
|
+
if (isMarkedNamespace(program, namespace))
|
|
456
|
+
return;
|
|
457
|
+
context.reportDiagnostic({
|
|
458
|
+
format: { service: namespace.name },
|
|
459
|
+
target: namespace
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
})
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// src/linter.ts
|
|
466
|
+
var rules = [listReturnsPage, serviceOperationIds, errorBodyShape];
|
|
467
|
+
var $linter = defineLinter({
|
|
468
|
+
rules,
|
|
469
|
+
ruleSets: {
|
|
470
|
+
recommended: {
|
|
471
|
+
enable: Object.fromEntries(rules.map(({ name }) => [`@nxgt/typespec/${name}`, true]))
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
});
|
|
354
475
|
|
|
355
476
|
// src/index.ts
|
|
356
477
|
function $onValidate(program) {
|
|
@@ -364,8 +485,9 @@ var $decorators = {
|
|
|
364
485
|
export {
|
|
365
486
|
$decorators,
|
|
366
487
|
$lib,
|
|
488
|
+
$linter,
|
|
367
489
|
$onValidate
|
|
368
490
|
};
|
|
369
491
|
|
|
370
|
-
//# debugId=
|
|
492
|
+
//# debugId=FB766460AB7EEFAD64756E2164756E21
|
|
371
493
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
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/index.ts"],
|
|
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"],
|
|
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 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
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",
|
|
7
|
-
"/**\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. */\
|
|
7
|
+
"/**\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
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
9
|
"/**\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
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 * The
|
|
11
|
+
"/**\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
|
+
"/**\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
|
+
"/**\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
|
+
"/**\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
|
+
"/**\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"
|
|
12
17
|
],
|
|
13
|
-
"mappings": "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",
|
|
14
|
-
"debugId": "
|
|
18
|
+
"mappings": "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",
|
|
19
|
+
"debugId": "FB766460AB7EEFAD64756E2164756E21",
|
|
15
20
|
"names": []
|
|
16
21
|
}
|
package/dist/linter.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,OAAO,+CASlB,CAAC"}
|
package/dist/operation-ids.d.ts
CHANGED
|
@@ -13,12 +13,21 @@ import { type OperationIdsOptions } from './resource';
|
|
|
13
13
|
* has no resource to name: `singular` and `plural` are an interface's.
|
|
14
14
|
*/
|
|
15
15
|
export declare function operationIds(context: DecoratorContext, target: Interface | Namespace, options?: OperationIdsOptions): void;
|
|
16
|
+
/** Marked itself, or in a marked namespace, however deep. */
|
|
17
|
+
export declare function isMarkedNamespace(program: Program, target: Namespace | undefined): boolean;
|
|
16
18
|
/**
|
|
17
19
|
* Whether `@operationIds` names the operation's id, or checks it: its
|
|
18
20
|
* interface or one of its namespaces is marked. An interface template's
|
|
19
21
|
* instance is never emitted; only the interfaces extending it are.
|
|
20
22
|
*/
|
|
21
23
|
export declare function isNamed(program: Program, operation: Operation): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the emitter writes the operation: not an instance of an operation
|
|
26
|
+
* template, `Read<Pet>`, nor an operation of an interface template's
|
|
27
|
+
* instance. An interface extending an instance copies its operations, which
|
|
28
|
+
* are instances too, and emitted.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isEmitted(operation: Operation): boolean;
|
|
22
31
|
/**
|
|
23
32
|
* Sets the ids, after every decorator, so the order of `@operationId` and
|
|
24
33
|
* `@operationIds` in the source does not matter. Two operations with one id
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-ids.d.ts","sourceRoot":"","sources":["../src/operation-ids.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAId,KAAK,SAAS,EAEd,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,OAAO,GAAE,mBAAwB,GAC/B,IAAI,CAaN;
|
|
1
|
+
{"version":3,"file":"operation-ids.d.ts","sourceRoot":"","sources":["../src/operation-ids.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAId,KAAK,SAAS,EAEd,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,OAAO,GAAE,mBAAwB,GAC/B,IAAI,CAaN;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,GAAG,SAAS,GAC3B,OAAO,CAKT;AAUD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAUvE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAIvD;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqC3D"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const errorBodyShape: import("@typespec/compiler").LinterRuleDefinition<"error-body-shape", {
|
|
2
|
+
readonly default: import("@typespec/compiler").CallableMessage<["operation", "status"]>;
|
|
3
|
+
}, Record<string, never>>;
|
|
4
|
+
//# sourceMappingURL=error-body-shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-body-shape.d.ts","sourceRoot":"","sources":["../../src/rules/error-body-shape.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,cAAc;;yBA+BzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-returns-page.d.ts","sourceRoot":"","sources":["../../src/rules/list-returns-page.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,eAAe;;yBA6B1B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const serviceOperationIds: import("@typespec/compiler").LinterRuleDefinition<"service-operation-ids", {
|
|
2
|
+
readonly default: import("@typespec/compiler").CallableMessage<["service"]>;
|
|
3
|
+
}, Record<string, never>>;
|
|
4
|
+
//# sourceMappingURL=service-operation-ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-operation-ids.d.ts","sourceRoot":"","sources":["../../src/rules/service-operation-ids.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB;;yBAmB9B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the emitter writes an operation: not a template's declaration,
|
|
3
|
+
* nor its instance (`Read<Pet>`), nor an operation of an interface
|
|
4
|
+
* template's. Only the operations declared from them are checked.
|
|
5
|
+
*/
|
|
6
|
+
import { type Operation } from '@typespec/compiler';
|
|
7
|
+
export declare function isWritten(operation: Operation): boolean;
|
|
8
|
+
//# sourceMappingURL=written.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"written.d.ts","sourceRoot":"","sources":["../../src/rules/written.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG3E,wBAAgB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAOvD"}
|
package/docs/README.md
CHANGED
|
@@ -11,5 +11,6 @@ The [package README](../README.md) is the short version.
|
|
|
11
11
|
| [Headers](guide/headers.md) | making a write idempotent, or declaring a rate limit |
|
|
12
12
|
| [Sorting](guide/sorting.md) | letting a client filter and sort a list |
|
|
13
13
|
| [Operation ids](guide/operation-ids.md) | naming each operation, and so the generated client's method: `list` in `Users` as `listUsers`, any other name as written |
|
|
14
|
+
| [Linter](guide/linter.md) | checking a spec against these conventions in `tsp compile`, or turning one rule off |
|
|
14
15
|
| [Troubleshooting](troubleshooting.md) | the compiler or the generator refused the spec |
|
|
15
16
|
| [Roadmap](roadmap.md) | wondering what is coming |
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Linter
|
|
2
|
+
|
|
3
|
+
The linter warns when a spec strays from the conventions the library gives
|
|
4
|
+
it: a list that returns an array, a service without `@operationIds`, an error
|
|
5
|
+
reply without the envelope.
|
|
6
|
+
|
|
7
|
+
## Turn it on
|
|
8
|
+
|
|
9
|
+
Extend the ruleset in `tspconfig.yaml`, beside the emitter:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
# api/tspconfig.yaml
|
|
13
|
+
emit:
|
|
14
|
+
- '@typespec/openapi3'
|
|
15
|
+
linter:
|
|
16
|
+
extends:
|
|
17
|
+
- '@nxgt/typespec/recommended'
|
|
18
|
+
options:
|
|
19
|
+
'@typespec/openapi3':
|
|
20
|
+
openapi-versions: ['3.1.0']
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Nothing runs without it: importing `@nxgt/typespec` does not lint the spec.
|
|
24
|
+
Each rule is a warning, so `tsp compile` still emits:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
warning @nxgt/typespec/list-returns-page: listAll returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`tsp compile api --warn-as-error` fails on any of them, for CI.
|
|
31
|
+
|
|
32
|
+
| Rule | Warns when |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| [`list-returns-page`](#list-returns-page) | a `list`, `search` or `query` returns an array |
|
|
35
|
+
| [`service-operation-ids`](#service-operation-ids) | a `@service` namespace has no `@operationIds` |
|
|
36
|
+
| [`error-body-shape`](#error-body-shape) | an error reply's body is not `status`, `message` and `timestamp` |
|
|
37
|
+
|
|
38
|
+
The library's own checks, such as `verb-method-mismatch` and
|
|
39
|
+
`duplicate-operation-id`, run whether or not the linter is on
|
|
40
|
+
([Operation ids](operation-ids.md)).
|
|
41
|
+
|
|
42
|
+
## `list-returns-page`
|
|
43
|
+
|
|
44
|
+
**Checks:** an operation whose name is `list`, `search` or `query`, alone or
|
|
45
|
+
followed by a capital letter (`listUsers`, `searchPosts`), with a success
|
|
46
|
+
reply whose body is an array. `listing` and `queryable` are not lists, and
|
|
47
|
+
are not checked. The name is checked as written, in an interface or not.
|
|
48
|
+
|
|
49
|
+
Only the operations the emitter writes are checked, by this rule and
|
|
50
|
+
`error-body-shape`: a template's declaration, its instance (`Read<Pet>`) and
|
|
51
|
+
the operations of an interface template are not, while an interface that
|
|
52
|
+
extends a template, `interface Users extends Crud<User> {}`, has its copied
|
|
53
|
+
operations checked.
|
|
54
|
+
|
|
55
|
+
**Why:** an array cannot grow a `total` or a `nextCursor` later without
|
|
56
|
+
breaking every client. `Page<Item>` and `CursorPage<Item>` can, and they are
|
|
57
|
+
the pages `@nxgt/drizzle` and `@nxgt/mongo` return.
|
|
58
|
+
|
|
59
|
+
Fails:
|
|
60
|
+
|
|
61
|
+
```tsp
|
|
62
|
+
@route("/users")
|
|
63
|
+
interface Users {
|
|
64
|
+
@get list(): User[];
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
warning @nxgt/typespec/list-returns-page: list returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Passes:
|
|
73
|
+
|
|
74
|
+
```tsp
|
|
75
|
+
@route("/users")
|
|
76
|
+
interface Users {
|
|
77
|
+
@get list(...PageParameters): Page<User>;
|
|
78
|
+
@get @route("/search") search(...CursorPageParameters): CursorPage<User>;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
More on both pages in [Pagination](pagination.md).
|
|
83
|
+
|
|
84
|
+
## `service-operation-ids`
|
|
85
|
+
|
|
86
|
+
**Checks:** a `@service` namespace that is not marked with `@operationIds`,
|
|
87
|
+
neither itself nor a namespace around it.
|
|
88
|
+
|
|
89
|
+
**Why:** without it, the emitter names each operation after its interface,
|
|
90
|
+
`Users_list`, and the generated client's methods and types take that name.
|
|
91
|
+
`@operationIds` gives `listUsers` ([Operation ids](operation-ids.md)).
|
|
92
|
+
|
|
93
|
+
Fails:
|
|
94
|
+
|
|
95
|
+
```tsp
|
|
96
|
+
@service
|
|
97
|
+
namespace Shop {
|
|
98
|
+
@route("/health") @get op health(): void;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
warning @nxgt/typespec/service-operation-ids: The service Shop has no @operationIds: its ids are the emitter's, such as Users_list. Mark the namespace with @operationIds.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Passes, marked on the service or on a namespace around it:
|
|
107
|
+
|
|
108
|
+
```tsp
|
|
109
|
+
@service
|
|
110
|
+
@operationIds
|
|
111
|
+
namespace Shop {
|
|
112
|
+
@route("/health") @get op health(): void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@operationIds
|
|
116
|
+
namespace Admin {
|
|
117
|
+
@service
|
|
118
|
+
namespace Billing {
|
|
119
|
+
@route("/invoices") @get op invoices(): void;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`@operationIds` on an interface alone does not count: the rule asks for the
|
|
125
|
+
namespace.
|
|
126
|
+
|
|
127
|
+
## `error-body-shape`
|
|
128
|
+
|
|
129
|
+
**Checks:** each reply of status 400 or above, of a range that starts at 400
|
|
130
|
+
or above (`5XX`), or of no status (`default`, an `@error` model without
|
|
131
|
+
`@statusCode`), that has a body. The body must be a model with a `status`, a
|
|
132
|
+
`message` and a `timestamp` property. A reply without a body passes, such as
|
|
133
|
+
`AuthenticationRequired`, `AccessDenied` or `ErrorWithoutBody<404>`.
|
|
134
|
+
|
|
135
|
+
**Why:** `@nxgt/openapi-hono` answers its own 400 and 500 with that envelope,
|
|
136
|
+
and a client written against it reads `message` from every error. A body of
|
|
137
|
+
another shape is one more case each client must handle.
|
|
138
|
+
|
|
139
|
+
Fails:
|
|
140
|
+
|
|
141
|
+
```tsp
|
|
142
|
+
model Teapot {
|
|
143
|
+
@statusCode _: 418;
|
|
144
|
+
@body body: { reason: string };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@route("/users")
|
|
148
|
+
interface Users {
|
|
149
|
+
@post create(@body user: User): User | Teapot;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
warning @nxgt/typespec/error-body-shape: create answers 418 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>.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
A range is named by its bounds, `500-599`, and a reply without a status as
|
|
158
|
+
`*`.
|
|
159
|
+
|
|
160
|
+
Passes: one of the library's errors, `ErrorResponse<Status>`, or a body that
|
|
161
|
+
spreads or extends `ErrorBody<Status>` and adds its own fields
|
|
162
|
+
(`model TeapotBody extends ErrorBody<418> { reason: string; }` passes too):
|
|
163
|
+
|
|
164
|
+
```tsp
|
|
165
|
+
model TeapotBody {
|
|
166
|
+
...ErrorBody<418>;
|
|
167
|
+
reason: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@error
|
|
171
|
+
model Teapot {
|
|
172
|
+
@statusCode _: 418;
|
|
173
|
+
@body body: TeapotBody;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@route("/users")
|
|
177
|
+
interface Users {
|
|
178
|
+
@get @route("/{id}") get(@path id: uuid): User | NotFound | AuthenticationRequired;
|
|
179
|
+
@post create(@body user: User): User | BadRequest | Teapot | ErrorResponse<503>;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The rule checks the property names, not their types. Each error is in
|
|
184
|
+
[Error replies](errors.md).
|
|
185
|
+
|
|
186
|
+
## Turn a rule off
|
|
187
|
+
|
|
188
|
+
For the whole spec, in `tspconfig.yaml`, with the reason:
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
linter:
|
|
192
|
+
extends:
|
|
193
|
+
- '@nxgt/typespec/recommended'
|
|
194
|
+
disable:
|
|
195
|
+
'@nxgt/typespec/error-body-shape': 'the legacy routes answer their own errors'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For one operation or one namespace, a `#suppress` line above it:
|
|
199
|
+
|
|
200
|
+
```tsp
|
|
201
|
+
@route("/users")
|
|
202
|
+
interface Users {
|
|
203
|
+
#suppress "@nxgt/typespec/list-returns-page" "a fixed set of ten"
|
|
204
|
+
@get @route("/top") listTop(): User[];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#suppress "@nxgt/typespec/service-operation-ids" "its clients already call Health_check"
|
|
208
|
+
@service
|
|
209
|
+
namespace Health {
|
|
210
|
+
@route("/health") @get op check(): void;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Or turn on only the rules you want, without the set:
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
linter:
|
|
218
|
+
enable:
|
|
219
|
+
'@nxgt/typespec/list-returns-page': true
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## A whole spec
|
|
223
|
+
|
|
224
|
+
The rules pass on a spec that uses the library throughout:
|
|
225
|
+
|
|
226
|
+
```tsp
|
|
227
|
+
// api/main.tsp
|
|
228
|
+
import "@typespec/http";
|
|
229
|
+
import "@nxgt/typespec";
|
|
230
|
+
|
|
231
|
+
using Http;
|
|
232
|
+
using Nxgt;
|
|
233
|
+
|
|
234
|
+
@service(#{ title: "Shop" })
|
|
235
|
+
@operationIds
|
|
236
|
+
namespace Shop;
|
|
237
|
+
|
|
238
|
+
model User {
|
|
239
|
+
@visibility(Lifecycle.Read) id: uuid;
|
|
240
|
+
name: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
@route("/users")
|
|
244
|
+
interface Users {
|
|
245
|
+
@get list(...PageParameters): Page<User> | BadRequest; // listUsers
|
|
246
|
+
@get @route("/search") search(...CursorPageParameters): CursorPage<User>; // searchUsers
|
|
247
|
+
@get @route("/{id}") get(@path id: uuid): User | NotFound | AuthenticationRequired; // getUser
|
|
248
|
+
@post create(@body user: User): User | BadRequest | Conflict; // createUser
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
bunx --no-install tsp compile api --warn-as-error
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The messages, with their fixes, are also in
|
|
257
|
+
[troubleshooting](../troubleshooting.md).
|
package/docs/roadmap.md
CHANGED
|
@@ -6,10 +6,12 @@ generated by `@nxgt/openapi-codegen` in CI before it ships.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
`
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
- **A linter.** The ruleset `@nxgt/typespec/recommended`, which a spec
|
|
10
|
+
extends in `tspconfig.yaml`, warns of a list that returns an array
|
|
11
|
+
(`list-returns-page`), a service without `@operationIds`
|
|
12
|
+
(`service-operation-ids`), and an error reply without the envelope
|
|
13
|
+
(`error-body-shape`). Each rule can be turned off, for the spec or for one
|
|
14
|
+
operation.
|
|
13
15
|
|
|
14
16
|
## Later
|
|
15
17
|
|
|
@@ -23,6 +25,13 @@ generated by `@nxgt/openapi-codegen` in CI before it ships.
|
|
|
23
25
|
|
|
24
26
|
## Shipped
|
|
25
27
|
|
|
28
|
+
### 0.7.0
|
|
29
|
+
|
|
30
|
+
- **Error aliases by verb.** `ListErrors`, `GetErrors`, `CreateErrors`,
|
|
31
|
+
`UpdateErrors`, `DeleteErrors` and `AuthErrors` name the errors each verb
|
|
32
|
+
usually answers, so an operation writes `User | GetErrors` instead of the
|
|
33
|
+
union. Each is exactly its union, and they combine.
|
|
34
|
+
|
|
26
35
|
### 0.6.0
|
|
27
36
|
|
|
28
37
|
- **Verbs name their methods.** An operation named after one of the
|
package/docs/troubleshooting.md
CHANGED
|
@@ -295,6 +295,139 @@ Two plain bodies, `Post | Draft`, are one reply whose body is either; two
|
|
|
295
295
|
description; bodies of different content types are negotiated. All three
|
|
296
296
|
pass.
|
|
297
297
|
|
|
298
|
+
## `returns an array: return Page<Item> or CursorPage<Item>`
|
|
299
|
+
|
|
300
|
+
**When:** a warning, with the ruleset `@nxgt/typespec/recommended` on: an
|
|
301
|
+
operation named `list`, `search` or `query`, alone or before a capital
|
|
302
|
+
letter, has a success reply whose body is an array.
|
|
303
|
+
|
|
304
|
+
```tsp
|
|
305
|
+
@route("/users")
|
|
306
|
+
interface Users {
|
|
307
|
+
@get list(): User[];
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```text
|
|
312
|
+
warning @nxgt/typespec/list-returns-page: list returns an array: return Page<Item> or CursorPage<Item>, which can carry a total or a next cursor.
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Why:** an array cannot grow a `total` or a `nextCursor` later without
|
|
316
|
+
breaking its clients. A page can.
|
|
317
|
+
|
|
318
|
+
**Fix:** return a page ([Pagination](guide/pagination.md)):
|
|
319
|
+
|
|
320
|
+
```tsp
|
|
321
|
+
@get list(...PageParameters): Page<User>;
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Or, for a list that will never page, silence that one operation:
|
|
325
|
+
|
|
326
|
+
```tsp
|
|
327
|
+
#suppress "@nxgt/typespec/list-returns-page" "a fixed set of ten"
|
|
328
|
+
@get @route("/top") listTop(): User[];
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## `The service … has no @operationIds: its ids are the emitter's, such as Users_list`
|
|
332
|
+
|
|
333
|
+
**When:** a warning, with the ruleset `@nxgt/typespec/recommended` on: a
|
|
334
|
+
`@service` namespace is not marked with `@operationIds`, neither itself nor
|
|
335
|
+
a namespace around it. `@operationIds` on its interfaces alone does not
|
|
336
|
+
count.
|
|
337
|
+
|
|
338
|
+
```tsp
|
|
339
|
+
@service
|
|
340
|
+
namespace Shop {
|
|
341
|
+
@route("/health") @get op health(): void;
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
```text
|
|
346
|
+
warning @nxgt/typespec/service-operation-ids: The service Shop has no @operationIds: its ids are the emitter's, such as Users_list. Mark the namespace with @operationIds.
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Why:** the emitter names each operation after its interface,
|
|
350
|
+
`Users_list`, and the generated client's methods and types take that name.
|
|
351
|
+
|
|
352
|
+
**Fix:** mark the namespace ([Operation ids](guide/operation-ids.md)):
|
|
353
|
+
|
|
354
|
+
```tsp
|
|
355
|
+
@service
|
|
356
|
+
@operationIds
|
|
357
|
+
namespace Shop {
|
|
358
|
+
@route("/health") @get op health(): void;
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Marking an existing service renames its operations, and the generated
|
|
363
|
+
client's methods with them. To keep the old ids, silence the rule on that
|
|
364
|
+
namespace:
|
|
365
|
+
|
|
366
|
+
```tsp
|
|
367
|
+
#suppress "@nxgt/typespec/service-operation-ids" "its clients already call Health_check"
|
|
368
|
+
@service
|
|
369
|
+
namespace Health {
|
|
370
|
+
@route("/health") @get op check(): void;
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## `answers … with a body that is not the nxgt envelope`
|
|
375
|
+
|
|
376
|
+
**When:** a warning, with the ruleset `@nxgt/typespec/recommended` on: a
|
|
377
|
+
reply of status 400 or above, of a range from 400 up, or `default` (an
|
|
378
|
+
`@error` model without `@statusCode`) has a body that is not a model with
|
|
379
|
+
`status`, `message` and `timestamp`.
|
|
380
|
+
|
|
381
|
+
```tsp
|
|
382
|
+
model Teapot {
|
|
383
|
+
@statusCode _: 418;
|
|
384
|
+
@body body: { reason: string };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@route("/users")
|
|
388
|
+
interface Users {
|
|
389
|
+
@post create(@body user: User): User | Teapot;
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```text
|
|
394
|
+
warning @nxgt/typespec/error-body-shape: create answers 418 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>.
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
A range is named `500-599`, and `default` `*`.
|
|
398
|
+
|
|
399
|
+
**Why:** `@nxgt/openapi-hono` answers its own 400 and 500 with the envelope,
|
|
400
|
+
and a client reads `message` from every error. Another shape is one more
|
|
401
|
+
case for each client.
|
|
402
|
+
|
|
403
|
+
**Fix:** declare one of the library's errors, `ErrorResponse<Status>`, or a
|
|
404
|
+
body that spreads or extends `ErrorBody<Status>` ([Error replies](guide/errors.md)):
|
|
405
|
+
|
|
406
|
+
```tsp
|
|
407
|
+
model TeapotBody {
|
|
408
|
+
...ErrorBody<418>;
|
|
409
|
+
reason: string;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@error
|
|
413
|
+
model Teapot {
|
|
414
|
+
@statusCode _: 418;
|
|
415
|
+
@body body: TeapotBody;
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
A reply without a body, such as `AuthenticationRequired`, passes. For a
|
|
420
|
+
spec whose errors have another shape on purpose, turn the rule off in
|
|
421
|
+
`tspconfig.yaml` ([Linter](guide/linter.md#turn-a-rule-off)):
|
|
422
|
+
|
|
423
|
+
```yaml
|
|
424
|
+
linter:
|
|
425
|
+
extends:
|
|
426
|
+
- '@nxgt/typespec/recommended'
|
|
427
|
+
disable:
|
|
428
|
+
'@nxgt/typespec/error-body-shape': 'the legacy routes answer their own errors'
|
|
429
|
+
```
|
|
430
|
+
|
|
298
431
|
## `Unknown decorator @operationId`
|
|
299
432
|
|
|
300
433
|
**When:** a spec writes `@operationId` next to `@operationIds`.
|