@webpieces/openapi-generator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +118 -0
  2. package/package.json +32 -0
  3. package/src/OpenApiGenerationError.d.ts +41 -0
  4. package/src/OpenApiGenerationError.js +45 -0
  5. package/src/OpenApiGenerationError.js.map +1 -0
  6. package/src/cli/OpenApiCli.d.ts +33 -0
  7. package/src/cli/OpenApiCli.js +100 -0
  8. package/src/cli/OpenApiCli.js.map +1 -0
  9. package/src/cli/WpOpenApiMain.d.ts +25 -0
  10. package/src/cli/WpOpenApiMain.js +62 -0
  11. package/src/cli/WpOpenApiMain.js.map +1 -0
  12. package/src/cli/wp-openapi.d.ts +2 -0
  13. package/src/cli/wp-openapi.js +18 -0
  14. package/src/cli/wp-openapi.js.map +1 -0
  15. package/src/emit/ArtifactWriter.d.ts +44 -0
  16. package/src/emit/ArtifactWriter.js +75 -0
  17. package/src/emit/ArtifactWriter.js.map +1 -0
  18. package/src/generate/DocumentSelection.d.ts +96 -0
  19. package/src/generate/DocumentSelection.js +153 -0
  20. package/src/generate/DocumentSelection.js.map +1 -0
  21. package/src/generate/GenerationInputs.d.ts +67 -0
  22. package/src/generate/GenerationInputs.js +88 -0
  23. package/src/generate/GenerationInputs.js.map +1 -0
  24. package/src/generate/OpenApiGenerator.d.ts +113 -0
  25. package/src/generate/OpenApiGenerator.js +306 -0
  26. package/src/generate/OpenApiGenerator.js.map +1 -0
  27. package/src/generate/OperationRenderer.d.ts +129 -0
  28. package/src/generate/OperationRenderer.js +256 -0
  29. package/src/generate/OperationRenderer.js.map +1 -0
  30. package/src/generate/SchemaRenderer.d.ts +89 -0
  31. package/src/generate/SchemaRenderer.js +236 -0
  32. package/src/generate/SchemaRenderer.js.map +1 -0
  33. package/src/generate/SecurityDeriver.d.ts +39 -0
  34. package/src/generate/SecurityDeriver.js +81 -0
  35. package/src/generate/SecurityDeriver.js.map +1 -0
  36. package/src/index.d.ts +32 -0
  37. package/src/index.js +70 -0
  38. package/src/index.js.map +1 -0
  39. package/src/json/JsonObject.d.ts +35 -0
  40. package/src/json/JsonObject.js +32 -0
  41. package/src/json/JsonObject.js.map +1 -0
  42. package/src/json/JsonWriter.d.ts +18 -0
  43. package/src/json/JsonWriter.js +48 -0
  44. package/src/json/JsonWriter.js.map +1 -0
  45. package/src/json/YamlReader.d.ts +35 -0
  46. package/src/json/YamlReader.js +111 -0
  47. package/src/json/YamlReader.js.map +1 -0
  48. package/src/json/YamlWriter.d.ts +35 -0
  49. package/src/json/YamlWriter.js +88 -0
  50. package/src/json/YamlWriter.js.map +1 -0
  51. package/src/load/ExportedConstantFolder.d.ts +21 -0
  52. package/src/load/ExportedConstantFolder.js +57 -0
  53. package/src/load/ExportedConstantFolder.js.map +1 -0
  54. package/src/load/ForeignFailure.d.ts +24 -0
  55. package/src/load/ForeignFailure.js +41 -0
  56. package/src/load/ForeignFailure.js.map +1 -0
  57. package/src/load/InputsLoader.d.ts +43 -0
  58. package/src/load/InputsLoader.js +148 -0
  59. package/src/load/InputsLoader.js.map +1 -0
  60. package/src/manifest/JsonReader.d.ts +37 -0
  61. package/src/manifest/JsonReader.js +109 -0
  62. package/src/manifest/JsonReader.js.map +1 -0
  63. package/src/manifest/ManifestLoader.d.ts +20 -0
  64. package/src/manifest/ManifestLoader.js +69 -0
  65. package/src/manifest/ManifestLoader.js.map +1 -0
  66. package/src/manifest/OpenApiManifest.d.ts +116 -0
  67. package/src/manifest/OpenApiManifest.js +142 -0
  68. package/src/manifest/OpenApiManifest.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArtifactWriter.js","sourceRoot":"","sources":["../../../../../../packages/docs/openapi-generator/src/emit/ArtifactWriter.ts"],"names":[],"mappings":";;;;AAAA,oDAA8B;AAC9B,wDAAkC;AAElC,mDAAgD;AAChD,mDAAgD;AAEhD,6EAA6E;AAC7E,MAAa,iBAAiB;IAEb;IACA;IAFb,YACa,QAAgB,EAChB,IAAY;QADZ,aAAQ,GAAR,QAAQ,CAAQ;QAChB,SAAI,GAAJ,IAAI,CAAQ;IACtB,CAAC;CACP;AALD,8CAKC;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAa,cAAc;IACN,IAAI,GAAG,IAAI,uBAAU,EAAE,CAAC;IACxB,IAAI,GAAG,IAAI,uBAAU,EAAE,CAAC;IAEzC,SAAS,CAAC,SAA6B,EAAE,MAAoB;QACzD,MAAM,SAAS,GAAwB,EAAE,CAAC;QAC1C,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;YAC1C,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACpB,SAAS,CAAC,IAAI,CACV,IAAI,iBAAiB,CACjB,GAAG,SAAS,CAAC,QAAQ,OAAO,EAC5B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CACtC,CACJ,CAAC;YACN,CAAC;YACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACpB,SAAS,CAAC,IAAI,CACV,IAAI,iBAAiB,CACjB,GAAG,SAAS,CAAC,QAAQ,OAAO,EAC5B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CACtC,CACJ,CAAC;YACN,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,kGAAkG;IAClG,KAAK,CAAC,MAAc,EAAE,SAAuC;QACzD,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAA2B,EAAE,EAAE;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAClD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,kFAAkF;IAClF,WAAW,CAAC,SAA6B;QACrC,OAAO,SAAS,CAAC,SAAS,CAAC;IAC/B,CAAC;CACJ;AAzCD,wCAyCC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { GeneratedDocument, GeneratedDocuments } from '../generate/GenerationInputs';\nimport { JsonWriter } from '../json/JsonWriter';\nimport { YamlWriter } from '../json/YamlWriter';\n\n/** One file this generation pass produces: its name, and its exact bytes. */\nexport class GeneratedArtifact {\n constructor(\n readonly fileName: string,\n readonly text: string,\n ) {}\n}\n\n/** Which serializations to write. Orthogonal to WHICH documents `@ApiType` selected. */\nexport type OutputFormat = 'json' | 'yaml' | 'both';\n\n/**\n * The generated documents, serialized.\n *\n * | file | audience |\n * |---|---|\n * | `full-private-openapi.json` | internal. Every contract declaring `SVC_TO_SVC`, hidden methods included. Nothing renders it for humans |\n * | `public-openapi.json` | customers. Contracts declaring `EXTERNAL_CUSTOMER`, minus every `{ hidden: true }` method |\n * | `mcp-openapi.json` | agents. Contracts declaring `MCP`, carrying the `x-mcp-*` extensions |\n *\n * A document is written only when some contract declared its type, which is decided by the generator\n * — this class writes what it is given. `--format` then chooses `.json`, `.yaml` or both from the\n * SAME in-memory document, so the two serializations cannot disagree.\n *\n * ## Why the goldens commit JSON only\n *\n * Committing both would double the review surface every decorator change has to be diffed against,\n * for a second file that is the first one restated. One spec parses each emitted YAML and asserts\n * deep equality with its JSON counterpart instead, which proves the YAML is correct without asking\n * anybody to read it.\n *\n * ## Why the JSON documents ARE committed\n *\n * `diff full-private-openapi.json public-openapi.json` is the complete list of what these contracts\n * do not show a customer. That only works as a review device if both files are in the tree, so hiding\n * a method shows up as a diff in the PR that hides it.\n */\nexport class ArtifactWriter {\n private readonly json = new JsonWriter();\n private readonly yaml = new YamlWriter();\n\n artifacts(documents: GeneratedDocuments, format: OutputFormat): readonly GeneratedArtifact[] {\n const artifacts: GeneratedArtifact[] = [];\n for (const generated of documents.documents) {\n if (format !== 'yaml') {\n artifacts.push(\n new GeneratedArtifact(\n `${generated.fileName}.json`,\n this.json.write(generated.document),\n ),\n );\n }\n if (format !== 'json') {\n artifacts.push(\n new GeneratedArtifact(\n `${generated.fileName}.yaml`,\n this.yaml.write(generated.document),\n ),\n );\n }\n }\n return artifacts;\n }\n\n /** Write them all, creating `outDir` if it does not exist. Returns the absolute paths written. */\n write(outDir: string, artifacts: readonly GeneratedArtifact[]): readonly string[] {\n fs.mkdirSync(outDir, { recursive: true });\n return artifacts.map((artifact: GeneratedArtifact) => {\n const file = path.join(outDir, artifact.fileName);\n fs.writeFileSync(file, artifact.text, 'utf8');\n return file;\n });\n }\n\n /** The document objects, for a caller that wants them rather than their bytes. */\n documentsOf(documents: GeneratedDocuments): readonly GeneratedDocument[] {\n return documents.documents;\n }\n}\n"]}
@@ -0,0 +1,96 @@
1
+ import { ApiDocModel, DocumentedEndpoint } from '@webpieces/api-doc-model';
2
+ import { ApiTypeKind } from '@webpieces/core-util';
3
+ /** The one line that says a document is not the customer contract. It appears in exactly one. */
4
+ export declare const INTERNAL_ONLY_LINE = "INTERNAL \u2014 service-to-service use only. This document lists every endpoint these services serve, including ones no customer is meant to see. It is not the customer contract and must never be handed to one.";
5
+ /**
6
+ * The retry contract, published ONCE per document rather than as booleans stamped on every operation.
7
+ *
8
+ * Each operation's own `description` ends with one derived sentence saying whether it is safe to
9
+ * retry; this table says where that sentence comes from. Three vendor-extension booleans per
10
+ * operation would have been invisible to a human — Swagger UI and most themes do not render `x-`
11
+ * extensions, and no standard generator reads `x-webpieces-*` — and would have tripled golden churn
12
+ * the day the mapping changed.
13
+ */
14
+ export declare const OPERATION_SEMANTICS: string;
15
+ /**
16
+ * WHICH contracts and operations a document contains, and what prose it stamps. The ONLY thing that
17
+ * differs between the three generated documents.
18
+ *
19
+ * ## Two orthogonal decisions, at two levels
20
+ *
21
+ * - `@ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER)` on the CONTRACT selects which FILES it feeds. A document
22
+ * is written at all only when some contract declares its type, so no contract declaring `MCP` means
23
+ * no `mcp-openapi.json` — there is no second emptiness rule to keep in step with that one.
24
+ * - `{ hidden: true }` on ONE `@Endpoint` subtracts that method from the CUSTOMER document only. It
25
+ * stays in the private document, and in the MCP document when it is a tool.
26
+ *
27
+ * The per-method case is real and cannot be said at class level: a NEW endpoint on an
28
+ * already-published API, built on main before it is announced. The per-contract case is the one that
29
+ * must not default to permissive, and at class level naming it costs one token per contract.
30
+ *
31
+ * ## Select, then render — never render, then filter
32
+ *
33
+ * One render function takes one of these and is called once per document. It builds
34
+ * `components.schemas` by walking OUTWARD from the operations this selection accepted, so an
35
+ * unselected operation's DTOs are never constructed at all.
36
+ *
37
+ * That asymmetry is about what a BUG does. Build-then-filter puts an unreleased feature's full
38
+ * request and response schemas into the document and then relies on a removal pass to take them out
39
+ * again — so a defect in the visibility logic SHIPS them, named and fully shaped, with only the URL
40
+ * missing, to customers who were told the feature was withheld. Select-then-render cannot do that:
41
+ * no code path can emit a schema that was never built.
42
+ *
43
+ * The usual objection to rendering more than once — that the passes drift in how each renders the
44
+ * same endpoint — applies to separate code paths. This is ONE function, so same code, same input,
45
+ * same output.
46
+ *
47
+ * ## The thing that bites: map ordering
48
+ *
49
+ * Collecting schemas during a walk makes the key order of `components.schemas` depend on which
50
+ * operations were walked, so two documents would list identical schemas in different orders and the
51
+ * goldens would churn on unrelated changes. The renderer SORTS the schema keys before emitting.
52
+ * Orders that carry MEANING are left alone and said so where they are built: `apis[]` order is the
53
+ * published sidebar, and the security-scheme order is the order the credentials are declared in.
54
+ */
55
+ export declare class DocumentSelection {
56
+ /** The file name this document is written to, and the name a failure uses for it. */
57
+ readonly fileName: string;
58
+ /** The `@ApiType` a contract must declare to appear in this document. */
59
+ readonly apiType: ApiTypeKind;
60
+ /** Drop methods marked `{ hidden: true }`. True for the customer document alone. */
61
+ readonly dropHidden: boolean;
62
+ /** Stamp `x-mcp-tool` and its siblings. Only the MCP document has a reader for them. */
63
+ readonly includeMcpExtensions: boolean;
64
+ /**
65
+ * Prepend {@link INTERNAL_ONLY_LINE} to `info.description`, and say what TRIGGERS each
66
+ * operation. Both are internal facts: a customer calls what they are given a url for, and
67
+ * whether ours fires on a clock or off a queue is our business.
68
+ */
69
+ readonly internalNotice: boolean;
70
+ private constructor();
71
+ /** Every contract declaring `SVC_TO_SVC` — which, by the fail-closed default, is every contract. */
72
+ static internal(): DocumentSelection;
73
+ /** Every contract declaring `EXTERNAL_CUSTOMER`, minus its hidden methods. */
74
+ static customer(): DocumentSelection;
75
+ /**
76
+ * Every contract declaring `MCP`. A HIDDEN method is kept — an internal endpoint that is an agent
77
+ * tool is still a tool, and requiring it to be published to customers in order to be one would be
78
+ * exactly the wrong coupling.
79
+ */
80
+ static mcp(): DocumentSelection;
81
+ /** Every document this generator knows how to write, in the order it writes them. */
82
+ static all(): readonly DocumentSelection[];
83
+ acceptsContract(model: ApiDocModel): boolean;
84
+ /**
85
+ * `{ hidden: true }` subtracts a method from the CUSTOMER document, and `@WpAuthLocalOnly`
86
+ * subtracts it from ALL of them.
87
+ *
88
+ * A local-only endpoint is not registered as a route at all once the process is deployed, so it
89
+ * exists nowhere any reader of any of these documents could call it. Publishing it even in the
90
+ * private one would document a route that does not exist in any deployed environment — worse
91
+ * than omitting it, because a reader would reasonably try to call it.
92
+ */
93
+ acceptsEndpoint(endpoint: DocumentedEndpoint): boolean;
94
+ /** `info.description`: the manifest's prose, plus whatever this document adds to it. */
95
+ description(manifestDescription: string | undefined): string | undefined;
96
+ }
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocumentSelection = exports.OPERATION_SEMANTICS = exports.INTERNAL_ONLY_LINE = void 0;
4
+ const core_util_1 = require("@webpieces/core-util");
5
+ /** The one line that says a document is not the customer contract. It appears in exactly one. */
6
+ exports.INTERNAL_ONLY_LINE = 'INTERNAL — service-to-service use only. This document lists every endpoint these services serve, including ones no customer is meant to see. It is not the customer contract and must never be handed to one.';
7
+ /**
8
+ * The retry contract, published ONCE per document rather than as booleans stamped on every operation.
9
+ *
10
+ * Each operation's own `description` ends with one derived sentence saying whether it is safe to
11
+ * retry; this table says where that sentence comes from. Three vendor-extension booleans per
12
+ * operation would have been invisible to a human — Swagger UI and most themes do not render `x-`
13
+ * extensions, and no standard generator reads `x-webpieces-*` — and would have tripled golden churn
14
+ * the day the mapping changed.
15
+ */
16
+ exports.OPERATION_SEMANTICS = [
17
+ "Every operation states whether it is safe to retry, derived from the endpoint's declared",
18
+ 'side-effect contract:',
19
+ '',
20
+ '| declared operation | read-only | destructive | idempotent |',
21
+ '|---|---|---|---|',
22
+ '| read | yes | no | yes |',
23
+ '| write-idempotent | no | yes | yes |',
24
+ '| write | no | yes | no |',
25
+ ].join('\n');
26
+ /**
27
+ * WHICH contracts and operations a document contains, and what prose it stamps. The ONLY thing that
28
+ * differs between the three generated documents.
29
+ *
30
+ * ## Two orthogonal decisions, at two levels
31
+ *
32
+ * - `@ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER)` on the CONTRACT selects which FILES it feeds. A document
33
+ * is written at all only when some contract declares its type, so no contract declaring `MCP` means
34
+ * no `mcp-openapi.json` — there is no second emptiness rule to keep in step with that one.
35
+ * - `{ hidden: true }` on ONE `@Endpoint` subtracts that method from the CUSTOMER document only. It
36
+ * stays in the private document, and in the MCP document when it is a tool.
37
+ *
38
+ * The per-method case is real and cannot be said at class level: a NEW endpoint on an
39
+ * already-published API, built on main before it is announced. The per-contract case is the one that
40
+ * must not default to permissive, and at class level naming it costs one token per contract.
41
+ *
42
+ * ## Select, then render — never render, then filter
43
+ *
44
+ * One render function takes one of these and is called once per document. It builds
45
+ * `components.schemas` by walking OUTWARD from the operations this selection accepted, so an
46
+ * unselected operation's DTOs are never constructed at all.
47
+ *
48
+ * That asymmetry is about what a BUG does. Build-then-filter puts an unreleased feature's full
49
+ * request and response schemas into the document and then relies on a removal pass to take them out
50
+ * again — so a defect in the visibility logic SHIPS them, named and fully shaped, with only the URL
51
+ * missing, to customers who were told the feature was withheld. Select-then-render cannot do that:
52
+ * no code path can emit a schema that was never built.
53
+ *
54
+ * The usual objection to rendering more than once — that the passes drift in how each renders the
55
+ * same endpoint — applies to separate code paths. This is ONE function, so same code, same input,
56
+ * same output.
57
+ *
58
+ * ## The thing that bites: map ordering
59
+ *
60
+ * Collecting schemas during a walk makes the key order of `components.schemas` depend on which
61
+ * operations were walked, so two documents would list identical schemas in different orders and the
62
+ * goldens would churn on unrelated changes. The renderer SORTS the schema keys before emitting.
63
+ * Orders that carry MEANING are left alone and said so where they are built: `apis[]` order is the
64
+ * published sidebar, and the security-scheme order is the order the credentials are declared in.
65
+ */
66
+ class DocumentSelection {
67
+ fileName;
68
+ apiType;
69
+ dropHidden;
70
+ includeMcpExtensions;
71
+ internalNotice;
72
+ constructor(
73
+ /** The file name this document is written to, and the name a failure uses for it. */
74
+ fileName,
75
+ /** The `@ApiType` a contract must declare to appear in this document. */
76
+ apiType,
77
+ /** Drop methods marked `{ hidden: true }`. True for the customer document alone. */
78
+ dropHidden,
79
+ /** Stamp `x-mcp-tool` and its siblings. Only the MCP document has a reader for them. */
80
+ includeMcpExtensions,
81
+ /**
82
+ * Prepend {@link INTERNAL_ONLY_LINE} to `info.description`, and say what TRIGGERS each
83
+ * operation. Both are internal facts: a customer calls what they are given a url for, and
84
+ * whether ours fires on a clock or off a queue is our business.
85
+ */
86
+ internalNotice) {
87
+ this.fileName = fileName;
88
+ this.apiType = apiType;
89
+ this.dropHidden = dropHidden;
90
+ this.includeMcpExtensions = includeMcpExtensions;
91
+ this.internalNotice = internalNotice;
92
+ }
93
+ /** Every contract declaring `SVC_TO_SVC` — which, by the fail-closed default, is every contract. */
94
+ // webpieces-disable no-function-outside-class -- static factory; the private constructor is what stops a fourth, unnamed selection being invented at a call site
95
+ static internal() {
96
+ return new DocumentSelection('full-private-openapi', core_util_1.SVC_TO_SVC, false, false, true);
97
+ }
98
+ /** Every contract declaring `EXTERNAL_CUSTOMER`, minus its hidden methods. */
99
+ // webpieces-disable no-function-outside-class -- static factory of this class
100
+ static customer() {
101
+ return new DocumentSelection('public-openapi', core_util_1.EXTERNAL_CUSTOMER, true, false, false);
102
+ }
103
+ /**
104
+ * Every contract declaring `MCP`. A HIDDEN method is kept — an internal endpoint that is an agent
105
+ * tool is still a tool, and requiring it to be published to customers in order to be one would be
106
+ * exactly the wrong coupling.
107
+ */
108
+ // webpieces-disable no-function-outside-class -- static factory of this class
109
+ static mcp() {
110
+ return new DocumentSelection('mcp-openapi', core_util_1.MCP, false, true, false);
111
+ }
112
+ /** Every document this generator knows how to write, in the order it writes them. */
113
+ // webpieces-disable no-function-outside-class -- static factory of this class
114
+ static all() {
115
+ return [
116
+ DocumentSelection.internal(),
117
+ DocumentSelection.customer(),
118
+ DocumentSelection.mcp(),
119
+ ];
120
+ }
121
+ acceptsContract(model) {
122
+ return model.apiTypes.includes(this.apiType);
123
+ }
124
+ /**
125
+ * `{ hidden: true }` subtracts a method from the CUSTOMER document, and `@WpAuthLocalOnly`
126
+ * subtracts it from ALL of them.
127
+ *
128
+ * A local-only endpoint is not registered as a route at all once the process is deployed, so it
129
+ * exists nowhere any reader of any of these documents could call it. Publishing it even in the
130
+ * private one would document a route that does not exist in any deployed environment — worse
131
+ * than omitting it, because a reader would reasonably try to call it.
132
+ */
133
+ acceptsEndpoint(endpoint) {
134
+ if (endpoint.auth?.decorator === core_util_1.WpAuthLocalOnly.name) {
135
+ return false;
136
+ }
137
+ return !this.dropHidden || !endpoint.hidden;
138
+ }
139
+ /** `info.description`: the manifest's prose, plus whatever this document adds to it. */
140
+ description(manifestDescription) {
141
+ const parts = [];
142
+ if (this.internalNotice) {
143
+ parts.push(exports.INTERNAL_ONLY_LINE);
144
+ }
145
+ if (manifestDescription !== undefined) {
146
+ parts.push(manifestDescription);
147
+ }
148
+ parts.push(exports.OPERATION_SEMANTICS);
149
+ return parts.join('\n\n');
150
+ }
151
+ }
152
+ exports.DocumentSelection = DocumentSelection;
153
+ //# sourceMappingURL=DocumentSelection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentSelection.js","sourceRoot":"","sources":["../../../../../../packages/docs/openapi-generator/src/generate/DocumentSelection.ts"],"names":[],"mappings":";;;AACA,oDAM8B;AAE9B,iGAAiG;AACpF,QAAA,kBAAkB,GAC3B,+MAA+M,CAAC;AAEpN;;;;;;;;GAQG;AACU,QAAA,mBAAmB,GAAG;IAC/B,0FAA0F;IAC1F,uBAAuB;IACvB,EAAE;IACF,+DAA+D;IAC/D,mBAAmB;IACnB,2BAA2B;IAC3B,uCAAuC;IACvC,2BAA2B;CAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAa,iBAAiB;IAGb;IAEA;IAEA;IAEA;IAMA;IAdb;IACI,qFAAqF;IAC5E,QAAgB;IACzB,yEAAyE;IAChE,OAAoB;IAC7B,oFAAoF;IAC3E,UAAmB;IAC5B,wFAAwF;IAC/E,oBAA6B;IACtC;;;;OAIG;IACM,cAAuB;QAZvB,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,YAAO,GAAP,OAAO,CAAa;QAEpB,eAAU,GAAV,UAAU,CAAS;QAEnB,yBAAoB,GAApB,oBAAoB,CAAS;QAM7B,mBAAc,GAAd,cAAc,CAAS;IACjC,CAAC;IAEJ,oGAAoG;IACpG,iKAAiK;IACjK,MAAM,CAAC,QAAQ;QACX,OAAO,IAAI,iBAAiB,CAAC,sBAAsB,EAAE,sBAAU,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACzF,CAAC;IAED,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,CAAC,QAAQ;QACX,OAAO,IAAI,iBAAiB,CAAC,gBAAgB,EAAE,6BAAiB,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1F,CAAC;IAED;;;;OAIG;IACH,8EAA8E;IAC9E,MAAM,CAAC,GAAG;QACN,OAAO,IAAI,iBAAiB,CAAC,aAAa,EAAE,eAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACzE,CAAC;IAED,qFAAqF;IACrF,8EAA8E;IAC9E,MAAM,CAAC,GAAG;QACN,OAAO;YACH,iBAAiB,CAAC,QAAQ,EAAE;YAC5B,iBAAiB,CAAC,QAAQ,EAAE;YAC5B,iBAAiB,CAAC,GAAG,EAAE;SAC1B,CAAC;IACN,CAAC;IAED,eAAe,CAAC,KAAkB;QAC9B,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;;;OAQG;IACH,eAAe,CAAC,QAA4B;QACxC,IAAI,QAAQ,CAAC,IAAI,EAAE,SAAS,KAAK,2BAAe,CAAC,IAAI,EAAE,CAAC;YACpD,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAChD,CAAC;IAED,wFAAwF;IACxF,WAAW,CAAC,mBAAuC;QAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,0BAAkB,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACpC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,2BAAmB,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;CACJ;AAlFD,8CAkFC","sourcesContent":["import { ApiDocModel, DocumentedEndpoint } from '@webpieces/api-doc-model';\nimport {\n ApiTypeKind,\n EXTERNAL_CUSTOMER,\n MCP,\n SVC_TO_SVC,\n WpAuthLocalOnly,\n} from '@webpieces/core-util';\n\n/** The one line that says a document is not the customer contract. It appears in exactly one. */\nexport const INTERNAL_ONLY_LINE =\n 'INTERNAL — service-to-service use only. This document lists every endpoint these services serve, including ones no customer is meant to see. It is not the customer contract and must never be handed to one.';\n\n/**\n * The retry contract, published ONCE per document rather than as booleans stamped on every operation.\n *\n * Each operation's own `description` ends with one derived sentence saying whether it is safe to\n * retry; this table says where that sentence comes from. Three vendor-extension booleans per\n * operation would have been invisible to a human — Swagger UI and most themes do not render `x-`\n * extensions, and no standard generator reads `x-webpieces-*` — and would have tripled golden churn\n * the day the mapping changed.\n */\nexport const OPERATION_SEMANTICS = [\n \"Every operation states whether it is safe to retry, derived from the endpoint's declared\",\n 'side-effect contract:',\n '',\n '| declared operation | read-only | destructive | idempotent |',\n '|---|---|---|---|',\n '| read | yes | no | yes |',\n '| write-idempotent | no | yes | yes |',\n '| write | no | yes | no |',\n].join('\\n');\n\n/**\n * WHICH contracts and operations a document contains, and what prose it stamps. The ONLY thing that\n * differs between the three generated documents.\n *\n * ## Two orthogonal decisions, at two levels\n *\n * - `@ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER)` on the CONTRACT selects which FILES it feeds. A document\n * is written at all only when some contract declares its type, so no contract declaring `MCP` means\n * no `mcp-openapi.json` — there is no second emptiness rule to keep in step with that one.\n * - `{ hidden: true }` on ONE `@Endpoint` subtracts that method from the CUSTOMER document only. It\n * stays in the private document, and in the MCP document when it is a tool.\n *\n * The per-method case is real and cannot be said at class level: a NEW endpoint on an\n * already-published API, built on main before it is announced. The per-contract case is the one that\n * must not default to permissive, and at class level naming it costs one token per contract.\n *\n * ## Select, then render — never render, then filter\n *\n * One render function takes one of these and is called once per document. It builds\n * `components.schemas` by walking OUTWARD from the operations this selection accepted, so an\n * unselected operation's DTOs are never constructed at all.\n *\n * That asymmetry is about what a BUG does. Build-then-filter puts an unreleased feature's full\n * request and response schemas into the document and then relies on a removal pass to take them out\n * again — so a defect in the visibility logic SHIPS them, named and fully shaped, with only the URL\n * missing, to customers who were told the feature was withheld. Select-then-render cannot do that:\n * no code path can emit a schema that was never built.\n *\n * The usual objection to rendering more than once — that the passes drift in how each renders the\n * same endpoint — applies to separate code paths. This is ONE function, so same code, same input,\n * same output.\n *\n * ## The thing that bites: map ordering\n *\n * Collecting schemas during a walk makes the key order of `components.schemas` depend on which\n * operations were walked, so two documents would list identical schemas in different orders and the\n * goldens would churn on unrelated changes. The renderer SORTS the schema keys before emitting.\n * Orders that carry MEANING are left alone and said so where they are built: `apis[]` order is the\n * published sidebar, and the security-scheme order is the order the credentials are declared in.\n */\nexport class DocumentSelection {\n private constructor(\n /** The file name this document is written to, and the name a failure uses for it. */\n readonly fileName: string,\n /** The `@ApiType` a contract must declare to appear in this document. */\n readonly apiType: ApiTypeKind,\n /** Drop methods marked `{ hidden: true }`. True for the customer document alone. */\n readonly dropHidden: boolean,\n /** Stamp `x-mcp-tool` and its siblings. Only the MCP document has a reader for them. */\n readonly includeMcpExtensions: boolean,\n /**\n * Prepend {@link INTERNAL_ONLY_LINE} to `info.description`, and say what TRIGGERS each\n * operation. Both are internal facts: a customer calls what they are given a url for, and\n * whether ours fires on a clock or off a queue is our business.\n */\n readonly internalNotice: boolean,\n ) {}\n\n /** Every contract declaring `SVC_TO_SVC` — which, by the fail-closed default, is every contract. */\n // webpieces-disable no-function-outside-class -- static factory; the private constructor is what stops a fourth, unnamed selection being invented at a call site\n static internal(): DocumentSelection {\n return new DocumentSelection('full-private-openapi', SVC_TO_SVC, false, false, true);\n }\n\n /** Every contract declaring `EXTERNAL_CUSTOMER`, minus its hidden methods. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static customer(): DocumentSelection {\n return new DocumentSelection('public-openapi', EXTERNAL_CUSTOMER, true, false, false);\n }\n\n /**\n * Every contract declaring `MCP`. A HIDDEN method is kept — an internal endpoint that is an agent\n * tool is still a tool, and requiring it to be published to customers in order to be one would be\n * exactly the wrong coupling.\n */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static mcp(): DocumentSelection {\n return new DocumentSelection('mcp-openapi', MCP, false, true, false);\n }\n\n /** Every document this generator knows how to write, in the order it writes them. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static all(): readonly DocumentSelection[] {\n return [\n DocumentSelection.internal(),\n DocumentSelection.customer(),\n DocumentSelection.mcp(),\n ];\n }\n\n acceptsContract(model: ApiDocModel): boolean {\n return model.apiTypes.includes(this.apiType);\n }\n\n /**\n * `{ hidden: true }` subtracts a method from the CUSTOMER document, and `@WpAuthLocalOnly`\n * subtracts it from ALL of them.\n *\n * A local-only endpoint is not registered as a route at all once the process is deployed, so it\n * exists nowhere any reader of any of these documents could call it. Publishing it even in the\n * private one would document a route that does not exist in any deployed environment — worse\n * than omitting it, because a reader would reasonably try to call it.\n */\n acceptsEndpoint(endpoint: DocumentedEndpoint): boolean {\n if (endpoint.auth?.decorator === WpAuthLocalOnly.name) {\n return false;\n }\n return !this.dropHidden || !endpoint.hidden;\n }\n\n /** `info.description`: the manifest's prose, plus whatever this document adds to it. */\n description(manifestDescription: string | undefined): string | undefined {\n const parts: string[] = [];\n if (this.internalNotice) {\n parts.push(INTERNAL_ONLY_LINE);\n }\n if (manifestDescription !== undefined) {\n parts.push(manifestDescription);\n }\n parts.push(OPERATION_SEMANTICS);\n return parts.join('\\n\\n');\n }\n}\n"]}
@@ -0,0 +1,67 @@
1
+ import { ApiDocModel } from '@webpieces/api-doc-model';
2
+ import { JsonObject } from '../json/JsonObject';
3
+ import { ApiEntry, OpenApiManifest } from '../manifest/OpenApiManifest';
4
+ /** One manifest entry, paired with the model extracted from the file it names. */
5
+ export declare class ContractModel {
6
+ readonly entry: ApiEntry;
7
+ readonly model: ApiDocModel;
8
+ /** Absolute path to the contract, so a refusal can name a file somebody can open. */
9
+ readonly file: string;
10
+ constructor(entry: ApiEntry, model: ApiDocModel,
11
+ /** Absolute path to the contract, so a refusal can name a file somebody can open. */
12
+ file: string);
13
+ }
14
+ /**
15
+ * One response header, with its name already FOLDED out of the constant the manifest named.
16
+ *
17
+ * The folding happens before generation, not during it, so the generator is a pure function of
18
+ * already-established facts: everything that can fail by reading the world has failed by then.
19
+ */
20
+ export declare class ResolvedResponseHeader {
21
+ readonly headerName: string;
22
+ readonly description: string | undefined;
23
+ constructor(headerName: string, description: string | undefined);
24
+ }
25
+ /**
26
+ * Everything generation needs, with every file already read and every constant already folded.
27
+ *
28
+ * Separating this from {@link OpenApiGenerator} is what makes the generator testable without a
29
+ * filesystem — and, more usefully, what makes the two documents provably one generation pass: they
30
+ * are rendered from THIS value, twice, with only the hidden-endpoint filter differing.
31
+ */
32
+ export declare class GenerationInputs {
33
+ readonly manifestPath: string;
34
+ readonly manifest: OpenApiManifest;
35
+ readonly contracts: readonly ContractModel[];
36
+ /** The document-wide error body's model, when the manifest declared one. */
37
+ readonly errorType: ApiDocModel | undefined;
38
+ readonly responseHeaders: readonly ResolvedResponseHeader[];
39
+ /** The markdown preamble's TEXT, already read from `descriptionFile`. */
40
+ readonly description: string | undefined;
41
+ constructor(manifestPath: string, manifest: OpenApiManifest, contracts: readonly ContractModel[],
42
+ /** The document-wide error body's model, when the manifest declared one. */
43
+ errorType: ApiDocModel | undefined, responseHeaders: readonly ResolvedResponseHeader[],
44
+ /** The markdown preamble's TEXT, already read from `descriptionFile`. */
45
+ description: string | undefined);
46
+ }
47
+ /** ONE rendered document and the name it is written under. */
48
+ export declare class GeneratedDocument {
49
+ /** Without an extension — `--format` decides whether it is `.json`, `.yaml` or both. */
50
+ readonly fileName: string;
51
+ readonly document: JsonObject;
52
+ constructor(
53
+ /** Without an extension — `--format` decides whether it is `.json`, `.yaml` or both. */
54
+ fileName: string, document: JsonObject);
55
+ }
56
+ /**
57
+ * The documents this generation pass produced — one per `@ApiType` some contract declared, and no
58
+ * others.
59
+ *
60
+ * `diff full-private-openapi.json public-openapi.json` is the complete list of what these contracts
61
+ * do not show a customer, and both are committed, so hiding a method shows up as a diff in the PR
62
+ * that hides it. That is the property the pair exists for.
63
+ */
64
+ export declare class GeneratedDocuments {
65
+ readonly documents: readonly GeneratedDocument[];
66
+ constructor(documents: readonly GeneratedDocument[]);
67
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeneratedDocuments = exports.GeneratedDocument = exports.GenerationInputs = exports.ResolvedResponseHeader = exports.ContractModel = void 0;
4
+ /** One manifest entry, paired with the model extracted from the file it names. */
5
+ class ContractModel {
6
+ entry;
7
+ model;
8
+ file;
9
+ constructor(entry, model,
10
+ /** Absolute path to the contract, so a refusal can name a file somebody can open. */
11
+ file) {
12
+ this.entry = entry;
13
+ this.model = model;
14
+ this.file = file;
15
+ }
16
+ }
17
+ exports.ContractModel = ContractModel;
18
+ /**
19
+ * One response header, with its name already FOLDED out of the constant the manifest named.
20
+ *
21
+ * The folding happens before generation, not during it, so the generator is a pure function of
22
+ * already-established facts: everything that can fail by reading the world has failed by then.
23
+ */
24
+ class ResolvedResponseHeader {
25
+ headerName;
26
+ description;
27
+ constructor(headerName, description) {
28
+ this.headerName = headerName;
29
+ this.description = description;
30
+ }
31
+ }
32
+ exports.ResolvedResponseHeader = ResolvedResponseHeader;
33
+ /**
34
+ * Everything generation needs, with every file already read and every constant already folded.
35
+ *
36
+ * Separating this from {@link OpenApiGenerator} is what makes the generator testable without a
37
+ * filesystem — and, more usefully, what makes the two documents provably one generation pass: they
38
+ * are rendered from THIS value, twice, with only the hidden-endpoint filter differing.
39
+ */
40
+ class GenerationInputs {
41
+ manifestPath;
42
+ manifest;
43
+ contracts;
44
+ errorType;
45
+ responseHeaders;
46
+ description;
47
+ constructor(manifestPath, manifest, contracts,
48
+ /** The document-wide error body's model, when the manifest declared one. */
49
+ errorType, responseHeaders,
50
+ /** The markdown preamble's TEXT, already read from `descriptionFile`. */
51
+ description) {
52
+ this.manifestPath = manifestPath;
53
+ this.manifest = manifest;
54
+ this.contracts = contracts;
55
+ this.errorType = errorType;
56
+ this.responseHeaders = responseHeaders;
57
+ this.description = description;
58
+ }
59
+ }
60
+ exports.GenerationInputs = GenerationInputs;
61
+ /** ONE rendered document and the name it is written under. */
62
+ class GeneratedDocument {
63
+ fileName;
64
+ document;
65
+ constructor(
66
+ /** Without an extension — `--format` decides whether it is `.json`, `.yaml` or both. */
67
+ fileName, document) {
68
+ this.fileName = fileName;
69
+ this.document = document;
70
+ }
71
+ }
72
+ exports.GeneratedDocument = GeneratedDocument;
73
+ /**
74
+ * The documents this generation pass produced — one per `@ApiType` some contract declared, and no
75
+ * others.
76
+ *
77
+ * `diff full-private-openapi.json public-openapi.json` is the complete list of what these contracts
78
+ * do not show a customer, and both are committed, so hiding a method shows up as a diff in the PR
79
+ * that hides it. That is the property the pair exists for.
80
+ */
81
+ class GeneratedDocuments {
82
+ documents;
83
+ constructor(documents) {
84
+ this.documents = documents;
85
+ }
86
+ }
87
+ exports.GeneratedDocuments = GeneratedDocuments;
88
+ //# sourceMappingURL=GenerationInputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerationInputs.js","sourceRoot":"","sources":["../../../../../../packages/docs/openapi-generator/src/generate/GenerationInputs.ts"],"names":[],"mappings":";;;AAIA,kFAAkF;AAClF,MAAa,aAAa;IAET;IACA;IAEA;IAJb,YACa,KAAe,EACf,KAAkB;IAC3B,qFAAqF;IAC5E,IAAY;QAHZ,UAAK,GAAL,KAAK,CAAU;QACf,UAAK,GAAL,KAAK,CAAa;QAElB,SAAI,GAAJ,IAAI,CAAQ;IACtB,CAAC;CACP;AAPD,sCAOC;AAED;;;;;GAKG;AACH,MAAa,sBAAsB;IAElB;IACA;IAFb,YACa,UAAkB,EAClB,WAA+B;QAD/B,eAAU,GAAV,UAAU,CAAQ;QAClB,gBAAW,GAAX,WAAW,CAAoB;IACzC,CAAC;CACP;AALD,wDAKC;AAED;;;;;;GAMG;AACH,MAAa,gBAAgB;IAEZ;IACA;IACA;IAEA;IACA;IAEA;IARb,YACa,YAAoB,EACpB,QAAyB,EACzB,SAAmC;IAC5C,4EAA4E;IACnE,SAAkC,EAClC,eAAkD;IAC3D,yEAAyE;IAChE,WAA+B;QAP/B,iBAAY,GAAZ,YAAY,CAAQ;QACpB,aAAQ,GAAR,QAAQ,CAAiB;QACzB,cAAS,GAAT,SAAS,CAA0B;QAEnC,cAAS,GAAT,SAAS,CAAyB;QAClC,oBAAe,GAAf,eAAe,CAAmC;QAElD,gBAAW,GAAX,WAAW,CAAoB;IACzC,CAAC;CACP;AAXD,4CAWC;AAED,8DAA8D;AAC9D,MAAa,iBAAiB;IAGb;IACA;IAHb;IACI,wFAAwF;IAC/E,QAAgB,EAChB,QAAoB;QADpB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,aAAQ,GAAR,QAAQ,CAAY;IAC9B,CAAC;CACP;AAND,8CAMC;AAED;;;;;;;GAOG;AACH,MAAa,kBAAkB;IACN;IAArB,YAAqB,SAAuC;QAAvC,cAAS,GAAT,SAAS,CAA8B;IAAG,CAAC;CACnE;AAFD,gDAEC","sourcesContent":["import { ApiDocModel } from '@webpieces/api-doc-model';\nimport { JsonObject } from '../json/JsonObject';\nimport { ApiEntry, OpenApiManifest } from '../manifest/OpenApiManifest';\n\n/** One manifest entry, paired with the model extracted from the file it names. */\nexport class ContractModel {\n constructor(\n readonly entry: ApiEntry,\n readonly model: ApiDocModel,\n /** Absolute path to the contract, so a refusal can name a file somebody can open. */\n readonly file: string,\n ) {}\n}\n\n/**\n * One response header, with its name already FOLDED out of the constant the manifest named.\n *\n * The folding happens before generation, not during it, so the generator is a pure function of\n * already-established facts: everything that can fail by reading the world has failed by then.\n */\nexport class ResolvedResponseHeader {\n constructor(\n readonly headerName: string,\n readonly description: string | undefined,\n ) {}\n}\n\n/**\n * Everything generation needs, with every file already read and every constant already folded.\n *\n * Separating this from {@link OpenApiGenerator} is what makes the generator testable without a\n * filesystem — and, more usefully, what makes the two documents provably one generation pass: they\n * are rendered from THIS value, twice, with only the hidden-endpoint filter differing.\n */\nexport class GenerationInputs {\n constructor(\n readonly manifestPath: string,\n readonly manifest: OpenApiManifest,\n readonly contracts: readonly ContractModel[],\n /** The document-wide error body's model, when the manifest declared one. */\n readonly errorType: ApiDocModel | undefined,\n readonly responseHeaders: readonly ResolvedResponseHeader[],\n /** The markdown preamble's TEXT, already read from `descriptionFile`. */\n readonly description: string | undefined,\n ) {}\n}\n\n/** ONE rendered document and the name it is written under. */\nexport class GeneratedDocument {\n constructor(\n /** Without an extension — `--format` decides whether it is `.json`, `.yaml` or both. */\n readonly fileName: string,\n readonly document: JsonObject,\n ) {}\n}\n\n/**\n * The documents this generation pass produced — one per `@ApiType` some contract declared, and no\n * others.\n *\n * `diff full-private-openapi.json public-openapi.json` is the complete list of what these contracts\n * do not show a customer, and both are committed, so hiding a method shows up as a diff in the PR\n * that hides it. That is the property the pair exists for.\n */\nexport class GeneratedDocuments {\n constructor(readonly documents: readonly GeneratedDocument[]) {}\n}\n"]}
@@ -0,0 +1,113 @@
1
+ import { GeneratedDocuments, GenerationInputs } from './GenerationInputs';
2
+ /**
3
+ * {@link GenerationInputs} -> one OpenAPI 3.1.0 document per `@ApiType` some contract declares.
4
+ *
5
+ * ## 3.1, because it is what the model can state HONESTLY
6
+ *
7
+ * `type: [T, "null"]` instead of a `nullable` keyword; a `description` legal beside a `$ref`; a
8
+ * top-level `webhooks:` block. Each of those is a fact the model holds that 3.0 would have forced
9
+ * this renderer to drop or to lie about. 3.1's schema dialect is also JSON Schema 2020-12, which is
10
+ * what MCP `tools/list` speaks.
11
+ *
12
+ * ## ONE render function, called once per document
13
+ *
14
+ * ```
15
+ * ApiDocModel --> render(selection) --+--> full-private-openapi.json SVC_TO_SVC contracts
16
+ * +--> public-openapi.json EXTERNAL_CUSTOMER contracts, minus hidden methods
17
+ * +--> mcp-openapi.json MCP contracts
18
+ * ```
19
+ *
20
+ * `components.schemas` is built by walking OUTWARD from the operations the selection accepted, so an
21
+ * unselected operation's DTOs are never constructed. See {@link DocumentSelection} for why that
22
+ * asymmetry — a bug emits nothing, rather than shipping an unreleased feature's schemas with only
23
+ * its URL removed — decides the whole design.
24
+ *
25
+ * ## It REFUSES to write an unmapped field
26
+ *
27
+ * An unmapped type renders as an empty schema, which in JSON Schema means "anything". Publishing one
28
+ * is a green build handing a partner a field with no shape, so the guard names the JSON pointer of
29
+ * every one and exits non-zero. There is deliberately NO flag to switch it off: the cure is at the
30
+ * contract, by naming the type.
31
+ */
32
+ export declare class OpenApiGenerator {
33
+ private readonly security;
34
+ /**
35
+ * ONE document per `@ApiType` some contract declares, and no others.
36
+ *
37
+ * A document nobody asked for is not written empty — it is not written. That is the ONE
38
+ * conditional in the whole pipeline, and it lives here rather than in three places: no contract
39
+ * declaring `MCP` means no `mcp-openapi.json`, with no second "is it empty?" rule to keep in step
40
+ * with the first.
41
+ */
42
+ generate(inputs: GenerationInputs): GeneratedDocuments;
43
+ /** ONE document, from the operations this selection accepts and nothing else. */
44
+ private render;
45
+ /**
46
+ * A webhook is served by the PARTNER, so neither our failure envelope nor the header we stamp on
47
+ * our own responses is true of it. Publishing either would document their server, not ours.
48
+ */
49
+ private webhookContract;
50
+ /** One contract's ACCEPTED endpoints, into `paths` or into `webhooks`. */
51
+ private renderContract;
52
+ /**
53
+ * A webhook is keyed by the EVENT NAME, with the `@ApiPath` base deliberately NOT prepended.
54
+ *
55
+ * There is no url of ours here — the partner hosts the endpoint, at whatever path they choose —
56
+ * so publishing `/our-base/delivered` would document a route that exists nowhere. What we are
57
+ * naming is the event we will send.
58
+ */
59
+ private eventName;
60
+ /**
61
+ * Every named type from every contract, merged.
62
+ *
63
+ * Two contracts declaring DIFFERENT types under one name is a hard failure, not a first-wins
64
+ * merge: `components.schemas` is keyed by name, so one of the two would be published as the
65
+ * other's shape, and the operation referring to it would be quietly wrong.
66
+ */
67
+ private mergedTypes;
68
+ /** Enough of a type's shape to tell two same-named types apart without comparing prose. */
69
+ private signature;
70
+ /**
71
+ * The ONE api-key regime this document publishes.
72
+ *
73
+ * Two regimes in one document is a hard failure: `securitySchemeNames` is a single ordered list,
74
+ * so there is no honest way to say which regime a given name belongs to, and a document that
75
+ * guessed would publish one regime's header names under the other's scheme keys.
76
+ */
77
+ private singleApiKey;
78
+ /**
79
+ * True when EVERY selected operation demands the credential, which is what licenses hoisting the
80
+ * requirement to the document. A document that mixes credentialled and uncredentialled routes
81
+ * stamps it per-operation instead — hoisting there would tell a partner that a public endpoint
82
+ * needs a key.
83
+ *
84
+ * Judged per DOCUMENT, over the operations that document actually contains, because that is the
85
+ * only question the document's own `security` block answers.
86
+ */
87
+ private everyOperationIsCovered;
88
+ /**
89
+ * The endpoints this selection accepts that are routes on OUR server. A webhook is somebody
90
+ * else's route, so it never counts toward our security requirement or our regime.
91
+ */
92
+ private selectedEndpoints;
93
+ /** `info`, whose `description` is the ONE piece of prose that differs between the documents. */
94
+ private info;
95
+ private servers;
96
+ /**
97
+ * `tags[]` in MANIFEST ORDER, because that order IS the published sidebar — one of the two
98
+ * orders in this document that carries meaning and is therefore NOT sorted.
99
+ */
100
+ private tagList;
101
+ private tagProse;
102
+ private errorResponses;
103
+ private errorSchemaRef;
104
+ /** `components.headers`, one entry per declared response header. */
105
+ private headers;
106
+ /** The `$ref`s every SUCCESS response carries at those headers. */
107
+ private headerRefs;
108
+ /**
109
+ * The guard. An unmapped field is a published partner-facing field with no shape, and there is
110
+ * deliberately no flag to switch this off — the cure is at the contract, by naming the type.
111
+ */
112
+ private refuseUnmappedFields;
113
+ }