@webpieces/docs-site 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 (95) hide show
  1. package/README.md +83 -0
  2. package/package.json +28 -0
  3. package/src/DocsSiteError.d.ts +24 -0
  4. package/src/DocsSiteError.js +33 -0
  5. package/src/DocsSiteError.js.map +1 -0
  6. package/src/assets/SiteScript.d.ts +11 -0
  7. package/src/assets/SiteScript.js +88 -0
  8. package/src/assets/SiteScript.js.map +1 -0
  9. package/src/assets/SiteStyles.d.ts +13 -0
  10. package/src/assets/SiteStyles.js +106 -0
  11. package/src/assets/SiteStyles.js.map +1 -0
  12. package/src/cli/DocsSiteCli.d.ts +37 -0
  13. package/src/cli/DocsSiteCli.js +116 -0
  14. package/src/cli/DocsSiteCli.js.map +1 -0
  15. package/src/cli/WpDocsSiteMain.d.ts +21 -0
  16. package/src/cli/WpDocsSiteMain.js +60 -0
  17. package/src/cli/WpDocsSiteMain.js.map +1 -0
  18. package/src/cli/wp-docs-site.d.ts +2 -0
  19. package/src/cli/wp-docs-site.js +22 -0
  20. package/src/cli/wp-docs-site.js.map +1 -0
  21. package/src/emit/SiteWriter.d.ts +13 -0
  22. package/src/emit/SiteWriter.js +34 -0
  23. package/src/emit/SiteWriter.js.map +1 -0
  24. package/src/index.d.ts +41 -0
  25. package/src/index.js +105 -0
  26. package/src/index.js.map +1 -0
  27. package/src/manifest/DocsManifest.d.ts +28 -0
  28. package/src/manifest/DocsManifest.js +44 -0
  29. package/src/manifest/DocsManifest.js.map +1 -0
  30. package/src/manifest/ProseLoader.d.ts +23 -0
  31. package/src/manifest/ProseLoader.js +81 -0
  32. package/src/manifest/ProseLoader.js.map +1 -0
  33. package/src/markdown/Markdown.d.ts +47 -0
  34. package/src/markdown/Markdown.js +178 -0
  35. package/src/markdown/Markdown.js.map +1 -0
  36. package/src/render/CardRenderer.d.ts +28 -0
  37. package/src/render/CardRenderer.js +70 -0
  38. package/src/render/CardRenderer.js.map +1 -0
  39. package/src/render/FieldTreeRenderer.d.ts +34 -0
  40. package/src/render/FieldTreeRenderer.js +91 -0
  41. package/src/render/FieldTreeRenderer.js.map +1 -0
  42. package/src/render/Html.d.ts +14 -0
  43. package/src/render/Html.js +28 -0
  44. package/src/render/Html.js.map +1 -0
  45. package/src/render/Layout.d.ts +21 -0
  46. package/src/render/Layout.js +74 -0
  47. package/src/render/Layout.js.map +1 -0
  48. package/src/render/NavModel.d.ts +47 -0
  49. package/src/render/NavModel.js +86 -0
  50. package/src/render/NavModel.js.map +1 -0
  51. package/src/render/OperationPageRenderer.d.ts +44 -0
  52. package/src/render/OperationPageRenderer.js +102 -0
  53. package/src/render/OperationPageRenderer.js.map +1 -0
  54. package/src/render/SitePage.d.ts +30 -0
  55. package/src/render/SitePage.js +46 -0
  56. package/src/render/SitePage.js.map +1 -0
  57. package/src/render/SiteRenderer.d.ts +34 -0
  58. package/src/render/SiteRenderer.js +121 -0
  59. package/src/render/SiteRenderer.js.map +1 -0
  60. package/src/render/SiteUrls.d.ts +24 -0
  61. package/src/render/SiteUrls.js +37 -0
  62. package/src/render/SiteUrls.js.map +1 -0
  63. package/src/samples/CodeSamples.d.ts +49 -0
  64. package/src/samples/CodeSamples.js +166 -0
  65. package/src/samples/CodeSamples.js.map +1 -0
  66. package/src/serve/DevServer.d.ts +35 -0
  67. package/src/serve/DevServer.js +109 -0
  68. package/src/serve/DevServer.js.map +1 -0
  69. package/src/spec/ApiSpec.d.ts +110 -0
  70. package/src/spec/ApiSpec.js +165 -0
  71. package/src/spec/ApiSpec.js.map +1 -0
  72. package/src/spec/ExampleBuilder.d.ts +32 -0
  73. package/src/spec/ExampleBuilder.js +119 -0
  74. package/src/spec/ExampleBuilder.js.map +1 -0
  75. package/src/spec/FieldTree.d.ts +75 -0
  76. package/src/spec/FieldTree.js +155 -0
  77. package/src/spec/FieldTree.js.map +1 -0
  78. package/src/spec/JsonNode.d.ts +53 -0
  79. package/src/spec/JsonNode.js +93 -0
  80. package/src/spec/JsonNode.js.map +1 -0
  81. package/src/spec/SchemaLens.d.ts +57 -0
  82. package/src/spec/SchemaLens.js +162 -0
  83. package/src/spec/SchemaLens.js.map +1 -0
  84. package/src/spec/SchemaShape.d.ts +22 -0
  85. package/src/spec/SchemaShape.js +61 -0
  86. package/src/spec/SchemaShape.js.map +1 -0
  87. package/src/spec/Slug.d.ts +16 -0
  88. package/src/spec/Slug.js +43 -0
  89. package/src/spec/Slug.js.map +1 -0
  90. package/src/spec/SpecLoader.d.ts +26 -0
  91. package/src/spec/SpecLoader.js +64 -0
  92. package/src/spec/SpecLoader.js.map +1 -0
  93. package/src/spec/SpecReader.d.ts +46 -0
  94. package/src/spec/SpecReader.js +174 -0
  95. package/src/spec/SpecReader.js.map +1 -0
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiSpec = exports.NamedSchema = exports.TagSection = exports.OperationInfo = exports.ResponseInfo = exports.SecuritySchemeInfo = exports.ServerInfo = void 0;
4
+ /**
5
+ * The model this package renders. It is read from a document STRUCTURALLY (see {@link JsonNode}), so
6
+ * it is deliberately NOT `@webpieces/openapi-generator`'s document classes even though it looks like
7
+ * them: a compile-time dependency on the generator would make "generated by us" a precondition for
8
+ * "renderable by us", and #985 is explicit that any conforming 3.0 or 3.1 document renders here.
9
+ *
10
+ * Everything below is data, so everything below is a class (`CLAUDE.md` §1).
11
+ */
12
+ /** One entry of the document's `servers[]`. The first one is what the code samples call. */
13
+ class ServerInfo {
14
+ url;
15
+ description;
16
+ constructor(url, description) {
17
+ this.url = url;
18
+ this.description = description;
19
+ }
20
+ }
21
+ exports.ServerInfo = ServerInfo;
22
+ /**
23
+ * One published security scheme. Only the fields a partner has to ACT on are carried: what to send
24
+ * and where. `headerName` is empty for a scheme that is not sent in a header, and the code samples
25
+ * then omit it rather than inventing a header name the server never reads.
26
+ */
27
+ class SecuritySchemeInfo {
28
+ key;
29
+ kind;
30
+ sentIn;
31
+ headerName;
32
+ description;
33
+ constructor(key, kind, sentIn, headerName, description) {
34
+ this.key = key;
35
+ this.kind = kind;
36
+ this.sentIn = sentIn;
37
+ this.headerName = headerName;
38
+ this.description = description;
39
+ }
40
+ }
41
+ exports.SecuritySchemeInfo = SecuritySchemeInfo;
42
+ /** One response of one operation. */
43
+ class ResponseInfo {
44
+ status;
45
+ description;
46
+ schema;
47
+ constructor(status, description, schema) {
48
+ this.status = status;
49
+ this.description = description;
50
+ this.schema = schema;
51
+ }
52
+ }
53
+ exports.ResponseInfo = ResponseInfo;
54
+ /**
55
+ * One rendered page's worth of operation.
56
+ *
57
+ * A WEBHOOK carries `isWebhook`, and with it no method, no path and no code samples. The url belongs
58
+ * to the PARTNER, so a curl of ours against one would invite them to call something that does not
59
+ * exist — see `responsibilities.md`.
60
+ */
61
+ class OperationInfo {
62
+ name;
63
+ slug;
64
+ httpMethod;
65
+ path;
66
+ description;
67
+ tag;
68
+ requestSchema;
69
+ responses;
70
+ isWebhook;
71
+ securityKeys;
72
+ eventName;
73
+ constructor(name, slug, httpMethod, path, description, tag, requestSchema, responses, isWebhook, securityKeys,
74
+ /**
75
+ * The event NAME a webhook is keyed by in the document's `webhooks:` block, and the empty
76
+ * string for an ordinary operation. It is carried separately from `name` because the two
77
+ * are different published facts — `orderStateChanged` is what we call the method,
78
+ * `order.state-changed` is what the partner matches on the wire.
79
+ */
80
+ eventName) {
81
+ this.name = name;
82
+ this.slug = slug;
83
+ this.httpMethod = httpMethod;
84
+ this.path = path;
85
+ this.description = description;
86
+ this.tag = tag;
87
+ this.requestSchema = requestSchema;
88
+ this.responses = responses;
89
+ this.isWebhook = isWebhook;
90
+ this.securityKeys = securityKeys;
91
+ this.eventName = eventName;
92
+ }
93
+ }
94
+ exports.OperationInfo = OperationInfo;
95
+ /**
96
+ * One nav section: an OpenAPI tag and the operations under it, webhooks appended after the
97
+ * operations that share the tag. EVERY tag gets one — a tag missing from the sidebar is an operation
98
+ * a partner cannot find, which reads exactly like an operation that does not exist.
99
+ */
100
+ class TagSection {
101
+ name;
102
+ description;
103
+ operations;
104
+ constructor(name, description, operations) {
105
+ this.name = name;
106
+ this.description = description;
107
+ this.operations = operations;
108
+ }
109
+ }
110
+ exports.TagSection = TagSection;
111
+ /**
112
+ * One named schema from `components.schemas`.
113
+ *
114
+ * `hasOwnPage` is true for an OBJECT only. A named enum or scalar alias stays inline at every use
115
+ * site, because a page carrying one line of chips is a click that costs the reader the context they
116
+ * were reading in.
117
+ */
118
+ class NamedSchema {
119
+ name;
120
+ slug;
121
+ node;
122
+ hasOwnPage;
123
+ constructor(name, slug, node, hasOwnPage) {
124
+ this.name = name;
125
+ this.slug = slug;
126
+ this.node = node;
127
+ this.hasOwnPage = hasOwnPage;
128
+ }
129
+ }
130
+ exports.NamedSchema = NamedSchema;
131
+ /** The whole document, read once. */
132
+ class ApiSpec {
133
+ title;
134
+ version;
135
+ description;
136
+ servers;
137
+ sections;
138
+ securitySchemes;
139
+ schemas;
140
+ documentSecurityKeys;
141
+ constructor(title, version, description, servers, sections, securitySchemes, schemas, documentSecurityKeys) {
142
+ this.title = title;
143
+ this.version = version;
144
+ this.description = description;
145
+ this.servers = servers;
146
+ this.sections = sections;
147
+ this.securitySchemes = securitySchemes;
148
+ this.schemas = schemas;
149
+ this.documentSecurityKeys = documentSecurityKeys;
150
+ }
151
+ /** The named schema `name` refers to, or `undefined` when the document does not define it. */
152
+ schemaNamed(name) {
153
+ return this.schemas.find((schema) => schema.name === name);
154
+ }
155
+ /** Every operation, across every section, in published order. */
156
+ operations() {
157
+ const all = [];
158
+ for (const section of this.sections) {
159
+ all.push(...section.operations);
160
+ }
161
+ return all;
162
+ }
163
+ }
164
+ exports.ApiSpec = ApiSpec;
165
+ //# sourceMappingURL=ApiSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiSpec.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/spec/ApiSpec.ts"],"names":[],"mappings":"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","sourcesContent":["import { JsonNode } from './JsonNode';\n\n/**\n * The model this package renders. It is read from a document STRUCTURALLY (see {@link JsonNode}), so\n * it is deliberately NOT `@webpieces/openapi-generator`'s document classes even though it looks like\n * them: a compile-time dependency on the generator would make \"generated by us\" a precondition for\n * \"renderable by us\", and #985 is explicit that any conforming 3.0 or 3.1 document renders here.\n *\n * Everything below is data, so everything below is a class (`CLAUDE.md` §1).\n */\n\n/** One entry of the document's `servers[]`. The first one is what the code samples call. */\nexport class ServerInfo {\n constructor(\n readonly url: string,\n readonly description: string | undefined,\n ) {}\n}\n\n/**\n * One published security scheme. Only the fields a partner has to ACT on are carried: what to send\n * and where. `headerName` is empty for a scheme that is not sent in a header, and the code samples\n * then omit it rather than inventing a header name the server never reads.\n */\nexport class SecuritySchemeInfo {\n constructor(\n readonly key: string,\n readonly kind: string,\n readonly sentIn: string,\n readonly headerName: string,\n readonly description: string | undefined,\n ) {}\n}\n\n/** One response of one operation. */\nexport class ResponseInfo {\n constructor(\n readonly status: string,\n readonly description: string,\n readonly schema: JsonNode | undefined,\n ) {}\n}\n\n/**\n * One rendered page's worth of operation.\n *\n * A WEBHOOK carries `isWebhook`, and with it no method, no path and no code samples. The url belongs\n * to the PARTNER, so a curl of ours against one would invite them to call something that does not\n * exist — see `responsibilities.md`.\n */\nexport class OperationInfo {\n constructor(\n readonly name: string,\n readonly slug: string,\n readonly httpMethod: string,\n readonly path: string,\n readonly description: string,\n readonly tag: string,\n readonly requestSchema: JsonNode | undefined,\n readonly responses: readonly ResponseInfo[],\n readonly isWebhook: boolean,\n readonly securityKeys: readonly string[],\n /**\n * The event NAME a webhook is keyed by in the document's `webhooks:` block, and the empty\n * string for an ordinary operation. It is carried separately from `name` because the two\n * are different published facts — `orderStateChanged` is what we call the method,\n * `order.state-changed` is what the partner matches on the wire.\n */\n readonly eventName: string,\n ) {}\n}\n\n/**\n * One nav section: an OpenAPI tag and the operations under it, webhooks appended after the\n * operations that share the tag. EVERY tag gets one — a tag missing from the sidebar is an operation\n * a partner cannot find, which reads exactly like an operation that does not exist.\n */\nexport class TagSection {\n constructor(\n readonly name: string,\n readonly description: string,\n readonly operations: readonly OperationInfo[],\n ) {}\n}\n\n/**\n * One named schema from `components.schemas`.\n *\n * `hasOwnPage` is true for an OBJECT only. A named enum or scalar alias stays inline at every use\n * site, because a page carrying one line of chips is a click that costs the reader the context they\n * were reading in.\n */\nexport class NamedSchema {\n constructor(\n readonly name: string,\n readonly slug: string,\n readonly node: JsonNode,\n readonly hasOwnPage: boolean,\n ) {}\n}\n\n/** The whole document, read once. */\nexport class ApiSpec {\n constructor(\n readonly title: string,\n readonly version: string,\n readonly description: string,\n readonly servers: readonly ServerInfo[],\n readonly sections: readonly TagSection[],\n readonly securitySchemes: readonly SecuritySchemeInfo[],\n readonly schemas: readonly NamedSchema[],\n readonly documentSecurityKeys: readonly string[],\n ) {}\n\n /** The named schema `name` refers to, or `undefined` when the document does not define it. */\n schemaNamed(name: string): NamedSchema | undefined {\n return this.schemas.find((schema: NamedSchema): boolean => schema.name === name);\n }\n\n /** Every operation, across every section, in published order. */\n operations(): readonly OperationInfo[] {\n const all: OperationInfo[] = [];\n for (const section of this.sections) {\n all.push(...section.operations);\n }\n return all;\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { JsonNode, JsonValue } from './JsonNode';
2
+ import { SchemaLens } from './SchemaLens';
3
+ /**
4
+ * Builds the example body a code sample sends, FULLY RESOLVED — every `$ref` followed, because a
5
+ * hyperlink cannot live inside a JSON sample and a partner copying one needs every nested field.
6
+ *
7
+ * ## Cycle safety includes the ROOT
8
+ *
9
+ * A schema is marked seen BEFORE its own properties are read, not after. Mark it afterwards and a
10
+ * self-referential DTO — `Category { parent: Category }`, or any pair that refers to each other —
11
+ * recurses until the stack dies, and the failure is a crash with no pointer in it rather than a
12
+ * page. A second visit renders `{}`, which is honest: the sample is showing the shape once.
13
+ *
14
+ * The values are DERIVED, never invented prose: an enum's first published value, a `date-time`'s
15
+ * canonical spelling, the field's own name for a free string. A sample that made up plausible
16
+ * content would read as data a partner can expect us to return.
17
+ */
18
+ export declare class ExampleBuilder {
19
+ private readonly lens;
20
+ private readonly shape;
21
+ constructor(lens: SchemaLens);
22
+ /** The example body for a schema, or `undefined` when there is no schema to build one from. */
23
+ build(schema: JsonNode | undefined): JsonValue | undefined;
24
+ private valueOf;
25
+ private inlineValueOf;
26
+ private mergedValueOf;
27
+ private typedValueOf;
28
+ private objectValueOf;
29
+ private stringValueOf;
30
+ /** The sample serialization: two-space JSON, which is what a partner pastes into their client. */
31
+ render(value: JsonValue | undefined): string;
32
+ }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExampleBuilder = void 0;
4
+ const SchemaShape_1 = require("./SchemaShape");
5
+ /**
6
+ * Builds the example body a code sample sends, FULLY RESOLVED — every `$ref` followed, because a
7
+ * hyperlink cannot live inside a JSON sample and a partner copying one needs every nested field.
8
+ *
9
+ * ## Cycle safety includes the ROOT
10
+ *
11
+ * A schema is marked seen BEFORE its own properties are read, not after. Mark it afterwards and a
12
+ * self-referential DTO — `Category { parent: Category }`, or any pair that refers to each other —
13
+ * recurses until the stack dies, and the failure is a crash with no pointer in it rather than a
14
+ * page. A second visit renders `{}`, which is honest: the sample is showing the shape once.
15
+ *
16
+ * The values are DERIVED, never invented prose: an enum's first published value, a `date-time`'s
17
+ * canonical spelling, the field's own name for a free string. A sample that made up plausible
18
+ * content would read as data a partner can expect us to return.
19
+ */
20
+ class ExampleBuilder {
21
+ lens;
22
+ shape = new SchemaShape_1.SchemaShape();
23
+ constructor(lens) {
24
+ this.lens = lens;
25
+ }
26
+ /** The example body for a schema, or `undefined` when there is no schema to build one from. */
27
+ build(schema) {
28
+ if (schema === undefined) {
29
+ return undefined;
30
+ }
31
+ return this.valueOf(schema, '', new Set());
32
+ }
33
+ valueOf(schema, fieldName, seen) {
34
+ const target = this.lens.refTargetOf(schema);
35
+ if (target !== undefined) {
36
+ if (seen.has(target.name)) {
37
+ return {};
38
+ }
39
+ const deeper = new Set(seen);
40
+ deeper.add(target.name);
41
+ return this.valueOf(this.lens.resolve(schema), fieldName, deeper);
42
+ }
43
+ return this.inlineValueOf(schema, fieldName, seen);
44
+ }
45
+ inlineValueOf(schema, fieldName, seen) {
46
+ const branches = schema.list('oneOf');
47
+ if (branches.length > 0) {
48
+ return this.valueOf(branches[0], fieldName, seen);
49
+ }
50
+ const merged = schema.list('allOf');
51
+ if (merged.length > 0) {
52
+ return this.mergedValueOf(merged, fieldName, seen);
53
+ }
54
+ const enumValues = this.lens.enumValuesOf(schema);
55
+ if (enumValues.length > 0) {
56
+ return enumValues[0];
57
+ }
58
+ return this.typedValueOf(schema, fieldName, seen);
59
+ }
60
+ mergedValueOf(branches, fieldName, seen) {
61
+ const merged = {};
62
+ for (const branch of branches) {
63
+ const value = this.valueOf(branch, fieldName, seen);
64
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
65
+ Object.assign(merged, value);
66
+ }
67
+ }
68
+ return merged;
69
+ }
70
+ typedValueOf(schema, fieldName, seen) {
71
+ const declared = this.shape.declaredType(schema);
72
+ if (declared === 'array') {
73
+ return [this.valueOf(schema.at('items'), fieldName, seen)];
74
+ }
75
+ if (declared === 'object' || schema.at('properties').isObject()) {
76
+ return this.objectValueOf(schema, seen);
77
+ }
78
+ if (declared === 'integer' || declared === 'number') {
79
+ return 0;
80
+ }
81
+ if (declared === 'boolean') {
82
+ return true;
83
+ }
84
+ return this.stringValueOf(schema, fieldName);
85
+ }
86
+ objectValueOf(schema, seen) {
87
+ const body = {};
88
+ for (const property of schema.at('properties').entries()) {
89
+ body[property.key] = this.valueOf(property.value, property.key, seen);
90
+ }
91
+ if (schema.at('properties').entries().length === 0 &&
92
+ schema.at('additionalProperties').isObject()) {
93
+ body['key'] = this.valueOf(schema.at('additionalProperties'), 'value', seen);
94
+ }
95
+ return body;
96
+ }
97
+ stringValueOf(schema, fieldName) {
98
+ const format = schema.text('format');
99
+ if (format === 'date-time') {
100
+ return '2026-01-31T09:30:00Z';
101
+ }
102
+ if (format === 'date') {
103
+ return '2026-01-31';
104
+ }
105
+ if (format === 'uuid') {
106
+ return '00000000-0000-0000-0000-000000000000';
107
+ }
108
+ return fieldName === '' ? 'string' : fieldName;
109
+ }
110
+ /** The sample serialization: two-space JSON, which is what a partner pastes into their client. */
111
+ render(value) {
112
+ if (value === undefined) {
113
+ return '';
114
+ }
115
+ return JSON.stringify(value, null, 2);
116
+ }
117
+ }
118
+ exports.ExampleBuilder = ExampleBuilder;
119
+ //# sourceMappingURL=ExampleBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExampleBuilder.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/spec/ExampleBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import { JsonMap, JsonNode, JsonValue } from './JsonNode';\nimport { SchemaLens } from './SchemaLens';\nimport { SchemaShape } from './SchemaShape';\n\n/**\n * Builds the example body a code sample sends, FULLY RESOLVED — every `$ref` followed, because a\n * hyperlink cannot live inside a JSON sample and a partner copying one needs every nested field.\n *\n * ## Cycle safety includes the ROOT\n *\n * A schema is marked seen BEFORE its own properties are read, not after. Mark it afterwards and a\n * self-referential DTO — `Category { parent: Category }`, or any pair that refers to each other —\n * recurses until the stack dies, and the failure is a crash with no pointer in it rather than a\n * page. A second visit renders `{}`, which is honest: the sample is showing the shape once.\n *\n * The values are DERIVED, never invented prose: an enum's first published value, a `date-time`'s\n * canonical spelling, the field's own name for a free string. A sample that made up plausible\n * content would read as data a partner can expect us to return.\n */\nexport class ExampleBuilder {\n private readonly shape = new SchemaShape();\n\n constructor(private readonly lens: SchemaLens) {}\n\n /** The example body for a schema, or `undefined` when there is no schema to build one from. */\n build(schema: JsonNode | undefined): JsonValue | undefined {\n if (schema === undefined) {\n return undefined;\n }\n return this.valueOf(schema, '', new Set<string>());\n }\n\n private valueOf(schema: JsonNode, fieldName: string, seen: ReadonlySet<string>): JsonValue {\n const target = this.lens.refTargetOf(schema);\n if (target !== undefined) {\n if (seen.has(target.name)) {\n return {};\n }\n const deeper = new Set<string>(seen);\n deeper.add(target.name);\n return this.valueOf(this.lens.resolve(schema), fieldName, deeper);\n }\n return this.inlineValueOf(schema, fieldName, seen);\n }\n\n private inlineValueOf(\n schema: JsonNode,\n fieldName: string,\n seen: ReadonlySet<string>,\n ): JsonValue {\n const branches = schema.list('oneOf');\n if (branches.length > 0) {\n return this.valueOf(branches[0]!, fieldName, seen);\n }\n const merged = schema.list('allOf');\n if (merged.length > 0) {\n return this.mergedValueOf(merged, fieldName, seen);\n }\n const enumValues = this.lens.enumValuesOf(schema);\n if (enumValues.length > 0) {\n return enumValues[0]!;\n }\n return this.typedValueOf(schema, fieldName, seen);\n }\n\n private mergedValueOf(\n branches: readonly JsonNode[],\n fieldName: string,\n seen: ReadonlySet<string>,\n ): JsonValue {\n const merged: JsonMap = {};\n for (const branch of branches) {\n const value = this.valueOf(branch, fieldName, seen);\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n Object.assign(merged, value);\n }\n }\n return merged;\n }\n\n private typedValueOf(\n schema: JsonNode,\n fieldName: string,\n seen: ReadonlySet<string>,\n ): JsonValue {\n const declared = this.shape.declaredType(schema);\n if (declared === 'array') {\n return [this.valueOf(schema.at('items'), fieldName, seen)];\n }\n if (declared === 'object' || schema.at('properties').isObject()) {\n return this.objectValueOf(schema, seen);\n }\n if (declared === 'integer' || declared === 'number') {\n return 0;\n }\n if (declared === 'boolean') {\n return true;\n }\n return this.stringValueOf(schema, fieldName);\n }\n\n private objectValueOf(schema: JsonNode, seen: ReadonlySet<string>): JsonValue {\n const body: JsonMap = {};\n for (const property of schema.at('properties').entries()) {\n body[property.key] = this.valueOf(property.value, property.key, seen);\n }\n if (\n schema.at('properties').entries().length === 0 &&\n schema.at('additionalProperties').isObject()\n ) {\n body['key'] = this.valueOf(schema.at('additionalProperties'), 'value', seen);\n }\n return body;\n }\n\n private stringValueOf(schema: JsonNode, fieldName: string): JsonValue {\n const format = schema.text('format');\n if (format === 'date-time') {\n return '2026-01-31T09:30:00Z';\n }\n if (format === 'date') {\n return '2026-01-31';\n }\n if (format === 'uuid') {\n return '00000000-0000-0000-0000-000000000000';\n }\n return fieldName === '' ? 'string' : fieldName;\n }\n\n /** The sample serialization: two-space JSON, which is what a partner pastes into their client. */\n render(value: JsonValue | undefined): string {\n if (value === undefined) {\n return '';\n }\n return JSON.stringify(value, null, 2);\n }\n}\n"]}
@@ -0,0 +1,75 @@
1
+ import { JsonNode } from './JsonNode';
2
+ import { NamedSchema } from './ApiSpec';
3
+ import { SchemaLens } from './SchemaLens';
4
+ /**
5
+ * One row of the parameter tree: the field name, the type label, the prose, and — for an enum — the
6
+ * COMPLETE list of possible values. All four are read from the spec; none is authored here.
7
+ */
8
+ export declare class FieldNode {
9
+ readonly name: string;
10
+ readonly typeLabel: string;
11
+ readonly description: string;
12
+ readonly required: boolean;
13
+ readonly possibleValues: readonly string[];
14
+ /** The named object this field's type is, when it has a page of its own. Else `undefined`. */
15
+ readonly linkTo: NamedSchema | undefined;
16
+ readonly children: readonly FieldNode[];
17
+ constructor(name: string, typeLabel: string, description: string, required: boolean, possibleValues: readonly string[],
18
+ /** The named object this field's type is, when it has a page of its own. Else `undefined`. */
19
+ linkTo: NamedSchema | undefined, children: readonly FieldNode[]);
20
+ }
21
+ /**
22
+ * One branch of a `oneOf` request body, keyed by its discriminator value.
23
+ *
24
+ * Every branch renders EXPANDED, one after another, rather than behind tabs. A partner writing one
25
+ * handler needs every shape at once — and a tab is invisible to Ctrl-F and to print, so the shapes
26
+ * behind the unselected tabs are, for a reader searching the page, simply not documented.
27
+ */
28
+ export declare class SchemaVariant {
29
+ readonly label: string;
30
+ readonly discriminatorValue: string;
31
+ readonly fields: readonly FieldNode[];
32
+ constructor(label: string, discriminatorValue: string, fields: readonly FieldNode[]);
33
+ }
34
+ /**
35
+ * Builds the parameter tree.
36
+ *
37
+ * ## Two trees on purpose: links here, full resolution in the example
38
+ *
39
+ * Every reference to a named OBJECT renders as a LINK to that object's own page, and the field's
40
+ * children are NOT inlined underneath it — the link IS the expansion. The generated example body
41
+ * (see `ExampleBuilder`) resolves the same references fully instead.
42
+ *
43
+ * Sharing one tree between them is the tempting simplification and it is wrong in both directions.
44
+ * A hyperlink cannot live inside a JSON sample, so a shared tree that links prints `"data": {}` on a
45
+ * partner's page; a shared tree that inlines gives every DTO as many copies as it has use sites, and
46
+ * none of them is the page a reader can link to.
47
+ *
48
+ * A named ENUM or scalar alias is NOT linked — it renders inline with its chips, because a page
49
+ * carrying one line of values costs the reader the context they were already reading.
50
+ */
51
+ export declare class FieldTreeBuilder {
52
+ private readonly lens;
53
+ private readonly shape;
54
+ constructor(lens: SchemaLens);
55
+ /** The fields of a schema — its own, plus every `allOf` branch's, in declaration order. */
56
+ fieldsOf(schema: JsonNode): readonly FieldNode[];
57
+ /**
58
+ * The `oneOf` branches of a schema, labelled by the discriminator value that selects each one.
59
+ * Empty when the schema is not a union, which is what tells a renderer to draw one flat tree.
60
+ */
61
+ variantsOf(schema: JsonNode): readonly SchemaVariant[];
62
+ /** The `propertyName` a discriminated union switches on, or the empty string when there is none. */
63
+ discriminatorPropertyOf(schema: JsonNode): string;
64
+ private discriminatorValueOf;
65
+ private fieldOf;
66
+ /** For `array<Order>`, the page `Order` lives on — the row links, the array bracket does not. */
67
+ private linkedItemOf;
68
+ /**
69
+ * The nested rows of an INLINE shape — an anonymous object, or an array of one. A named object
70
+ * never gets children here; it got a link instead, which is what stops this recursing forever
71
+ * on a self-referential DTO.
72
+ */
73
+ private childrenOf;
74
+ private requiredNamesOf;
75
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FieldTreeBuilder = exports.SchemaVariant = exports.FieldNode = void 0;
4
+ const SchemaShape_1 = require("./SchemaShape");
5
+ /**
6
+ * One row of the parameter tree: the field name, the type label, the prose, and — for an enum — the
7
+ * COMPLETE list of possible values. All four are read from the spec; none is authored here.
8
+ */
9
+ class FieldNode {
10
+ name;
11
+ typeLabel;
12
+ description;
13
+ required;
14
+ possibleValues;
15
+ linkTo;
16
+ children;
17
+ constructor(name, typeLabel, description, required, possibleValues,
18
+ /** The named object this field's type is, when it has a page of its own. Else `undefined`. */
19
+ linkTo, children) {
20
+ this.name = name;
21
+ this.typeLabel = typeLabel;
22
+ this.description = description;
23
+ this.required = required;
24
+ this.possibleValues = possibleValues;
25
+ this.linkTo = linkTo;
26
+ this.children = children;
27
+ }
28
+ }
29
+ exports.FieldNode = FieldNode;
30
+ /**
31
+ * One branch of a `oneOf` request body, keyed by its discriminator value.
32
+ *
33
+ * Every branch renders EXPANDED, one after another, rather than behind tabs. A partner writing one
34
+ * handler needs every shape at once — and a tab is invisible to Ctrl-F and to print, so the shapes
35
+ * behind the unselected tabs are, for a reader searching the page, simply not documented.
36
+ */
37
+ class SchemaVariant {
38
+ label;
39
+ discriminatorValue;
40
+ fields;
41
+ constructor(label, discriminatorValue, fields) {
42
+ this.label = label;
43
+ this.discriminatorValue = discriminatorValue;
44
+ this.fields = fields;
45
+ }
46
+ }
47
+ exports.SchemaVariant = SchemaVariant;
48
+ /**
49
+ * Builds the parameter tree.
50
+ *
51
+ * ## Two trees on purpose: links here, full resolution in the example
52
+ *
53
+ * Every reference to a named OBJECT renders as a LINK to that object's own page, and the field's
54
+ * children are NOT inlined underneath it — the link IS the expansion. The generated example body
55
+ * (see `ExampleBuilder`) resolves the same references fully instead.
56
+ *
57
+ * Sharing one tree between them is the tempting simplification and it is wrong in both directions.
58
+ * A hyperlink cannot live inside a JSON sample, so a shared tree that links prints `"data": {}` on a
59
+ * partner's page; a shared tree that inlines gives every DTO as many copies as it has use sites, and
60
+ * none of them is the page a reader can link to.
61
+ *
62
+ * A named ENUM or scalar alias is NOT linked — it renders inline with its chips, because a page
63
+ * carrying one line of values costs the reader the context they were already reading.
64
+ */
65
+ class FieldTreeBuilder {
66
+ lens;
67
+ shape = new SchemaShape_1.SchemaShape();
68
+ constructor(lens) {
69
+ this.lens = lens;
70
+ }
71
+ /** The fields of a schema — its own, plus every `allOf` branch's, in declaration order. */
72
+ fieldsOf(schema) {
73
+ const resolved = this.lens.resolve(schema);
74
+ const fields = [];
75
+ for (const branch of resolved.list('allOf')) {
76
+ fields.push(...this.fieldsOf(branch));
77
+ }
78
+ const required = this.requiredNamesOf(resolved);
79
+ for (const property of resolved.at('properties').entries()) {
80
+ fields.push(this.fieldOf(property, required.includes(property.key)));
81
+ }
82
+ return fields;
83
+ }
84
+ /**
85
+ * The `oneOf` branches of a schema, labelled by the discriminator value that selects each one.
86
+ * Empty when the schema is not a union, which is what tells a renderer to draw one flat tree.
87
+ */
88
+ variantsOf(schema) {
89
+ const branches = this.lens.branchesOf(schema);
90
+ if (branches.length === 0) {
91
+ return [];
92
+ }
93
+ const mapping = this.lens.resolve(schema).at('discriminator').at('mapping');
94
+ const variants = [];
95
+ for (const branch of branches) {
96
+ const label = this.lens.label(branch);
97
+ variants.push(new SchemaVariant(label, this.discriminatorValueOf(mapping, label), this.fieldsOf(branch)));
98
+ }
99
+ return variants;
100
+ }
101
+ /** The `propertyName` a discriminated union switches on, or the empty string when there is none. */
102
+ discriminatorPropertyOf(schema) {
103
+ return this.lens.resolve(schema).at('discriminator').text('propertyName') ?? '';
104
+ }
105
+ discriminatorValueOf(mapping, typeName) {
106
+ for (const entry of mapping.entries()) {
107
+ const pointer = entry.value.asText();
108
+ if (pointer !== undefined && pointer.endsWith(`/${typeName}`)) {
109
+ return entry.key;
110
+ }
111
+ }
112
+ return '';
113
+ }
114
+ fieldOf(property, required) {
115
+ const named = this.lens.named(property.value);
116
+ const linked = named !== undefined && named.hasOwnPage ? named : undefined;
117
+ return new FieldNode(property.key, this.lens.label(property.value), this.lens.describe(property.value), required, this.lens.enumValuesOf(property.value), linked ?? this.linkedItemOf(property.value), linked === undefined ? this.childrenOf(property.value) : []);
118
+ }
119
+ /** For `array<Order>`, the page `Order` lives on — the row links, the array bracket does not. */
120
+ linkedItemOf(property) {
121
+ if (this.shape.declaredType(this.lens.resolve(property)) !== 'array') {
122
+ return undefined;
123
+ }
124
+ const named = this.lens.named(this.lens.resolve(property).at('items'));
125
+ return named !== undefined && named.hasOwnPage ? named : undefined;
126
+ }
127
+ /**
128
+ * The nested rows of an INLINE shape — an anonymous object, or an array of one. A named object
129
+ * never gets children here; it got a link instead, which is what stops this recursing forever
130
+ * on a self-referential DTO.
131
+ */
132
+ childrenOf(property) {
133
+ const resolved = this.lens.resolve(property);
134
+ if (this.shape.declaredType(resolved) === 'array') {
135
+ const items = resolved.at('items');
136
+ return this.lens.named(items) === undefined ? this.fieldsOf(items) : [];
137
+ }
138
+ if (resolved.at('properties').isObject()) {
139
+ return this.fieldsOf(resolved);
140
+ }
141
+ return [];
142
+ }
143
+ requiredNamesOf(schema) {
144
+ const names = [];
145
+ for (const entry of schema.list('required')) {
146
+ const name = entry.asText();
147
+ if (name !== undefined) {
148
+ names.push(name);
149
+ }
150
+ }
151
+ return names;
152
+ }
153
+ }
154
+ exports.FieldTreeBuilder = FieldTreeBuilder;
155
+ //# sourceMappingURL=FieldTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldTree.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/spec/FieldTree.ts"],"names":[],"mappings":"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","sourcesContent":["import { JsonEntry, JsonNode } from './JsonNode';\nimport { NamedSchema } from './ApiSpec';\nimport { SchemaLens } from './SchemaLens';\nimport { SchemaShape } from './SchemaShape';\n\n/**\n * One row of the parameter tree: the field name, the type label, the prose, and — for an enum — the\n * COMPLETE list of possible values. All four are read from the spec; none is authored here.\n */\nexport class FieldNode {\n constructor(\n readonly name: string,\n readonly typeLabel: string,\n readonly description: string,\n readonly required: boolean,\n readonly possibleValues: readonly string[],\n /** The named object this field's type is, when it has a page of its own. Else `undefined`. */\n readonly linkTo: NamedSchema | undefined,\n readonly children: readonly FieldNode[],\n ) {}\n}\n\n/**\n * One branch of a `oneOf` request body, keyed by its discriminator value.\n *\n * Every branch renders EXPANDED, one after another, rather than behind tabs. A partner writing one\n * handler needs every shape at once — and a tab is invisible to Ctrl-F and to print, so the shapes\n * behind the unselected tabs are, for a reader searching the page, simply not documented.\n */\nexport class SchemaVariant {\n constructor(\n readonly label: string,\n readonly discriminatorValue: string,\n readonly fields: readonly FieldNode[],\n ) {}\n}\n\n/**\n * Builds the parameter tree.\n *\n * ## Two trees on purpose: links here, full resolution in the example\n *\n * Every reference to a named OBJECT renders as a LINK to that object's own page, and the field's\n * children are NOT inlined underneath it — the link IS the expansion. The generated example body\n * (see `ExampleBuilder`) resolves the same references fully instead.\n *\n * Sharing one tree between them is the tempting simplification and it is wrong in both directions.\n * A hyperlink cannot live inside a JSON sample, so a shared tree that links prints `\"data\": {}` on a\n * partner's page; a shared tree that inlines gives every DTO as many copies as it has use sites, and\n * none of them is the page a reader can link to.\n *\n * A named ENUM or scalar alias is NOT linked — it renders inline with its chips, because a page\n * carrying one line of values costs the reader the context they were already reading.\n */\nexport class FieldTreeBuilder {\n private readonly shape = new SchemaShape();\n\n constructor(private readonly lens: SchemaLens) {}\n\n /** The fields of a schema — its own, plus every `allOf` branch's, in declaration order. */\n fieldsOf(schema: JsonNode): readonly FieldNode[] {\n const resolved = this.lens.resolve(schema);\n const fields: FieldNode[] = [];\n for (const branch of resolved.list('allOf')) {\n fields.push(...this.fieldsOf(branch));\n }\n const required = this.requiredNamesOf(resolved);\n for (const property of resolved.at('properties').entries()) {\n fields.push(this.fieldOf(property, required.includes(property.key)));\n }\n return fields;\n }\n\n /**\n * The `oneOf` branches of a schema, labelled by the discriminator value that selects each one.\n * Empty when the schema is not a union, which is what tells a renderer to draw one flat tree.\n */\n variantsOf(schema: JsonNode): readonly SchemaVariant[] {\n const branches = this.lens.branchesOf(schema);\n if (branches.length === 0) {\n return [];\n }\n const mapping = this.lens.resolve(schema).at('discriminator').at('mapping');\n const variants: SchemaVariant[] = [];\n for (const branch of branches) {\n const label = this.lens.label(branch);\n variants.push(\n new SchemaVariant(\n label,\n this.discriminatorValueOf(mapping, label),\n this.fieldsOf(branch),\n ),\n );\n }\n return variants;\n }\n\n /** The `propertyName` a discriminated union switches on, or the empty string when there is none. */\n discriminatorPropertyOf(schema: JsonNode): string {\n return this.lens.resolve(schema).at('discriminator').text('propertyName') ?? '';\n }\n\n private discriminatorValueOf(mapping: JsonNode, typeName: string): string {\n for (const entry of mapping.entries()) {\n const pointer = entry.value.asText();\n if (pointer !== undefined && pointer.endsWith(`/${typeName}`)) {\n return entry.key;\n }\n }\n return '';\n }\n\n private fieldOf(property: JsonEntry, required: boolean): FieldNode {\n const named = this.lens.named(property.value);\n const linked = named !== undefined && named.hasOwnPage ? named : undefined;\n return new FieldNode(\n property.key,\n this.lens.label(property.value),\n this.lens.describe(property.value),\n required,\n this.lens.enumValuesOf(property.value),\n linked ?? this.linkedItemOf(property.value),\n linked === undefined ? this.childrenOf(property.value) : [],\n );\n }\n\n /** For `array<Order>`, the page `Order` lives on — the row links, the array bracket does not. */\n private linkedItemOf(property: JsonNode): NamedSchema | undefined {\n if (this.shape.declaredType(this.lens.resolve(property)) !== 'array') {\n return undefined;\n }\n const named = this.lens.named(this.lens.resolve(property).at('items'));\n return named !== undefined && named.hasOwnPage ? named : undefined;\n }\n\n /**\n * The nested rows of an INLINE shape — an anonymous object, or an array of one. A named object\n * never gets children here; it got a link instead, which is what stops this recursing forever\n * on a self-referential DTO.\n */\n private childrenOf(property: JsonNode): readonly FieldNode[] {\n const resolved = this.lens.resolve(property);\n if (this.shape.declaredType(resolved) === 'array') {\n const items = resolved.at('items');\n return this.lens.named(items) === undefined ? this.fieldsOf(items) : [];\n }\n if (resolved.at('properties').isObject()) {\n return this.fieldsOf(resolved);\n }\n return [];\n }\n\n private requiredNamesOf(schema: JsonNode): readonly string[] {\n const names: string[] = [];\n for (const entry of schema.list('required')) {\n const name = entry.asText();\n if (name !== undefined) {\n names.push(name);\n }\n }\n return names;\n }\n}\n"]}
@@ -0,0 +1,53 @@
1
+ /** Everything an OpenAPI document can hold. It is JSON, so this is the whole value space. */
2
+ export type JsonValue = string | number | boolean | null | JsonValue[] | JsonMap;
3
+ /** The object case of {@link JsonValue}, named so the recursive alias can refer to it. */
4
+ export type JsonMap = {
5
+ [key: string]: JsonValue;
6
+ };
7
+ /**
8
+ * A READER over one JSON value, and the reason this package can render a document it did not
9
+ * generate.
10
+ *
11
+ * The alternative — importing `@webpieces/openapi-generator`'s document classes, which are
12
+ * structurally identical — would make "generated by us" a precondition for "renderable by us"
13
+ * (issue #985). So the document is read STRUCTURALLY: every accessor asks "is there a string at this
14
+ * key", never "is this an instance of our class", and a key that is absent or the wrong shape comes
15
+ * back `undefined` instead of throwing. A conforming document written by any other tool reads the
16
+ * same way.
17
+ *
18
+ * It is deliberately total rather than validating. A docs site that refuses to render because one
19
+ * optional `description` was a number is worse than one that renders the rest: the reader loses a
20
+ * whole reference over a field they were not looking at.
21
+ */
22
+ export declare class JsonNode {
23
+ readonly raw: unknown;
24
+ constructor(raw: unknown);
25
+ /** True when this node is a JSON object (and not an array, and not null). */
26
+ isObject(): boolean;
27
+ /** The keys of this object, in document order. Empty for anything that is not an object. */
28
+ keys(): readonly string[];
29
+ /** The child at `key`, always a node — an absent key reads as a node over `undefined`. */
30
+ at(key: string): JsonNode;
31
+ /** The string at `key`, or `undefined` when it is absent or is not a string. */
32
+ text(key: string): string | undefined;
33
+ /** This node itself as a string, or `undefined`. */
34
+ asText(): string | undefined;
35
+ /** The boolean at `key`, or `undefined`. A missing flag is not `false`; the caller decides. */
36
+ flag(key: string): boolean | undefined;
37
+ /** The number at `key`, or `undefined`. */
38
+ number(key: string): number | undefined;
39
+ /** The array at `key` as nodes. Empty when absent or not an array. */
40
+ list(key: string): readonly JsonNode[];
41
+ /** This node itself as an array of nodes. Empty when it is not an array. */
42
+ asList(): readonly JsonNode[];
43
+ /** Every own entry of this object as `[key, node]`, in document order. */
44
+ entries(): readonly JsonEntry[];
45
+ /** True when this node holds nothing at all — an absent key, or an explicit `null`. */
46
+ isAbsent(): boolean;
47
+ }
48
+ /** One `[key, value]` pair of a JSON object, as a class because it is data. */
49
+ export declare class JsonEntry {
50
+ readonly key: string;
51
+ readonly value: JsonNode;
52
+ constructor(key: string, value: JsonNode);
53
+ }