@mesh-tech/mesh-cli 0.35.2 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/docs-preview.css +1599 -0
- package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js +48 -0
- package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js.map +6 -0
- package/dist/bin/mesh.capture-history-A4IAVVIR.js +38 -0
- package/dist/bin/mesh.capture-history-A4IAVVIR.js.map +6 -0
- package/dist/bin/mesh.chunk-5A5OFSYR.js +187 -0
- package/dist/bin/mesh.chunk-5A5OFSYR.js.map +6 -0
- package/dist/bin/mesh.chunk-7DMAFUBQ.js +7715 -0
- package/dist/bin/mesh.chunk-7DMAFUBQ.js.map +6 -0
- package/dist/bin/mesh.chunk-A4DGMGHF.js +303 -0
- package/dist/bin/mesh.chunk-A4DGMGHF.js.map +6 -0
- package/dist/bin/mesh.chunk-CQ2IC4OP.js +217 -0
- package/dist/bin/mesh.chunk-CQ2IC4OP.js.map +6 -0
- package/dist/bin/mesh.chunk-F4654EPX.js +383 -0
- package/dist/bin/mesh.chunk-F4654EPX.js.map +6 -0
- package/dist/bin/mesh.chunk-IQOBTL42.js +60 -0
- package/dist/bin/mesh.chunk-IQOBTL42.js.map +6 -0
- package/dist/bin/mesh.chunk-J6DVWKRW.js +647 -0
- package/dist/bin/mesh.chunk-J6DVWKRW.js.map +6 -0
- package/dist/bin/mesh.chunk-JB7FWYSP.js +1751 -0
- package/dist/bin/mesh.chunk-JB7FWYSP.js.map +6 -0
- package/dist/bin/mesh.chunk-MCBMHWXP.js +837 -0
- package/dist/bin/mesh.chunk-MCBMHWXP.js.map +6 -0
- package/dist/bin/mesh.chunk-OW5EMVG5.js +507 -0
- package/dist/bin/mesh.chunk-OW5EMVG5.js.map +6 -0
- package/dist/bin/mesh.chunk-R5VVTXG4.js +10092 -0
- package/dist/bin/mesh.chunk-R5VVTXG4.js.map +6 -0
- package/dist/bin/mesh.chunk-VKV6Z3UI.js +1146 -0
- package/dist/bin/mesh.chunk-VKV6Z3UI.js.map +6 -0
- package/dist/bin/mesh.chunk-WFFDF346.js +59 -0
- package/dist/bin/mesh.chunk-WFFDF346.js.map +6 -0
- package/dist/bin/mesh.chunk-WOJOTUFZ.js +835 -0
- package/dist/bin/mesh.chunk-WOJOTUFZ.js.map +6 -0
- package/dist/bin/mesh.cli-reference-H3TOPIET.js +158 -0
- package/dist/bin/mesh.cli-reference-H3TOPIET.js.map +6 -0
- package/dist/bin/mesh.convert-KK6ZP256.js +274 -0
- package/dist/bin/mesh.convert-KK6ZP256.js.map +6 -0
- package/dist/bin/mesh.create-app-A5F45PWZ.js +39 -0
- package/dist/bin/mesh.create-app-A5F45PWZ.js.map +6 -0
- package/dist/bin/mesh.dev-token-server-GYDYPXYH.js +61 -0
- package/dist/bin/mesh.dev-token-server-GYDYPXYH.js.map +6 -0
- package/dist/bin/mesh.js +20 -26414
- package/dist/bin/mesh.js.map +3 -3
- package/dist/bin/mesh.login-JNHPVMCT.js +91 -0
- package/dist/bin/mesh.login-JNHPVMCT.js.map +6 -0
- package/dist/bin/mesh.platform-setup-EB64EFRN.js +37 -0
- package/dist/bin/mesh.platform-setup-EB64EFRN.js.map +6 -0
- package/dist/bin/mesh.preview-3IP34TDO.js +2410 -0
- package/dist/bin/mesh.preview-3IP34TDO.js.map +6 -0
- package/dist/bin/mesh.program-DWOKHNMQ.js +23 -0
- package/dist/bin/mesh.program-DWOKHNMQ.js.map +6 -0
- package/dist/bin/mesh.registry-OXF57OQW.js +47 -0
- package/dist/bin/mesh.registry-OXF57OQW.js.map +6 -0
- package/dist/bin/mesh.registry-identity-2HSGQGJC.js +42 -0
- package/dist/bin/mesh.registry-identity-2HSGQGJC.js.map +6 -0
- package/dist/bin/mesh.skills-MNDSMBIE.js +23 -0
- package/dist/bin/mesh.skills-MNDSMBIE.js.map +6 -0
- package/dist/bin/mesh.src-5D5DFSLX.js +1302 -0
- package/dist/bin/mesh.src-5D5DFSLX.js.map +6 -0
- package/dist/bin/mesh.src-G7TJBLNJ.js +40 -0
- package/dist/bin/mesh.src-G7TJBLNJ.js.map +6 -0
- package/dist/bin/mesh.stack-EFTFAOCV.js +64 -0
- package/dist/bin/mesh.stack-EFTFAOCV.js.map +6 -0
- package/dist/bin/mesh.start-ARIIY6CV.js +454 -0
- package/dist/bin/mesh.start-ARIIY6CV.js.map +6 -0
- package/dist/bin/mesh.wizard-ROAQAGSF.js +500 -0
- package/dist/bin/mesh.wizard-ROAQAGSF.js.map +6 -0
- package/dist/build-info.json +2 -2
- package/dist/src/commands/app-check.d.ts +2 -1
- package/dist/src/commands/app-check.js +32 -0
- package/dist/src/commands/create-app.d.ts +1 -0
- package/dist/src/commands/create-app.js +4 -0
- package/dist/src/commands/dev.d.ts +6 -0
- package/dist/src/commands/dev.js +152 -3
- package/dist/src/commands/docs.js +33 -0
- package/dist/src/commands/local/act-as-local.d.ts +5 -0
- package/dist/src/commands/local/act-as-local.js +41 -0
- package/dist/src/commands/local/auth-provision.d.ts +1 -0
- package/dist/src/commands/local/auth-provision.js +1 -0
- package/dist/src/commands/local/index.js +4 -0
- package/dist/src/commands/local/local-api-key.d.ts +44 -0
- package/dist/src/commands/local/local-api-key.js +115 -0
- package/dist/src/commands/local/local-spicedb.d.ts +95 -0
- package/dist/src/commands/local/local-spicedb.js +304 -0
- package/dist/src/commands/local/publish-docs.d.ts +81 -0
- package/dist/src/commands/local/publish-docs.js +328 -0
- package/dist/src/commands/local/seed-zitadel.js +2 -0
- package/dist/src/commands/temporal.js +191 -61
- package/dist/src/commands/temporal.js.map +3 -3
- package/dist/src/docs/convert.d.ts +35 -0
- package/dist/src/docs/convert.js +276 -0
- package/dist/src/docs/preview-source.d.ts +13 -0
- package/dist/src/docs/preview-source.js +58 -0
- package/dist/src/docs/preview.d.ts +32 -0
- package/dist/src/docs/preview.js +279 -0
- package/dist/src/utils/scaffold-versions.d.ts +1 -0
- package/dist/src/utils/scaffold-versions.js +1 -0
- package/fragments/agents/AGENTS.md.hbs +27 -27
- package/fragments/base/README.md.hbs +5 -2
- package/fragments/base/index.ts.hbs +22 -33
- package/fragments/base/package.json.hbs +5 -1
- package/fragments/service/api/openapi.json.hbs +1 -1
- package/fragments/service/docs/docs.yaml.hbs +10 -0
- package/fragments/service/docs/pages/getting-started.md.hbs +6 -9
- package/package.json +19 -3
- package/skills/mesh/SKILL.md +2 -2
- package/stack/docker-compose.hub.yml +6 -0
|
@@ -0,0 +1,2410 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SANDBOX_MODE,
|
|
3
|
+
buildDocsSource,
|
|
4
|
+
docsImageUrl,
|
|
5
|
+
docsPage,
|
|
6
|
+
tryItAllows
|
|
7
|
+
} from "./mesh.chunk-J6DVWKRW.js";
|
|
8
|
+
import {
|
|
9
|
+
findPackageRoot
|
|
10
|
+
} from "./mesh.chunk-MCBMHWXP.js";
|
|
11
|
+
import {
|
|
12
|
+
logError,
|
|
13
|
+
logInfo,
|
|
14
|
+
logSuccess,
|
|
15
|
+
logWarn
|
|
16
|
+
} from "./mesh.chunk-WFFDF346.js";
|
|
17
|
+
import "./mesh.chunk-IQOBTL42.js";
|
|
18
|
+
|
|
19
|
+
// packages/mesh-cli/src/docs/preview.ts
|
|
20
|
+
import { createServer } from "node:http";
|
|
21
|
+
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
22
|
+
import { dirname, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
|
|
23
|
+
import * as React from "react";
|
|
24
|
+
import { renderToString } from "react-dom/server";
|
|
25
|
+
|
|
26
|
+
// packages/api-docs-engine/src/search.ts
|
|
27
|
+
var FIELD_WEIGHTS = {
|
|
28
|
+
name: 3,
|
|
29
|
+
id: 3,
|
|
30
|
+
entity: 2,
|
|
31
|
+
service: 1,
|
|
32
|
+
description: 1
|
|
33
|
+
};
|
|
34
|
+
function splitIdentifierWords(s) {
|
|
35
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[_\-./]+/g, " ").split(/\s+/).filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
function tokenizeIdentifier(s) {
|
|
38
|
+
return splitIdentifierWords(s).join(" ").toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
function scoreRecord(fields, tokens) {
|
|
41
|
+
let score = 0;
|
|
42
|
+
Object.keys(FIELD_WEIGHTS).forEach((f) => {
|
|
43
|
+
const text = fields[f];
|
|
44
|
+
for (const t of tokens) if (text.includes(t)) score += FIELD_WEIGHTS[f];
|
|
45
|
+
});
|
|
46
|
+
if (tokens.every((t) => fields.name.includes(t))) score += 5;
|
|
47
|
+
else if (tokens.every((t) => fields.id.includes(t))) score += 4;
|
|
48
|
+
return score;
|
|
49
|
+
}
|
|
50
|
+
function searchRecords(records, query, filters) {
|
|
51
|
+
let pool = records;
|
|
52
|
+
if (filters?.entity) pool = pool.filter((r) => r.entry.entity === filters.entity);
|
|
53
|
+
if (filters?.capability) pool = pool.filter((r) => r.entry.capability === filters.capability);
|
|
54
|
+
if (filters?.service) pool = pool.filter((r) => r.entry.service === filters.service);
|
|
55
|
+
const q = query.trim().toLowerCase();
|
|
56
|
+
if (!q) return pool.map((r) => r.entry);
|
|
57
|
+
const tokens = [...new Set(q.split(/\s+/))].filter(Boolean);
|
|
58
|
+
return pool.map((r) => ({ entry: r.entry, score: scoreRecord(r.fields, tokens) })).filter((x) => x.score > 0).sort((a, b) => b.score - a.score || a.entry.id.localeCompare(b.entry.id)).map((x) => x.entry);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// packages/api-docs-engine/src/entity.ts
|
|
62
|
+
function deriveEntity(id) {
|
|
63
|
+
const [token] = splitIdentifierWords(id);
|
|
64
|
+
return token ?? id;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// packages/api-docs-engine/src/capability.ts
|
|
68
|
+
var HEALTH = /health|heartbeat|readiness|liveness|(?:^|[^a-z])ping(?:$|[^a-z])/i;
|
|
69
|
+
var VALIDATION = /validate|verify|eligib/i;
|
|
70
|
+
var READ_VERB = /^(get|list|search|lookup|inquir|retrieve|fetch|find|query|read|view|export|download|show)/i;
|
|
71
|
+
var READ_METHODS = /* @__PURE__ */ new Set(["get", "head"]);
|
|
72
|
+
function deriveCapability(method, id) {
|
|
73
|
+
const m = method.toLowerCase();
|
|
74
|
+
if (HEALTH.test(id) || id.toLowerCase() === "status") return "health";
|
|
75
|
+
if (VALIDATION.test(id)) return "validation";
|
|
76
|
+
if (READ_METHODS.has(m)) return "read";
|
|
77
|
+
if (READ_VERB.test(id)) return "read";
|
|
78
|
+
return "mutation";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// packages/api-docs-engine/src/normalize.ts
|
|
82
|
+
var HTTP_METHODS = [
|
|
83
|
+
"get",
|
|
84
|
+
"post",
|
|
85
|
+
"put",
|
|
86
|
+
"patch",
|
|
87
|
+
"delete",
|
|
88
|
+
"head",
|
|
89
|
+
"options",
|
|
90
|
+
"trace"
|
|
91
|
+
];
|
|
92
|
+
function operationKey(op, method, route) {
|
|
93
|
+
const native = op["x-native-operation"];
|
|
94
|
+
if (typeof native === "string" && native) return native;
|
|
95
|
+
const oid = op.operationId;
|
|
96
|
+
if (typeof oid === "string" && oid) return oid;
|
|
97
|
+
return `${method.toUpperCase()} ${route}`;
|
|
98
|
+
}
|
|
99
|
+
function normalizeOps(doc, subset) {
|
|
100
|
+
const ops = [];
|
|
101
|
+
for (const [route, item] of Object.entries(doc.paths ?? {})) {
|
|
102
|
+
if (!item || typeof item !== "object") continue;
|
|
103
|
+
const record = item;
|
|
104
|
+
for (const method of HTTP_METHODS) {
|
|
105
|
+
const op = record[method];
|
|
106
|
+
if (!op || typeof op !== "object") continue;
|
|
107
|
+
const operation = op;
|
|
108
|
+
const id = operationKey(operation, method, route);
|
|
109
|
+
if (subset && !subset.has(id)) continue;
|
|
110
|
+
ops.push({ id, method, route, op: operation });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
ops.sort((a, b) => a.id.localeCompare(b.id));
|
|
114
|
+
return ops;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// packages/api-docs-engine/src/index-build.ts
|
|
118
|
+
function buildIndex(doc, subset) {
|
|
119
|
+
const entries = [];
|
|
120
|
+
for (const { id, method, route, op } of normalizeOps(doc, subset)) {
|
|
121
|
+
const domain = op["x-entity-domain"];
|
|
122
|
+
const tags = Array.isArray(op.tags) ? op.tags.filter((t) => typeof t === "string") : [];
|
|
123
|
+
entries.push({
|
|
124
|
+
id,
|
|
125
|
+
name: op.summary ?? id,
|
|
126
|
+
// Prefer the generator's curated group; fall back to the CamelCase token.
|
|
127
|
+
entity: op["x-entity-group"] ?? deriveEntity(id),
|
|
128
|
+
...domain ? { domain } : {},
|
|
129
|
+
capability: op["x-capability-kind"] ?? deriveCapability(method, id),
|
|
130
|
+
service: op.tags?.[0] ?? "Other",
|
|
131
|
+
method,
|
|
132
|
+
route,
|
|
133
|
+
...tags.length > 0 ? { tags } : {},
|
|
134
|
+
...typeof op.summary === "string" ? { summary: op.summary } : {}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
entries.sort((a, b) => a.id.localeCompare(b.id));
|
|
138
|
+
return entries;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// packages/api-docs-engine/src/schema-util.ts
|
|
142
|
+
function refName(ref) {
|
|
143
|
+
return ref ? ref.replace(/^#\/components\/schemas\//, "") : void 0;
|
|
144
|
+
}
|
|
145
|
+
function refFromSchema(schema) {
|
|
146
|
+
if (!schema) return void 0;
|
|
147
|
+
if (typeof schema.$ref === "string") return schema.$ref;
|
|
148
|
+
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
149
|
+
for (const branch of schema[key] ?? []) {
|
|
150
|
+
const ref = refFromSchema(branch);
|
|
151
|
+
if (ref) return ref;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
function resolveRef(prop) {
|
|
157
|
+
if (prop.$ref) return refName(prop.$ref);
|
|
158
|
+
const allOfRef = prop.allOf?.find((s) => s.$ref)?.$ref;
|
|
159
|
+
if (allOfRef) return refName(allOfRef);
|
|
160
|
+
const unionRef = (prop.anyOf ?? prop.oneOf)?.find((s) => s.$ref)?.$ref;
|
|
161
|
+
if (unionRef) return refName(unionRef);
|
|
162
|
+
if (prop.items) {
|
|
163
|
+
if (prop.items.$ref) return refName(prop.items.$ref);
|
|
164
|
+
const itemsAllOfRef = prop.items.allOf?.find((s) => s.$ref)?.$ref;
|
|
165
|
+
if (itemsAllOfRef) return refName(itemsAllOfRef);
|
|
166
|
+
}
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
169
|
+
function isArray(prop) {
|
|
170
|
+
return prop.type === "array" || Array.isArray(prop.type) && prop.type.includes("array");
|
|
171
|
+
}
|
|
172
|
+
var PRIMITIVES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "array"]);
|
|
173
|
+
function directType(prop) {
|
|
174
|
+
return Array.isArray(prop.type) ? prop.type.find((t) => t !== "null") : prop.type;
|
|
175
|
+
}
|
|
176
|
+
function primitiveType(prop) {
|
|
177
|
+
const direct = directType(prop);
|
|
178
|
+
if (direct && direct !== "object") return direct;
|
|
179
|
+
const union = prop.anyOf ?? prop.oneOf;
|
|
180
|
+
if (union) {
|
|
181
|
+
for (const member of union) {
|
|
182
|
+
const mt = directType(member);
|
|
183
|
+
if (mt && PRIMITIVES.has(mt)) return mt;
|
|
184
|
+
const inner = member.anyOf ?? member.oneOf;
|
|
185
|
+
if (inner) {
|
|
186
|
+
for (const m of inner) {
|
|
187
|
+
const it = directType(m);
|
|
188
|
+
if (it && PRIMITIVES.has(it)) return it;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (direct === "object") return "object";
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
var MAX_ALLOF_HOPS = 8;
|
|
197
|
+
function composesAllOf(schema) {
|
|
198
|
+
const members = Array.isArray(schema.allOf) ? schema.allOf : [];
|
|
199
|
+
return members.length > 1 || members.length > 0 && !!schema.properties;
|
|
200
|
+
}
|
|
201
|
+
function mergeAllOf(doc, schema) {
|
|
202
|
+
const properties = {};
|
|
203
|
+
const required = /* @__PURE__ */ new Set();
|
|
204
|
+
const seen = /* @__PURE__ */ new Set();
|
|
205
|
+
const schemas = doc.components?.schemas ?? {};
|
|
206
|
+
const visit = (s, hops) => {
|
|
207
|
+
if (!s || typeof s !== "object" || hops > MAX_ALLOF_HOPS) return;
|
|
208
|
+
if (typeof s.$ref === "string") {
|
|
209
|
+
const name = refName(s.$ref);
|
|
210
|
+
if (seen.has(name) || !Object.hasOwn(schemas, name)) return;
|
|
211
|
+
seen.add(name);
|
|
212
|
+
visit(schemas[name], hops + 1);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
for (const member of Array.isArray(s.allOf) ? s.allOf : []) visit(member, hops + 1);
|
|
216
|
+
Object.assign(properties, s.properties ?? {});
|
|
217
|
+
for (const name of Array.isArray(s.required) ? s.required : []) required.add(name);
|
|
218
|
+
};
|
|
219
|
+
visit(schema, 0);
|
|
220
|
+
const { allOf: _allOf, $ref: _ref, ...rest } = schema;
|
|
221
|
+
return { ...rest, properties, ...required.size > 0 ? { required: [...required] } : {} };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// packages/api-docs-engine/src/fields.ts
|
|
225
|
+
var NATIVE_PAREN = /\s*\(native:\s*([^)]+)\)\s*$/;
|
|
226
|
+
var NATIVE_BARE = /^native:\s*(.+)$/;
|
|
227
|
+
function parseDescription(raw) {
|
|
228
|
+
if (!raw) return {};
|
|
229
|
+
const paren = raw.match(NATIVE_PAREN);
|
|
230
|
+
if (paren) {
|
|
231
|
+
const description = raw.slice(0, paren.index ?? raw.length).trim();
|
|
232
|
+
return { nativeName: (paren[1] ?? "").trim() || void 0, description: description || void 0 };
|
|
233
|
+
}
|
|
234
|
+
const bare = raw.match(NATIVE_BARE);
|
|
235
|
+
if (bare) return { nativeName: (bare[1] ?? "").trim() || void 0 };
|
|
236
|
+
const trimmed = raw.trim();
|
|
237
|
+
return trimmed ? { description: trimmed } : {};
|
|
238
|
+
}
|
|
239
|
+
function descriptionOf(prop) {
|
|
240
|
+
if (prop.description) return prop.description;
|
|
241
|
+
return prop.allOf?.find((s) => s.description)?.description;
|
|
242
|
+
}
|
|
243
|
+
function typeLabel(prop, ref) {
|
|
244
|
+
if (ref) return isArray(prop) ? `${ref}[]` : ref;
|
|
245
|
+
return primitiveType(prop) ?? "unknown";
|
|
246
|
+
}
|
|
247
|
+
function isInlineObject(prop, ref) {
|
|
248
|
+
return !ref && !!prop.properties && Object.keys(prop.properties).length > 0;
|
|
249
|
+
}
|
|
250
|
+
var MAX_INLINE_DEPTH = 3;
|
|
251
|
+
function resolveFields(schema, depth) {
|
|
252
|
+
if (!schema.properties) return [];
|
|
253
|
+
const required = new Set(schema.required ?? []);
|
|
254
|
+
return Object.entries(schema.properties).map(([name, prop]) => {
|
|
255
|
+
const ref = resolveRef(prop);
|
|
256
|
+
const { description, nativeName } = parseDescription(descriptionOf(prop));
|
|
257
|
+
const node = {
|
|
258
|
+
name,
|
|
259
|
+
nativeName,
|
|
260
|
+
type: typeLabel(prop, ref),
|
|
261
|
+
ref,
|
|
262
|
+
required: required.has(name),
|
|
263
|
+
deprecated: prop.deprecated === true,
|
|
264
|
+
description,
|
|
265
|
+
enumValues: prop.enum
|
|
266
|
+
};
|
|
267
|
+
if (prop.default !== void 0) node.defaultValue = String(prop.default);
|
|
268
|
+
const canonical = prop["x-canonical-values"];
|
|
269
|
+
if (Array.isArray(canonical)) {
|
|
270
|
+
node.canonicalValues = canonical;
|
|
271
|
+
}
|
|
272
|
+
if (isInlineObject(prop, ref) && depth < MAX_INLINE_DEPTH) {
|
|
273
|
+
node.children = resolveFields(prop, depth + 1);
|
|
274
|
+
}
|
|
275
|
+
return node;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function resolveComponentFields(doc, componentName) {
|
|
279
|
+
const comp = doc.components.schemas[componentName];
|
|
280
|
+
if (!comp) return [];
|
|
281
|
+
if (!comp.properties && isArray(comp)) {
|
|
282
|
+
const itemRef = resolveRef(comp);
|
|
283
|
+
const item = itemRef ? doc.components.schemas[itemRef] : comp.items;
|
|
284
|
+
if (item?.properties) return resolveFields(item, 0);
|
|
285
|
+
}
|
|
286
|
+
if (Array.isArray(comp.allOf) && comp.allOf.length > 0) return resolveFields(mergeAllOf(doc, comp), 0);
|
|
287
|
+
if (!comp.properties) return [];
|
|
288
|
+
return resolveFields(comp, 0);
|
|
289
|
+
}
|
|
290
|
+
function resolveSchemaFields(doc, schema) {
|
|
291
|
+
if (!schema) return [];
|
|
292
|
+
if (composesAllOf(schema)) return resolveFields(mergeAllOf(doc, schema), 0);
|
|
293
|
+
const ref = refName(refFromSchema(schema));
|
|
294
|
+
if (ref) return resolveComponentFields(doc, ref);
|
|
295
|
+
if (schema.properties) return resolveFields(schema, 0);
|
|
296
|
+
if (isArray(schema)) {
|
|
297
|
+
const itemRef = resolveRef(schema);
|
|
298
|
+
const item = itemRef ? doc.components.schemas[itemRef] : schema.items;
|
|
299
|
+
if (item?.properties) return resolveFields(item, 0);
|
|
300
|
+
}
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// packages/api-docs-engine/src/sample.ts
|
|
305
|
+
var MAX_REF_DEPTH = 1;
|
|
306
|
+
var MAX_INLINE_DEPTH2 = 4;
|
|
307
|
+
function placeholder(doc, prop, depth) {
|
|
308
|
+
const ref = resolveRef(prop);
|
|
309
|
+
if (ref) {
|
|
310
|
+
const nested = depth >= MAX_REF_DEPTH ? {} : synthFromComponent(doc, ref, depth + 1);
|
|
311
|
+
return isArray(prop) ? [nested] : nested;
|
|
312
|
+
}
|
|
313
|
+
if (prop.properties && Object.keys(prop.properties).length > 0) {
|
|
314
|
+
const nested = depth >= MAX_INLINE_DEPTH2 ? {} : synthFromSchema(doc, prop, depth + 1);
|
|
315
|
+
return isArray(prop) ? [nested] : nested;
|
|
316
|
+
}
|
|
317
|
+
if (prop.enum?.length) return prop.enum[0];
|
|
318
|
+
const t = primitiveType(prop);
|
|
319
|
+
switch (t) {
|
|
320
|
+
case "number":
|
|
321
|
+
case "integer":
|
|
322
|
+
return 0;
|
|
323
|
+
case "boolean":
|
|
324
|
+
return false;
|
|
325
|
+
case "array":
|
|
326
|
+
return [];
|
|
327
|
+
case "object":
|
|
328
|
+
return {};
|
|
329
|
+
default:
|
|
330
|
+
return "string";
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function schemaExample(schema) {
|
|
334
|
+
if (schema.example !== void 0) return schema.example;
|
|
335
|
+
const ex = schema.examples;
|
|
336
|
+
if (Array.isArray(ex) && ex.length > 0) return ex[0];
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
function synthFromSchema(doc, raw, depth) {
|
|
340
|
+
const example = schemaExample(raw);
|
|
341
|
+
if (example !== void 0) return example;
|
|
342
|
+
const schema = Array.isArray(raw.allOf) && raw.allOf.length > 0 ? mergeAllOf(doc, raw) : raw;
|
|
343
|
+
if (!schema.properties && isArray(schema) && schema.items) {
|
|
344
|
+
return [placeholder(doc, schema.items, depth)];
|
|
345
|
+
}
|
|
346
|
+
if (!schema.properties) return {};
|
|
347
|
+
const requiredList = schema.required ?? [];
|
|
348
|
+
const required = new Set(requiredList);
|
|
349
|
+
const includeAll = requiredList.length === 0;
|
|
350
|
+
const out = {};
|
|
351
|
+
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
352
|
+
if (!includeAll && !required.has(name)) continue;
|
|
353
|
+
out[name] = placeholder(doc, prop, depth);
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
function synthFromComponent(doc, componentName, depth) {
|
|
358
|
+
const comp = doc.components.schemas[componentName];
|
|
359
|
+
if (!comp) return {};
|
|
360
|
+
return synthFromSchema(doc, comp, depth);
|
|
361
|
+
}
|
|
362
|
+
function synthesizeSample(doc, componentName, depth = 0) {
|
|
363
|
+
return synthFromComponent(doc, componentName, depth);
|
|
364
|
+
}
|
|
365
|
+
function synthesizeSchemaSample(doc, schema) {
|
|
366
|
+
if (composesAllOf(schema)) return synthFromSchema(doc, schema, 0);
|
|
367
|
+
const ref = refName(refFromSchema(schema));
|
|
368
|
+
if (ref) return synthFromComponent(doc, ref, 0);
|
|
369
|
+
return synthFromSchema(doc, schema, 0);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// packages/api-docs-engine/src/operation-detail.ts
|
|
373
|
+
var MAX_SCHEMA_HOPS = 8;
|
|
374
|
+
var PARAM_LOCATIONS = /* @__PURE__ */ new Set(["path", "query", "header", "cookie"]);
|
|
375
|
+
function unescapePointer(segment) {
|
|
376
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
377
|
+
}
|
|
378
|
+
function resolveComponent(doc, value, section) {
|
|
379
|
+
const prefix = `#/components/${section}/`;
|
|
380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
381
|
+
let current = value;
|
|
382
|
+
while (current && typeof current === "object" && typeof current.$ref === "string") {
|
|
383
|
+
const ref = current.$ref;
|
|
384
|
+
if (seen.has(ref) || !ref.startsWith(prefix)) return void 0;
|
|
385
|
+
seen.add(ref);
|
|
386
|
+
const table = doc.components?.[section];
|
|
387
|
+
current = table?.[unescapePointer(ref.slice(prefix.length))];
|
|
388
|
+
}
|
|
389
|
+
return current && typeof current === "object" ? current : void 0;
|
|
390
|
+
}
|
|
391
|
+
function pickJsonMedia(content) {
|
|
392
|
+
if (!content) return void 0;
|
|
393
|
+
const exact = content["application/json"];
|
|
394
|
+
if (exact) return { contentType: "application/json", media: exact };
|
|
395
|
+
for (const [contentType, media] of Object.entries(content)) {
|
|
396
|
+
const base = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
397
|
+
if (base === "application/json" || base.endsWith("+json")) return { contentType, media };
|
|
398
|
+
}
|
|
399
|
+
return void 0;
|
|
400
|
+
}
|
|
401
|
+
function mediaExamples(doc, media) {
|
|
402
|
+
const out = [];
|
|
403
|
+
if (media.example !== void 0) out.push({ name: "example", value: media.example });
|
|
404
|
+
for (const [name, raw] of Object.entries(media.examples ?? {})) {
|
|
405
|
+
const example = resolveComponent(doc, raw, "examples");
|
|
406
|
+
if (!example || example.value === void 0) continue;
|
|
407
|
+
out.push({
|
|
408
|
+
name,
|
|
409
|
+
...typeof example.summary === "string" ? { summary: example.summary } : {},
|
|
410
|
+
value: example.value
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
function admitsNull(schema) {
|
|
416
|
+
if (schema.nullable === true) return true;
|
|
417
|
+
if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
|
|
418
|
+
return [...schema.anyOf ?? [], ...schema.oneOf ?? []].some(
|
|
419
|
+
(member) => member?.type === "null" || Array.isArray(member?.type) && member.type.length === 1 && member.type[0] === "null"
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
function flattenSchema(doc, schema) {
|
|
423
|
+
if (!schema) return void 0;
|
|
424
|
+
const seen = /* @__PURE__ */ new Set();
|
|
425
|
+
let merged = {};
|
|
426
|
+
let nullable = false;
|
|
427
|
+
let current = schema;
|
|
428
|
+
while (current) {
|
|
429
|
+
nullable ||= admitsNull(current);
|
|
430
|
+
const name = refName(refFromSchema(current));
|
|
431
|
+
const target = name && !seen.has(name) && seen.size < MAX_SCHEMA_HOPS ? doc.components.schemas[name] : void 0;
|
|
432
|
+
if (!name || !target) {
|
|
433
|
+
merged = { ...current, ...merged };
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
seen.add(name);
|
|
437
|
+
const { $ref: _ref, allOf: _allOf, anyOf: _anyOf, oneOf: _oneOf, ...local } = current;
|
|
438
|
+
merged = { ...local, ...merged };
|
|
439
|
+
current = target;
|
|
440
|
+
}
|
|
441
|
+
return nullable ? { ...merged, nullable: true } : merged;
|
|
442
|
+
}
|
|
443
|
+
function parameterSchema(doc, raw) {
|
|
444
|
+
const schema = flattenSchema(doc, raw);
|
|
445
|
+
if (!schema) return void 0;
|
|
446
|
+
const out = {};
|
|
447
|
+
let type = primitiveType(schema);
|
|
448
|
+
const items = type === "array" ? flattenSchema(doc, schema.items) : void 0;
|
|
449
|
+
if (items) {
|
|
450
|
+
const itemType = primitiveType(items);
|
|
451
|
+
if (itemType && itemType !== "object" && itemType !== "array") type = `${itemType}[]`;
|
|
452
|
+
}
|
|
453
|
+
if (type) out.type = type;
|
|
454
|
+
if (typeof schema.format === "string") out.format = schema.format;
|
|
455
|
+
const enumValues = Array.isArray(schema.enum) ? schema.enum : items?.enum;
|
|
456
|
+
if (Array.isArray(enumValues)) out.enum = [...enumValues];
|
|
457
|
+
if (schema.default !== void 0) out.default = schema.default;
|
|
458
|
+
if (typeof schema.minimum === "number") out.minimum = schema.minimum;
|
|
459
|
+
if (typeof schema.maximum === "number") out.maximum = schema.maximum;
|
|
460
|
+
if (typeof schema.pattern === "string") out.pattern = schema.pattern;
|
|
461
|
+
if (schema.nullable === true) out.nullable = true;
|
|
462
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
463
|
+
}
|
|
464
|
+
function toDocsParameter(doc, param) {
|
|
465
|
+
const location = param.in;
|
|
466
|
+
if (typeof param.name !== "string" || !PARAM_LOCATIONS.has(location)) return void 0;
|
|
467
|
+
const rawSchema = param.schema ?? Object.values(param.content ?? {})[0]?.schema;
|
|
468
|
+
const schema = parameterSchema(doc, rawSchema);
|
|
469
|
+
const firstNamed = Object.values(param.examples ?? {}).map((raw) => resolveComponent(doc, raw, "examples")).find((ex) => ex?.value !== void 0)?.value;
|
|
470
|
+
const example = param.example ?? firstNamed ?? flattenSchema(doc, rawSchema)?.example;
|
|
471
|
+
return {
|
|
472
|
+
name: param.name,
|
|
473
|
+
in: location,
|
|
474
|
+
required: location === "path" ? true : param.required === true,
|
|
475
|
+
...typeof param.description === "string" ? { description: param.description } : {},
|
|
476
|
+
...param.deprecated === true ? { deprecated: true } : {},
|
|
477
|
+
...schema ? { schema } : {},
|
|
478
|
+
...example !== void 0 ? { example } : {}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function operationParameters(doc, pathItem, op) {
|
|
482
|
+
const merged = [];
|
|
483
|
+
const position = /* @__PURE__ */ new Map();
|
|
484
|
+
const add = (raw, override) => {
|
|
485
|
+
const resolved = resolveComponent(doc, raw, "parameters");
|
|
486
|
+
const param = resolved ? toDocsParameter(doc, resolved) : void 0;
|
|
487
|
+
if (!param) return;
|
|
488
|
+
const key = `${param.in}\0${param.name}`;
|
|
489
|
+
const at = position.get(key);
|
|
490
|
+
if (at !== void 0) {
|
|
491
|
+
if (override) merged[at] = param;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
position.set(key, merged.length);
|
|
495
|
+
merged.push(param);
|
|
496
|
+
};
|
|
497
|
+
for (const raw of Array.isArray(pathItem?.parameters) ? pathItem.parameters : []) add(raw, false);
|
|
498
|
+
for (const raw of Array.isArray(op.parameters) ? op.parameters : []) add(raw, true);
|
|
499
|
+
return merged;
|
|
500
|
+
}
|
|
501
|
+
function statusRank(status) {
|
|
502
|
+
if (/^\d{3}$/.test(status)) return Number(status);
|
|
503
|
+
const range = status.match(/^([1-5])XX$/i);
|
|
504
|
+
if (range) return Number(range[1]) * 100 + 99.5;
|
|
505
|
+
if (status.toLowerCase() === "default") return Number.POSITIVE_INFINITY;
|
|
506
|
+
return Number.MAX_SAFE_INTEGER;
|
|
507
|
+
}
|
|
508
|
+
function responseHeaders(doc, response) {
|
|
509
|
+
const out = [];
|
|
510
|
+
const declared = response?.headers;
|
|
511
|
+
if (!declared || typeof declared !== "object") return out;
|
|
512
|
+
for (const [name, raw] of Object.entries(declared)) {
|
|
513
|
+
if (name.toLowerCase() === "content-type") continue;
|
|
514
|
+
const header = resolveComponent(doc, raw, "headers");
|
|
515
|
+
const entry = { name };
|
|
516
|
+
if (typeof header?.description === "string") entry.description = header.description;
|
|
517
|
+
if (header?.required === true) entry.required = true;
|
|
518
|
+
const facts = parameterSchema(doc, header?.schema ?? Object.values(header?.content ?? {})[0]?.schema);
|
|
519
|
+
if (facts?.type || facts?.format) {
|
|
520
|
+
entry.schema = {
|
|
521
|
+
...facts.type ? { type: facts.type } : {},
|
|
522
|
+
...facts.format ? { format: facts.format } : {}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
out.push(entry);
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
529
|
+
function operationResponses(doc, op) {
|
|
530
|
+
const entries = Object.entries(op.responses ?? {});
|
|
531
|
+
entries.sort(([a], [b]) => statusRank(a) - statusRank(b) || a.localeCompare(b));
|
|
532
|
+
return entries.map(([status, raw]) => {
|
|
533
|
+
const response = resolveComponent(doc, raw, "responses");
|
|
534
|
+
const out = { status };
|
|
535
|
+
const description = response?.description ?? raw?.description;
|
|
536
|
+
if (typeof description === "string") out.description = description;
|
|
537
|
+
const headers = responseHeaders(doc, response);
|
|
538
|
+
if (headers.length > 0) out.headers = headers;
|
|
539
|
+
const json = pickJsonMedia(response?.content);
|
|
540
|
+
if (!json) {
|
|
541
|
+
const first = Object.keys(response?.content ?? {})[0];
|
|
542
|
+
if (first) out.contentType = first;
|
|
543
|
+
return out;
|
|
544
|
+
}
|
|
545
|
+
out.contentType = json.contentType;
|
|
546
|
+
const examples = mediaExamples(doc, json.media);
|
|
547
|
+
if (json.media.schema) out.fields = resolveSchemaFields(doc, json.media.schema);
|
|
548
|
+
if (examples.length > 0) {
|
|
549
|
+
out.sample = examples[0]?.value;
|
|
550
|
+
out.examples = examples;
|
|
551
|
+
} else if (json.media.schema) {
|
|
552
|
+
out.sample = synthesizeSchemaSample(doc, json.media.schema);
|
|
553
|
+
}
|
|
554
|
+
return out;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function schemeDetail(scheme) {
|
|
558
|
+
switch (scheme.type) {
|
|
559
|
+
case "http":
|
|
560
|
+
return typeof scheme.scheme === "string" ? scheme.scheme.toLowerCase() : void 0;
|
|
561
|
+
case "apiKey":
|
|
562
|
+
return scheme.in && scheme.name ? `${scheme.in} ${scheme.name}` : void 0;
|
|
563
|
+
case "openIdConnect":
|
|
564
|
+
return scheme.openIdConnectUrl;
|
|
565
|
+
default:
|
|
566
|
+
return void 0;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function securityScheme(doc, name, scopes) {
|
|
570
|
+
const scheme = resolveComponent(doc, doc.components?.securitySchemes?.[name], "securitySchemes");
|
|
571
|
+
const entry = { scheme: name };
|
|
572
|
+
if (typeof scheme?.type === "string") entry.type = scheme.type;
|
|
573
|
+
const detail = scheme ? schemeDetail(scheme) : void 0;
|
|
574
|
+
if (detail) entry.schemeDetail = detail;
|
|
575
|
+
if (typeof scheme?.bearerFormat === "string") entry.bearerFormat = scheme.bearerFormat;
|
|
576
|
+
if (typeof scheme?.description === "string") entry.description = scheme.description;
|
|
577
|
+
const named = Array.isArray(scopes) ? scopes.filter((s) => typeof s === "string") : [];
|
|
578
|
+
if (named.length > 0) entry.scopes = [...new Set(named)];
|
|
579
|
+
return entry;
|
|
580
|
+
}
|
|
581
|
+
function operationSecurity(doc, op) {
|
|
582
|
+
const requirements = op.security !== void 0 ? op.security : doc.security;
|
|
583
|
+
if (!Array.isArray(requirements)) return void 0;
|
|
584
|
+
const out = [];
|
|
585
|
+
for (const requirement of requirements) {
|
|
586
|
+
if (!requirement || typeof requirement !== "object" || Array.isArray(requirement)) continue;
|
|
587
|
+
out.push({
|
|
588
|
+
schemes: Object.entries(requirement).map(([name, scopes]) => securityScheme(doc, name, scopes))
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return out;
|
|
592
|
+
}
|
|
593
|
+
function operationRequestBody(doc, op) {
|
|
594
|
+
const body = resolveComponent(doc, op.requestBody, "requestBodies");
|
|
595
|
+
if (!body) return void 0;
|
|
596
|
+
const requestBody = { required: body.required === true, fields: [] };
|
|
597
|
+
if (typeof body.description === "string") requestBody.description = body.description;
|
|
598
|
+
const content = body.content && typeof body.content === "object" ? body.content : void 0;
|
|
599
|
+
const json = pickJsonMedia(content);
|
|
600
|
+
if (!json) {
|
|
601
|
+
const first = Object.keys(content ?? {})[0];
|
|
602
|
+
if (first) requestBody.contentType = first;
|
|
603
|
+
return { requestBody, sample: {} };
|
|
604
|
+
}
|
|
605
|
+
requestBody.contentType = json.contentType;
|
|
606
|
+
const schema = json.media.schema;
|
|
607
|
+
if (schema) requestBody.fields = resolveSchemaFields(doc, schema);
|
|
608
|
+
const examples = mediaExamples(doc, json.media);
|
|
609
|
+
if (examples.length > 0) requestBody.examples = examples;
|
|
610
|
+
const sample = examples.length > 0 ? examples[0]?.value : schema ? synthesizeSchemaSample(doc, schema) : {};
|
|
611
|
+
return { requestBody, sample };
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// packages/api-docs-engine/src/slices.ts
|
|
615
|
+
var DOCS_URL = /Vendor docs:\s*(https?:\S+)/;
|
|
616
|
+
function dedupeRefs(fields) {
|
|
617
|
+
const seen = /* @__PURE__ */ new Set();
|
|
618
|
+
for (const f of fields) if (f.ref) seen.add(f.ref);
|
|
619
|
+
return [...seen].sort((a, b) => a.localeCompare(b));
|
|
620
|
+
}
|
|
621
|
+
function getType(doc, name) {
|
|
622
|
+
const comp = doc.components.schemas[name];
|
|
623
|
+
if (!comp) return null;
|
|
624
|
+
const fields = resolveComponentFields(doc, name);
|
|
625
|
+
return {
|
|
626
|
+
name,
|
|
627
|
+
description: comp.description,
|
|
628
|
+
deprecated: comp.deprecated === true,
|
|
629
|
+
fields,
|
|
630
|
+
refs: dedupeRefs(fields)
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function responseSchema(post) {
|
|
634
|
+
const responses = post.responses ?? {};
|
|
635
|
+
const code = "200" in responses ? "200" : Object.keys(responses).find((c) => /^2\d\d$/.test(c)) ?? ["default", "DEFAULT"].find((c) => c in responses);
|
|
636
|
+
if (!code) return void 0;
|
|
637
|
+
return responses[code]?.content?.["application/json"]?.schema;
|
|
638
|
+
}
|
|
639
|
+
function reqResponseComponent(post, kind) {
|
|
640
|
+
const schema = kind === "req" ? post.requestBody?.content?.["application/json"]?.schema : responseSchema(post);
|
|
641
|
+
const ref = refFromSchema(schema);
|
|
642
|
+
return ref ? ref.replace(/^#\/components\/schemas\//, "") : void 0;
|
|
643
|
+
}
|
|
644
|
+
function buildOpSlice(doc, norm) {
|
|
645
|
+
const { id, method, route, op } = norm;
|
|
646
|
+
const body = operationRequestBody(doc, op);
|
|
647
|
+
const rsComp = reqResponseComponent(op, "rs");
|
|
648
|
+
const request = body?.requestBody.fields ?? [];
|
|
649
|
+
const response = rsComp ? resolveComponentFields(doc, rsComp) : [];
|
|
650
|
+
const docsUrl = op.externalDocs?.url ?? op.description?.match(DOCS_URL)?.[1];
|
|
651
|
+
const { description } = parseDescription(op.description);
|
|
652
|
+
const refs = [.../* @__PURE__ */ new Set([...dedupeRefs(request), ...dedupeRefs(response)])].sort(
|
|
653
|
+
(a, b) => a.localeCompare(b)
|
|
654
|
+
);
|
|
655
|
+
const requestExamples = body?.requestBody.examples ?? [];
|
|
656
|
+
const security = operationSecurity(doc, op);
|
|
657
|
+
const tags = Array.isArray(op.tags) ? op.tags.filter((t) => typeof t === "string") : [];
|
|
658
|
+
return {
|
|
659
|
+
id,
|
|
660
|
+
name: op.summary ?? id,
|
|
661
|
+
// Prefer the generator's curated group; fall back to the CamelCase token.
|
|
662
|
+
entity: op["x-entity-group"] ?? deriveEntity(id),
|
|
663
|
+
capability: op["x-capability-kind"] ?? deriveCapability(method, id),
|
|
664
|
+
service: op.tags?.[0] ?? "Other",
|
|
665
|
+
method,
|
|
666
|
+
route,
|
|
667
|
+
description,
|
|
668
|
+
docsUrl,
|
|
669
|
+
developerResources: op["x-developer-resources"],
|
|
670
|
+
request,
|
|
671
|
+
response,
|
|
672
|
+
// A document-supplied request example beats the synthesized skeleton.
|
|
673
|
+
sampleRequest: body ? body.sample : {},
|
|
674
|
+
sampleResponse: rsComp ? synthesizeSample(doc, rsComp) : {},
|
|
675
|
+
refs,
|
|
676
|
+
...typeof op.operationId === "string" && op.operationId ? { operationId: op.operationId } : {},
|
|
677
|
+
...typeof op.summary === "string" ? { summary: op.summary } : {},
|
|
678
|
+
...tags.length > 0 ? { tags } : {},
|
|
679
|
+
...op.deprecated === true ? { deprecated: true } : {},
|
|
680
|
+
parameters: operationParameters(doc, doc.paths?.[route], op),
|
|
681
|
+
responses: operationResponses(doc, op),
|
|
682
|
+
...requestExamples.length > 0 ? { requestExamples } : {},
|
|
683
|
+
...body ? { requestBody: body.requestBody } : {},
|
|
684
|
+
...security ? { security } : {}
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// packages/api-docs-engine/src/tags.ts
|
|
689
|
+
function collectTags(doc, ops, scoped) {
|
|
690
|
+
const used = /* @__PURE__ */ new Set();
|
|
691
|
+
for (const { op } of ops) {
|
|
692
|
+
for (const tag of Array.isArray(op.tags) ? op.tags : []) {
|
|
693
|
+
if (typeof tag === "string") used.add(tag);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const out = [];
|
|
697
|
+
const declared = /* @__PURE__ */ new Set();
|
|
698
|
+
for (const tag of Array.isArray(doc.tags) ? doc.tags : []) {
|
|
699
|
+
if (!tag || typeof tag.name !== "string" || declared.has(tag.name)) continue;
|
|
700
|
+
declared.add(tag.name);
|
|
701
|
+
if (scoped && !used.has(tag.name)) continue;
|
|
702
|
+
out.push({
|
|
703
|
+
name: tag.name,
|
|
704
|
+
...typeof tag.description === "string" ? { description: tag.description } : {}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
const undeclared = [...used].filter((name) => !declared.has(name)).sort((a, b) => a.localeCompare(b));
|
|
708
|
+
for (const name of undeclared) out.push({ name });
|
|
709
|
+
return out;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// packages/api-docs-engine/src/engine.ts
|
|
713
|
+
function buildEngine(doc, subset) {
|
|
714
|
+
const index = buildIndex(doc, subset);
|
|
715
|
+
const idSet = new Set(index.map((e) => e.id));
|
|
716
|
+
const ops = normalizeOps(doc, subset);
|
|
717
|
+
const opById = /* @__PURE__ */ new Map();
|
|
718
|
+
for (const norm of ops) opById.set(norm.id, norm);
|
|
719
|
+
const tags = collectTags(doc, ops, subset !== void 0);
|
|
720
|
+
const records = index.map((entry) => {
|
|
721
|
+
const norm = opById.get(entry.id);
|
|
722
|
+
const { description } = parseDescription(norm?.op.description);
|
|
723
|
+
return {
|
|
724
|
+
entry,
|
|
725
|
+
fields: {
|
|
726
|
+
name: entry.name.toLowerCase(),
|
|
727
|
+
id: tokenizeIdentifier(entry.id),
|
|
728
|
+
entity: entry.entity.toLowerCase(),
|
|
729
|
+
service: entry.service.toLowerCase(),
|
|
730
|
+
description: (description ?? "").toLowerCase()
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
});
|
|
734
|
+
return {
|
|
735
|
+
index: () => index,
|
|
736
|
+
tags: () => tags,
|
|
737
|
+
search: (query, filters) => searchRecords(records, query, filters),
|
|
738
|
+
getType: (name) => getType(doc, name),
|
|
739
|
+
getOp: (id) => {
|
|
740
|
+
if (!idSet.has(id)) return null;
|
|
741
|
+
const norm = opById.get(id);
|
|
742
|
+
return norm ? buildOpSlice(doc, norm) : null;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// packages/canvas-api-docs/src/panes/field-list.tsx
|
|
748
|
+
import { useState } from "react";
|
|
749
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
750
|
+
function fieldRowKey(field, i) {
|
|
751
|
+
return field.name || `#${i}`;
|
|
752
|
+
}
|
|
753
|
+
function FieldRow({
|
|
754
|
+
field,
|
|
755
|
+
depth,
|
|
756
|
+
onDrill,
|
|
757
|
+
onOpenField,
|
|
758
|
+
loadType,
|
|
759
|
+
renderText
|
|
760
|
+
}) {
|
|
761
|
+
const inlineChildren = field.children && field.children.length > 0 ? field.children : null;
|
|
762
|
+
const expandableRef = !!field.ref && !!loadType;
|
|
763
|
+
const expandable = !!inlineChildren || expandableRef;
|
|
764
|
+
const [open, setOpen] = useState(depth === 0 && !!inlineChildren);
|
|
765
|
+
const [refFields, setRefFields] = useState(null);
|
|
766
|
+
const [loading, setLoading] = useState(false);
|
|
767
|
+
const indent = 12 + depth * 16;
|
|
768
|
+
const toggle = () => {
|
|
769
|
+
const next = !open;
|
|
770
|
+
setOpen(next);
|
|
771
|
+
if (next && expandableRef && refFields === null && !loading) {
|
|
772
|
+
setLoading(true);
|
|
773
|
+
loadType(field.ref).then((fs) => setRefFields(fs)).catch(() => setRefFields([])).finally(() => setLoading(false));
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
const childFields = inlineChildren ?? refFields;
|
|
777
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
778
|
+
/* @__PURE__ */ jsxs("tr", { className: "border-b align-top transition-colors last:border-0 hover:bg-muted/30", children: [
|
|
779
|
+
/* @__PURE__ */ jsxs("td", { className: "py-1.5 pr-3 align-top", style: { paddingLeft: indent }, children: [
|
|
780
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-start gap-1 whitespace-nowrap", children: [
|
|
781
|
+
expandable ? /* @__PURE__ */ jsx(
|
|
782
|
+
"button",
|
|
783
|
+
{
|
|
784
|
+
type: "button",
|
|
785
|
+
onClick: toggle,
|
|
786
|
+
"aria-label": open ? "Collapse" : "Expand",
|
|
787
|
+
className: "mt-px flex size-4 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
788
|
+
children: /* @__PURE__ */ jsx(
|
|
789
|
+
"svg",
|
|
790
|
+
{
|
|
791
|
+
viewBox: "0 0 20 20",
|
|
792
|
+
fill: "currentColor",
|
|
793
|
+
"aria-hidden": "true",
|
|
794
|
+
className: `size-3.5 transition-transform duration-150 ${open ? "rotate-90" : ""}`,
|
|
795
|
+
children: /* @__PURE__ */ jsx("path", { d: "M7 5l6 5-6 5z" })
|
|
796
|
+
}
|
|
797
|
+
)
|
|
798
|
+
}
|
|
799
|
+
) : /* @__PURE__ */ jsx("span", { className: "inline-block size-4 shrink-0" }),
|
|
800
|
+
onOpenField ? /* @__PURE__ */ jsx(
|
|
801
|
+
"button",
|
|
802
|
+
{
|
|
803
|
+
type: "button",
|
|
804
|
+
onClick: () => onOpenField(field),
|
|
805
|
+
className: "font-mono text-xs font-medium hover:text-primary hover:underline",
|
|
806
|
+
children: field.name
|
|
807
|
+
}
|
|
808
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-mono text-xs font-medium", children: field.name }),
|
|
809
|
+
field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" }),
|
|
810
|
+
field.deprecated && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded bg-amber-500/15 px-1 py-0.5 text-[9px] font-medium uppercase text-amber-700 dark:text-amber-300", children: "deprecated" })
|
|
811
|
+
] }),
|
|
812
|
+
field.nativeName && field.nativeName !== field.name && /* @__PURE__ */ jsx("div", { className: "ml-4 mt-0.5 whitespace-nowrap font-mono text-[10px] text-muted-foreground/70", children: field.nativeName })
|
|
813
|
+
] }),
|
|
814
|
+
/* @__PURE__ */ jsx("td", { className: "whitespace-nowrap px-3 py-1.5 align-top", children: field.ref && onDrill ? (
|
|
815
|
+
// Referenced object: props expand inline (the caret); the type name
|
|
816
|
+
// is a small muted link out to the full type pane.
|
|
817
|
+
/* @__PURE__ */ jsxs(
|
|
818
|
+
"button",
|
|
819
|
+
{
|
|
820
|
+
type: "button",
|
|
821
|
+
onClick: () => onDrill?.(field.ref),
|
|
822
|
+
title: "Open type",
|
|
823
|
+
className: "text-left font-mono text-[11px] text-muted-foreground hover:text-primary hover:underline",
|
|
824
|
+
children: [
|
|
825
|
+
field.type,
|
|
826
|
+
" \u2197"
|
|
827
|
+
]
|
|
828
|
+
}
|
|
829
|
+
)
|
|
830
|
+
) : /* @__PURE__ */ jsx("span", { className: "inline-block rounded bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground", children: field.type }) }),
|
|
831
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 align-top", children: field.description && /* @__PURE__ */ jsx(
|
|
832
|
+
"div",
|
|
833
|
+
{
|
|
834
|
+
className: "line-clamp-2 max-w-[360px] min-w-[180px] text-xs text-muted-foreground",
|
|
835
|
+
title: field.description,
|
|
836
|
+
children: renderText ? renderText(field.description) : field.description
|
|
837
|
+
}
|
|
838
|
+
) })
|
|
839
|
+
] }),
|
|
840
|
+
open && loading && /* @__PURE__ */ jsx("tr", { className: "border-b last:border-0", children: /* @__PURE__ */ jsxs("td", { colSpan: 3, className: "whitespace-nowrap py-1 text-[10px] text-muted-foreground", style: { paddingLeft: indent + 28 }, children: [
|
|
841
|
+
"Loading ",
|
|
842
|
+
field.type,
|
|
843
|
+
"\u2026"
|
|
844
|
+
] }) }),
|
|
845
|
+
open && childFields?.map((c, i) => /* @__PURE__ */ jsx(
|
|
846
|
+
FieldRow,
|
|
847
|
+
{
|
|
848
|
+
field: c,
|
|
849
|
+
depth: depth + 1,
|
|
850
|
+
onDrill,
|
|
851
|
+
onOpenField,
|
|
852
|
+
loadType,
|
|
853
|
+
renderText
|
|
854
|
+
},
|
|
855
|
+
fieldRowKey(c, i)
|
|
856
|
+
))
|
|
857
|
+
] });
|
|
858
|
+
}
|
|
859
|
+
function FieldRows({
|
|
860
|
+
fields,
|
|
861
|
+
onDrill,
|
|
862
|
+
onOpenField,
|
|
863
|
+
loadType,
|
|
864
|
+
renderText
|
|
865
|
+
}) {
|
|
866
|
+
if (fields.length === 0) {
|
|
867
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-md border px-3 py-2 text-xs text-muted-foreground", children: "No fields." });
|
|
868
|
+
}
|
|
869
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full border-collapse text-sm", children: [
|
|
870
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/40 text-left text-[11px] uppercase tracking-wide text-muted-foreground", children: [
|
|
871
|
+
/* @__PURE__ */ jsx("th", { className: "whitespace-nowrap px-3 py-1.5 font-medium", children: "Field" }),
|
|
872
|
+
/* @__PURE__ */ jsx("th", { className: "whitespace-nowrap px-3 py-1.5 font-medium", children: "Type" }),
|
|
873
|
+
/* @__PURE__ */ jsx("th", { className: "w-full px-3 py-1.5 font-medium", children: "Description" })
|
|
874
|
+
] }) }),
|
|
875
|
+
/* @__PURE__ */ jsx("tbody", { children: fields.map((f, i) => /* @__PURE__ */ jsx(
|
|
876
|
+
FieldRow,
|
|
877
|
+
{
|
|
878
|
+
field: f,
|
|
879
|
+
depth: 0,
|
|
880
|
+
onDrill,
|
|
881
|
+
onOpenField,
|
|
882
|
+
loadType,
|
|
883
|
+
renderText
|
|
884
|
+
},
|
|
885
|
+
fieldRowKey(f, i)
|
|
886
|
+
)) })
|
|
887
|
+
] }) });
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// packages/canvas-api-docs/src/docs/json-schema.ts
|
|
891
|
+
var MAX_DEPTH = 4;
|
|
892
|
+
var MAX_COMPOSITION_DEPTH = 8;
|
|
893
|
+
var MAX_NODES = 2e3;
|
|
894
|
+
var MAX_REF_CHAIN = 32;
|
|
895
|
+
var MAX_LABEL_LENGTH = 200;
|
|
896
|
+
var SCHEMA_TRUNCATED_FIELD = "(schema truncated)";
|
|
897
|
+
function exhausted(budget) {
|
|
898
|
+
if (budget.nodes > 0) return false;
|
|
899
|
+
budget.truncated = true;
|
|
900
|
+
return true;
|
|
901
|
+
}
|
|
902
|
+
function spend(budget) {
|
|
903
|
+
if (budget.nodes <= 0) {
|
|
904
|
+
budget.truncated = true;
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
budget.nodes -= 1;
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
function decodeSegment(segment) {
|
|
911
|
+
try {
|
|
912
|
+
return decodeURIComponent(segment);
|
|
913
|
+
} catch {
|
|
914
|
+
return segment;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function deref(schema, root, trail, budget) {
|
|
918
|
+
let current = schema;
|
|
919
|
+
let hops = 0;
|
|
920
|
+
while (current?.$ref) {
|
|
921
|
+
const m = /^#\/(definitions|\$defs)\/(.+)$/.exec(current.$ref);
|
|
922
|
+
if (!m || trail.has(current.$ref)) return {};
|
|
923
|
+
if (hops >= MAX_REF_CHAIN) {
|
|
924
|
+
budget.truncated = true;
|
|
925
|
+
return {};
|
|
926
|
+
}
|
|
927
|
+
if (!spend(budget)) return {};
|
|
928
|
+
hops += 1;
|
|
929
|
+
trail.add(current.$ref);
|
|
930
|
+
const table = root[m[1]];
|
|
931
|
+
const name = decodeSegment(m[2]);
|
|
932
|
+
current = table && Object.hasOwn(table, name) ? table[name] : void 0;
|
|
933
|
+
}
|
|
934
|
+
return current;
|
|
935
|
+
}
|
|
936
|
+
function objectShape(schema, root, seen, budget, hops) {
|
|
937
|
+
if (!spend(budget)) return { props: {}, required: /* @__PURE__ */ new Set() };
|
|
938
|
+
const trail = new Set(seen);
|
|
939
|
+
const s = deref(schema, root, trail, budget);
|
|
940
|
+
const props = { ...s?.properties ?? {} };
|
|
941
|
+
const required = new Set(Array.isArray(s?.required) ? s.required : []);
|
|
942
|
+
for (const part of s?.allOf ?? []) {
|
|
943
|
+
if (hops >= MAX_COMPOSITION_DEPTH) {
|
|
944
|
+
budget.truncated = true;
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
const merged = objectShape(part, root, trail, budget, hops + 1);
|
|
948
|
+
Object.assign(props, merged.props);
|
|
949
|
+
for (const r of merged.required) required.add(r);
|
|
950
|
+
}
|
|
951
|
+
return { props, required };
|
|
952
|
+
}
|
|
953
|
+
function capLabel(label, budget) {
|
|
954
|
+
if (label.length <= MAX_LABEL_LENGTH) return label;
|
|
955
|
+
budget.truncated = true;
|
|
956
|
+
return `${label.slice(0, MAX_LABEL_LENGTH - 1)}\u2026`;
|
|
957
|
+
}
|
|
958
|
+
function typeLabel2(schema, root, seen, budget, hops = 0) {
|
|
959
|
+
if (!spend(budget)) return "\u2026";
|
|
960
|
+
const trail = new Set(seen);
|
|
961
|
+
const s = deref(schema, root, trail, budget);
|
|
962
|
+
if (!s) return "object";
|
|
963
|
+
const alternatives = s.oneOf ?? s.anyOf;
|
|
964
|
+
if (alternatives && alternatives.length > 0) {
|
|
965
|
+
if (hops >= MAX_COMPOSITION_DEPTH) {
|
|
966
|
+
budget.truncated = true;
|
|
967
|
+
return "one of: \u2026";
|
|
968
|
+
}
|
|
969
|
+
let label = "one of: ";
|
|
970
|
+
for (const [i, a] of alternatives.entries()) {
|
|
971
|
+
if (label.length > MAX_LABEL_LENGTH) break;
|
|
972
|
+
label += `${i > 0 ? " | " : ""}${typeLabel2(a, root, trail, budget, hops + 1)}`;
|
|
973
|
+
}
|
|
974
|
+
return capLabel(label, budget);
|
|
975
|
+
}
|
|
976
|
+
const base = Array.isArray(s.type) ? s.type.filter((t) => t !== "null").join(" | ") : s.type;
|
|
977
|
+
if (base === "array") {
|
|
978
|
+
if (hops >= MAX_COMPOSITION_DEPTH) {
|
|
979
|
+
budget.truncated = true;
|
|
980
|
+
return "\u2026[]";
|
|
981
|
+
}
|
|
982
|
+
return capLabel(`${typeLabel2(s.items, root, trail, budget, hops + 1)}[]`, budget);
|
|
983
|
+
}
|
|
984
|
+
const ref = schema?.$ref?.split("/").pop();
|
|
985
|
+
if (ref && (base === void 0 || base === "object")) return decodeSegment(ref);
|
|
986
|
+
return base ?? "object";
|
|
987
|
+
}
|
|
988
|
+
function fieldsOf(schema, root, depth, seen, budget, hops) {
|
|
989
|
+
if (depth > MAX_DEPTH) return [];
|
|
990
|
+
if (hops > MAX_COMPOSITION_DEPTH) {
|
|
991
|
+
budget.truncated = true;
|
|
992
|
+
return [];
|
|
993
|
+
}
|
|
994
|
+
if (!spend(budget)) return [];
|
|
995
|
+
const trail = new Set(seen);
|
|
996
|
+
const s = deref(schema, root, trail, budget);
|
|
997
|
+
if (!s) return [];
|
|
998
|
+
if (s.type === "array" || Array.isArray(s.type) && s.type.includes("array")) return fieldsOf(s.items, root, depth, trail, budget, hops + 1);
|
|
999
|
+
const alternatives = s.oneOf ?? s.anyOf;
|
|
1000
|
+
if (alternatives && !s.properties) {
|
|
1001
|
+
const fields2 = [];
|
|
1002
|
+
for (const a of alternatives) {
|
|
1003
|
+
if (exhausted(budget)) break;
|
|
1004
|
+
for (const f of fieldsOf(a, root, depth, trail, budget, hops + 1)) fields2.push({ ...f, required: false });
|
|
1005
|
+
}
|
|
1006
|
+
return fields2;
|
|
1007
|
+
}
|
|
1008
|
+
const { props, required } = objectShape(s, root, trail, budget, hops);
|
|
1009
|
+
const fields = [];
|
|
1010
|
+
for (const [name, raw] of Object.entries(props)) {
|
|
1011
|
+
if (name.startsWith("x-")) continue;
|
|
1012
|
+
if (exhausted(budget)) break;
|
|
1013
|
+
const p = deref(raw, root, new Set(trail), budget) ?? {};
|
|
1014
|
+
const children = fieldsOf(raw, root, depth + 1, trail, budget, hops);
|
|
1015
|
+
fields.push({
|
|
1016
|
+
name,
|
|
1017
|
+
type: typeLabel2(raw, root, trail, budget),
|
|
1018
|
+
required: required.has(name),
|
|
1019
|
+
deprecated: p.deprecated === true,
|
|
1020
|
+
...p.description ? { description: p.description } : {},
|
|
1021
|
+
...Array.isArray(p.enum) ? { enumValues: p.enum.map(String) } : {},
|
|
1022
|
+
...p.default !== void 0 ? { defaultValue: String(p.default) } : {},
|
|
1023
|
+
...children.length > 0 ? { children } : {}
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
return fields;
|
|
1027
|
+
}
|
|
1028
|
+
function jsonSchemaFields(schema) {
|
|
1029
|
+
const root = schema ?? {};
|
|
1030
|
+
if (typeof schema !== "object" || schema === null) return [];
|
|
1031
|
+
const budget = { nodes: MAX_NODES, truncated: false };
|
|
1032
|
+
const fields = fieldsOf(root, root, 0, /* @__PURE__ */ new Set(), budget, 0);
|
|
1033
|
+
if (!budget.truncated) return fields;
|
|
1034
|
+
return [
|
|
1035
|
+
...fields,
|
|
1036
|
+
{
|
|
1037
|
+
name: SCHEMA_TRUNCATED_FIELD,
|
|
1038
|
+
type: "\u2026",
|
|
1039
|
+
required: false,
|
|
1040
|
+
deprecated: false,
|
|
1041
|
+
description: "This payload schema is too large or too deeply nested to show in full; the published schema is the whole contract."
|
|
1042
|
+
}
|
|
1043
|
+
];
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// packages/canvas-api-docs/src/docs/markdown.tsx
|
|
1047
|
+
import { createContext, useContext, useId as useId2 } from "react";
|
|
1048
|
+
|
|
1049
|
+
// packages/rich-markdown/src/rich-markdown.tsx
|
|
1050
|
+
import { memo, useMemo } from "react";
|
|
1051
|
+
import ReactMarkdown from "react-markdown";
|
|
1052
|
+
import rehypeHighlight from "rehype-highlight";
|
|
1053
|
+
|
|
1054
|
+
// packages/rich-markdown/src/cn.ts
|
|
1055
|
+
function cn(...parts) {
|
|
1056
|
+
return parts.filter(Boolean).join(" ");
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// packages/rich-markdown/src/code-block.tsx
|
|
1060
|
+
import { useRef, useState as useState2 } from "react";
|
|
1061
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1062
|
+
function CodeBlock({ language, children }) {
|
|
1063
|
+
const codeRef = useRef(null);
|
|
1064
|
+
const [copied, setCopied] = useState2(false);
|
|
1065
|
+
const onCopy = () => {
|
|
1066
|
+
const text = codeRef.current?.textContent ?? "";
|
|
1067
|
+
void navigator.clipboard?.writeText(text).then(() => {
|
|
1068
|
+
setCopied(true);
|
|
1069
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1070
|
+
});
|
|
1071
|
+
};
|
|
1072
|
+
return /* @__PURE__ */ jsxs2("div", { className: "not-prose group my-3 overflow-hidden rounded border border-border", children: [
|
|
1073
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-b border-border bg-muted/50 px-3 py-1.5", children: [
|
|
1074
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs font-medium text-muted-foreground", children: language }),
|
|
1075
|
+
/* @__PURE__ */ jsx2(
|
|
1076
|
+
"button",
|
|
1077
|
+
{
|
|
1078
|
+
type: "button",
|
|
1079
|
+
onClick: onCopy,
|
|
1080
|
+
className: "rounded px-2 py-0.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
1081
|
+
"aria-label": "Copy code",
|
|
1082
|
+
children: copied ? "Copied" : "Copy"
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
] }),
|
|
1086
|
+
/* @__PURE__ */ jsx2("pre", { className: "overflow-x-auto p-3 text-[12px] leading-relaxed", children: /* @__PURE__ */ jsx2("code", { ref: codeRef, className: "hljs", children }) })
|
|
1087
|
+
] });
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// packages/rich-markdown/src/hast.ts
|
|
1091
|
+
function isElement(node) {
|
|
1092
|
+
return node.type === "element";
|
|
1093
|
+
}
|
|
1094
|
+
function hastToString(node) {
|
|
1095
|
+
if (node.type === "text") return String(node.value);
|
|
1096
|
+
const children = node.children;
|
|
1097
|
+
return children ? children.map(hastToString).join("") : "";
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// packages/rich-markdown/src/images.ts
|
|
1101
|
+
var BUNDLED_IMAGE_URL = /^data:image\/(?:png|jpeg|gif|webp);base64,[A-Za-z0-9+/]*={0,2}$/;
|
|
1102
|
+
function isBundledImageUrl(url) {
|
|
1103
|
+
return BUNDLED_IMAGE_URL.test(url);
|
|
1104
|
+
}
|
|
1105
|
+
function imagePlaceholderText(alt) {
|
|
1106
|
+
const label = typeof alt === "string" ? alt.trim() : "";
|
|
1107
|
+
return label ? `[image: ${label}]` : "[image]";
|
|
1108
|
+
}
|
|
1109
|
+
function replaceImages(node, insideLink, keep) {
|
|
1110
|
+
const children = node.children;
|
|
1111
|
+
if (!children) return;
|
|
1112
|
+
for (let i = 0; i < children.length; i++) {
|
|
1113
|
+
const child = children[i];
|
|
1114
|
+
if (!isElement(child)) {
|
|
1115
|
+
replaceImages(child, insideLink, keep);
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (child.tagName === "img") {
|
|
1119
|
+
const src = child.properties.src;
|
|
1120
|
+
if (keep && typeof src === "string" && keep(src)) continue;
|
|
1121
|
+
const text = { type: "text", value: imagePlaceholderText(child.properties.alt) };
|
|
1122
|
+
const replacement = insideLink || typeof src !== "string" || src === "" ? { type: "element", tagName: "span", properties: { dataImagePlaceholder: "" }, children: [text] } : { type: "element", tagName: "a", properties: { href: src, dataImagePlaceholder: "" }, children: [text] };
|
|
1123
|
+
children[i] = replacement;
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
replaceImages(child, insideLink || child.tagName === "a", keep);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function rehypeBlockImages(options = {}) {
|
|
1130
|
+
return (tree) => {
|
|
1131
|
+
replaceImages(tree, false, options.keep);
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// packages/rich-markdown/src/mermaid-block.tsx
|
|
1136
|
+
import { useEffect as useEffect2, useId, useState as useState4 } from "react";
|
|
1137
|
+
|
|
1138
|
+
// packages/rich-markdown/src/use-is-dark-class.ts
|
|
1139
|
+
import { useEffect, useState as useState3 } from "react";
|
|
1140
|
+
function useIsDarkClass() {
|
|
1141
|
+
const [isDark, setIsDark] = useState3(
|
|
1142
|
+
() => typeof document !== "undefined" && document.documentElement.classList.contains("dark")
|
|
1143
|
+
);
|
|
1144
|
+
useEffect(() => {
|
|
1145
|
+
const root = document.documentElement;
|
|
1146
|
+
const update = () => setIsDark(root.classList.contains("dark"));
|
|
1147
|
+
update();
|
|
1148
|
+
const observer = new MutationObserver(update);
|
|
1149
|
+
observer.observe(root, { attributes: true, attributeFilter: ["class"] });
|
|
1150
|
+
return () => observer.disconnect();
|
|
1151
|
+
}, []);
|
|
1152
|
+
return isDark;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// packages/rich-markdown/src/mermaid-block.tsx
|
|
1156
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1157
|
+
function mermaidTheme(isDark) {
|
|
1158
|
+
return isDark ? "dark" : "default";
|
|
1159
|
+
}
|
|
1160
|
+
async function renderMermaid(mermaid, id, diagram, isDark) {
|
|
1161
|
+
try {
|
|
1162
|
+
mermaid.initialize({ securityLevel: "strict", startOnLoad: false, theme: mermaidTheme(isDark) });
|
|
1163
|
+
const { svg } = await mermaid.render(id, diagram);
|
|
1164
|
+
return { ok: true, svg };
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
function MermaidBlock({ diagram }) {
|
|
1170
|
+
const reactId = useId();
|
|
1171
|
+
const id = `mermaid-${reactId.replace(/:/g, "")}`;
|
|
1172
|
+
const isDark = useIsDarkClass();
|
|
1173
|
+
const [result, setResult] = useState4(null);
|
|
1174
|
+
useEffect2(() => {
|
|
1175
|
+
let cancelled = false;
|
|
1176
|
+
void (async () => {
|
|
1177
|
+
const mermaid = (await import("mermaid")).default;
|
|
1178
|
+
const r = await renderMermaid(mermaid, id, diagram, isDark);
|
|
1179
|
+
if (!cancelled) setResult(r);
|
|
1180
|
+
})();
|
|
1181
|
+
return () => {
|
|
1182
|
+
cancelled = true;
|
|
1183
|
+
};
|
|
1184
|
+
}, [diagram, isDark, id]);
|
|
1185
|
+
if (result?.ok) {
|
|
1186
|
+
return /* @__PURE__ */ jsx3(
|
|
1187
|
+
"div",
|
|
1188
|
+
{
|
|
1189
|
+
"data-mermaid": true,
|
|
1190
|
+
className: "rich-md-mermaid my-4 flex justify-center",
|
|
1191
|
+
dangerouslySetInnerHTML: { __html: result.svg }
|
|
1192
|
+
}
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
if (result && !result.ok) {
|
|
1196
|
+
return /* @__PURE__ */ jsxs3("div", { "data-mermaid": true, className: "rich-md-mermaid my-4", children: [
|
|
1197
|
+
/* @__PURE__ */ jsxs3("p", { className: "mb-1 text-xs text-destructive", children: [
|
|
1198
|
+
"Failed to render diagram: ",
|
|
1199
|
+
result.error
|
|
1200
|
+
] }),
|
|
1201
|
+
/* @__PURE__ */ jsx3("pre", { className: "overflow-auto rounded bg-muted p-3 font-mono text-[12px] text-foreground", children: /* @__PURE__ */ jsx3("code", { children: diagram }) })
|
|
1202
|
+
] });
|
|
1203
|
+
}
|
|
1204
|
+
return /* @__PURE__ */ jsx3("div", { "data-mermaid": true, className: "rich-md-mermaid my-4 text-xs text-muted-foreground", children: "Rendering diagram\u2026" });
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// packages/rich-markdown/src/url-policy.ts
|
|
1208
|
+
var LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto"]);
|
|
1209
|
+
var RESOURCE_SCHEMES = /* @__PURE__ */ new Set(["http", "https"]);
|
|
1210
|
+
var EXTERNAL_LINK_REL = "noopener noreferrer nofollow";
|
|
1211
|
+
function urlScheme(url) {
|
|
1212
|
+
const colon = url.indexOf(":");
|
|
1213
|
+
if (colon === -1) return null;
|
|
1214
|
+
for (const separator of ["/", "?", "#"]) {
|
|
1215
|
+
const at = url.indexOf(separator);
|
|
1216
|
+
if (at !== -1 && at < colon) return null;
|
|
1217
|
+
}
|
|
1218
|
+
return url.slice(0, colon).toLowerCase();
|
|
1219
|
+
}
|
|
1220
|
+
function isExternalHref(href) {
|
|
1221
|
+
if (href.startsWith("//")) return true;
|
|
1222
|
+
const scheme = urlScheme(href);
|
|
1223
|
+
return scheme === "http" || scheme === "https";
|
|
1224
|
+
}
|
|
1225
|
+
function makeUrlTransform({ allowDocLinks }) {
|
|
1226
|
+
return (url, key) => {
|
|
1227
|
+
if (key === "href") return isAllowedLinkHref(url, allowDocLinks) ? url : void 0;
|
|
1228
|
+
const scheme = urlScheme(url);
|
|
1229
|
+
if (scheme === null) return url;
|
|
1230
|
+
return RESOURCE_SCHEMES.has(scheme) ? url : void 0;
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function isAllowedLinkHref(href, allowDocLinks) {
|
|
1234
|
+
const scheme = urlScheme(href);
|
|
1235
|
+
if (scheme === null) return true;
|
|
1236
|
+
return LINK_SCHEMES.has(scheme) || allowDocLinks && scheme === "doc";
|
|
1237
|
+
}
|
|
1238
|
+
function isRelativeHref(href) {
|
|
1239
|
+
return !href.startsWith("#") && !href.startsWith("//") && urlScheme(href) === null;
|
|
1240
|
+
}
|
|
1241
|
+
function resolveLinkTarget(href, isFootnoteLink, policy) {
|
|
1242
|
+
if (href === void 0) return {};
|
|
1243
|
+
if (href.startsWith("#")) {
|
|
1244
|
+
if (!policy.fragmentPrefix || href === "#" || isFootnoteLink) return { href };
|
|
1245
|
+
return { href: `#${policy.fragmentPrefix}${href.slice(1)}` };
|
|
1246
|
+
}
|
|
1247
|
+
if (!isRelativeHref(href)) return { href };
|
|
1248
|
+
if (policy.resolveRelativeLink) {
|
|
1249
|
+
const resolved = policy.resolveRelativeLink(href);
|
|
1250
|
+
if (!resolved || !isAllowedLinkHref(resolved.href, policy.allowDocLinks)) return {};
|
|
1251
|
+
return resolved.onClick ? { href: resolved.href, onClick: resolved.onClick } : { href: resolved.href };
|
|
1252
|
+
}
|
|
1253
|
+
return policy.inertRelativeLinks ? {} : { href };
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// packages/rich-markdown/src/components.tsx
|
|
1257
|
+
import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
|
|
1258
|
+
function components({ onLinkClick, embeds, mermaid, links, resolveImage }) {
|
|
1259
|
+
return {
|
|
1260
|
+
img: ({ node: _n, src, alt, ...props }) => {
|
|
1261
|
+
if (resolveImage && typeof src === "string" && isRelativeHref(src)) {
|
|
1262
|
+
const resolved = src ? resolveImage(src) : null;
|
|
1263
|
+
if (!resolved) return /* @__PURE__ */ jsx4("span", { "data-image-placeholder": "", children: imagePlaceholderText(alt) });
|
|
1264
|
+
return /* @__PURE__ */ jsx4("img", { ...props, src: resolved, alt: alt ?? "", className: "max-w-full" });
|
|
1265
|
+
}
|
|
1266
|
+
return /* @__PURE__ */ jsx4("img", { ...props, src, alt });
|
|
1267
|
+
},
|
|
1268
|
+
h1: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("h1", { className: "mb-3 mt-4 text-xl font-semibold text-foreground first:mt-0", ...props }),
|
|
1269
|
+
h2: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("h2", { className: "mb-2 mt-4 text-lg font-semibold text-foreground", ...props }),
|
|
1270
|
+
h3: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("h3", { className: "mb-2 mt-3 text-base font-semibold text-foreground", ...props }),
|
|
1271
|
+
p: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("p", { className: "mb-3 text-sm leading-relaxed text-foreground", ...props }),
|
|
1272
|
+
/**
|
|
1273
|
+
* `pl-5`, NOT `ml-5`.
|
|
1274
|
+
*
|
|
1275
|
+
* A margin moves the whole `ul` box right, and `list-style-position` defaults
|
|
1276
|
+
* to `outside` — so the marker is drawn in the space to the LEFT of the
|
|
1277
|
+
* content box, which the margin had just vacated. Net effect: the bullet
|
|
1278
|
+
* lands back at the container's left edge, flush with the surrounding
|
|
1279
|
+
* paragraphs, and only the item's text looks indented. Padding puts that
|
|
1280
|
+
* space inside the box, so the marker sits within the indent where it
|
|
1281
|
+
* belongs. Tailwind's preflight zeroes the browser's default `padding-left`,
|
|
1282
|
+
* which is why this has to be stated at all.
|
|
1283
|
+
*/
|
|
1284
|
+
ul: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("ul", { className: "mb-3 pl-5 list-disc space-y-1 text-sm text-foreground", ...props }),
|
|
1285
|
+
ol: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("ol", { className: "mb-3 pl-5 list-decimal space-y-1 text-sm text-foreground", ...props }),
|
|
1286
|
+
li: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("li", { className: "text-sm leading-relaxed", ...props }),
|
|
1287
|
+
a: ({ node, href: sourceHref, children, ...props }) => {
|
|
1288
|
+
const properties = node?.properties ?? {};
|
|
1289
|
+
const isFootnoteLink = properties.dataFootnoteRef !== void 0 || properties.dataFootnoteBackref !== void 0;
|
|
1290
|
+
const { href, onClick } = resolveLinkTarget(sourceHref, isFootnoteLink, links);
|
|
1291
|
+
return /* @__PURE__ */ jsx4(
|
|
1292
|
+
"a",
|
|
1293
|
+
{
|
|
1294
|
+
href,
|
|
1295
|
+
className: "text-primary underline underline-offset-2",
|
|
1296
|
+
onClick: (e) => {
|
|
1297
|
+
if (!href) return;
|
|
1298
|
+
onClick?.(e);
|
|
1299
|
+
onLinkClick?.(href, e);
|
|
1300
|
+
},
|
|
1301
|
+
...props,
|
|
1302
|
+
...href && isExternalHref(href) ? { rel: EXTERNAL_LINK_REL, target: "_blank" } : {},
|
|
1303
|
+
children
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
},
|
|
1307
|
+
strong: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("strong", { className: "font-semibold text-foreground", ...props }),
|
|
1308
|
+
blockquote: ({ node: _n, ...props }) => /* @__PURE__ */ jsx4("blockquote", { className: "mb-3 border-l-2 border-border pl-3 text-sm italic text-muted-foreground", ...props }),
|
|
1309
|
+
code: ({ className, children, node }) => {
|
|
1310
|
+
const match = /language-(\w+)/.exec(className || "");
|
|
1311
|
+
const lang = match?.[1];
|
|
1312
|
+
if (lang === "mermaid") {
|
|
1313
|
+
if (!mermaid) return /* @__PURE__ */ jsx4(CodeBlock, { language: "mermaid", children });
|
|
1314
|
+
return /* @__PURE__ */ jsx4(MermaidBlock, { diagram: String(children).replace(/\n$/, "") });
|
|
1315
|
+
}
|
|
1316
|
+
if (lang && embeds && embeds[lang]) {
|
|
1317
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: embeds[lang](String(children).replace(/\n$/, "")) });
|
|
1318
|
+
}
|
|
1319
|
+
if (lang) {
|
|
1320
|
+
return /* @__PURE__ */ jsx4(CodeBlock, { language: lang, children });
|
|
1321
|
+
}
|
|
1322
|
+
const isBlock = node?.position && String(children).includes("\n");
|
|
1323
|
+
if (isBlock) return /* @__PURE__ */ jsx4(CodeBlock, { language: "text", children });
|
|
1324
|
+
return /* @__PURE__ */ jsx4("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[0.9em] text-foreground", children });
|
|
1325
|
+
},
|
|
1326
|
+
pre: ({ children }) => /* @__PURE__ */ jsx4(Fragment2, { children }),
|
|
1327
|
+
table: ({ children }) => /* @__PURE__ */ jsx4("div", { className: "mb-3 overflow-auto rounded border border-border", children: /* @__PURE__ */ jsx4("table", { className: "w-full border-collapse text-sm", children }) }),
|
|
1328
|
+
th: ({ children }) => /* @__PURE__ */ jsx4("th", { className: "border-b border-border bg-muted/40 px-2 py-1 text-left font-semibold text-foreground", children }),
|
|
1329
|
+
td: ({ children }) => /* @__PURE__ */ jsx4("td", { className: "border-b border-border/60 px-2 py-1 text-foreground", children })
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// packages/rich-markdown/src/headings.ts
|
|
1334
|
+
import Markdown from "react-markdown";
|
|
1335
|
+
import remarkGfm from "remark-gfm";
|
|
1336
|
+
var REMARK_PLUGINS = [remarkGfm];
|
|
1337
|
+
var HEADING_TAG = /^h([1-6])$/;
|
|
1338
|
+
var SLUG_STRIP = /[^\p{L}\p{M}\p{N}\p{Pc}\s-]/gu;
|
|
1339
|
+
function headingSlug(text) {
|
|
1340
|
+
const slug = text.trim().toLowerCase().replace(SLUG_STRIP, "").replace(/\s/g, "-");
|
|
1341
|
+
return slug || "section";
|
|
1342
|
+
}
|
|
1343
|
+
function createSlugger() {
|
|
1344
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
1345
|
+
return (text) => {
|
|
1346
|
+
const base = headingSlug(text);
|
|
1347
|
+
let slug = base;
|
|
1348
|
+
while (occurrences.has(slug)) {
|
|
1349
|
+
const next = occurrences.get(base) + 1;
|
|
1350
|
+
occurrences.set(base, next);
|
|
1351
|
+
slug = `${base}-${next}`;
|
|
1352
|
+
}
|
|
1353
|
+
occurrences.set(slug, 0);
|
|
1354
|
+
return slug;
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
function assignIds(node, slug, onHeading, idPrefix = "") {
|
|
1358
|
+
const children = node.children;
|
|
1359
|
+
if (!children) return;
|
|
1360
|
+
for (const child of children) {
|
|
1361
|
+
if (!isElement(child)) continue;
|
|
1362
|
+
const match = HEADING_TAG.exec(child.tagName);
|
|
1363
|
+
if (match && child.properties.id === void 0) {
|
|
1364
|
+
const text = hastToString(child).trim();
|
|
1365
|
+
const id = `${idPrefix}${slug(text)}`;
|
|
1366
|
+
child.properties.id = id;
|
|
1367
|
+
onHeading?.({ depth: Number(match[1]), text, id });
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
assignIds(child, slug, onHeading, idPrefix);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
function rehypeHeadingIds(options = {}) {
|
|
1374
|
+
return (tree) => {
|
|
1375
|
+
assignIds(tree, createSlugger(), options.onHeading, options.idPrefix);
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
function extractHeadings(markdown, options = {}) {
|
|
1379
|
+
const headings = [];
|
|
1380
|
+
if (!markdown) return headings;
|
|
1381
|
+
Markdown({
|
|
1382
|
+
children: markdown,
|
|
1383
|
+
remarkPlugins: REMARK_PLUGINS,
|
|
1384
|
+
rehypePlugins: [
|
|
1385
|
+
[rehypeHeadingIds, { onHeading: (h) => headings.push(h), idPrefix: options.idPrefix }]
|
|
1386
|
+
],
|
|
1387
|
+
allowElement: () => false
|
|
1388
|
+
});
|
|
1389
|
+
return headings;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// packages/rich-markdown/src/rich-markdown.tsx
|
|
1393
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1394
|
+
var URL_TRANSFORM = makeUrlTransform({ allowDocLinks: false });
|
|
1395
|
+
var URL_TRANSFORM_WITH_DOC_LINKS = makeUrlTransform({ allowDocLinks: true });
|
|
1396
|
+
var HIGHLIGHT = [rehypeHighlight, { ignoreMissing: true, plainText: ["mermaid"] }];
|
|
1397
|
+
var PLUGINS_BLOCKING_IMAGES = [rehypeHeadingIds, rehypeBlockImages, HIGHLIGHT];
|
|
1398
|
+
var PLUGINS_ALLOWING_IMAGES = [rehypeHeadingIds, HIGHLIGHT];
|
|
1399
|
+
function rehypePluginsFor(images, headingIdPrefix, keep) {
|
|
1400
|
+
if (!headingIdPrefix && !keep) return images === "allow" ? PLUGINS_ALLOWING_IMAGES : PLUGINS_BLOCKING_IMAGES;
|
|
1401
|
+
const ids = headingIdPrefix ? [rehypeHeadingIds, { idPrefix: headingIdPrefix }] : rehypeHeadingIds;
|
|
1402
|
+
return images === "allow" ? [ids, HIGHLIGHT] : [ids, [rehypeBlockImages, { keep }], HIGHLIGHT];
|
|
1403
|
+
}
|
|
1404
|
+
function resolvedImage(resolveImage, src) {
|
|
1405
|
+
if (!resolveImage || !isRelativeHref(src) || src === "") return null;
|
|
1406
|
+
const url = resolveImage(src);
|
|
1407
|
+
return url !== null && isBundledImageUrl(url) ? url : null;
|
|
1408
|
+
}
|
|
1409
|
+
var UNTRUSTED_HEADING_ID_PREFIX = "md-";
|
|
1410
|
+
var RichMarkdown = memo(function RichMarkdown2({
|
|
1411
|
+
text,
|
|
1412
|
+
className,
|
|
1413
|
+
onLinkClick,
|
|
1414
|
+
embeds,
|
|
1415
|
+
untrusted = false,
|
|
1416
|
+
allowDocLinks = false,
|
|
1417
|
+
images = "allow",
|
|
1418
|
+
mermaid = true,
|
|
1419
|
+
headingIdPrefix,
|
|
1420
|
+
resolveRelativeLink,
|
|
1421
|
+
resolveImage
|
|
1422
|
+
}) {
|
|
1423
|
+
const docLinks = untrusted ? false : allowDocLinks;
|
|
1424
|
+
const imagePolicy = untrusted ? "block" : images;
|
|
1425
|
+
const idPrefix = untrusted ? headingIdPrefix || UNTRUSTED_HEADING_ID_PREFIX : headingIdPrefix;
|
|
1426
|
+
const rehypePlugins = useMemo(
|
|
1427
|
+
() => rehypePluginsFor(
|
|
1428
|
+
imagePolicy,
|
|
1429
|
+
idPrefix,
|
|
1430
|
+
resolveImage ? (src) => resolvedImage(resolveImage, src) !== null : void 0
|
|
1431
|
+
),
|
|
1432
|
+
[imagePolicy, idPrefix, resolveImage]
|
|
1433
|
+
);
|
|
1434
|
+
if (!text) return null;
|
|
1435
|
+
const links = {
|
|
1436
|
+
allowDocLinks: docLinks,
|
|
1437
|
+
resolveRelativeLink,
|
|
1438
|
+
inertRelativeLinks: untrusted,
|
|
1439
|
+
fragmentPrefix: idPrefix
|
|
1440
|
+
};
|
|
1441
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("rich-md", className), children: /* @__PURE__ */ jsx5(
|
|
1442
|
+
ReactMarkdown,
|
|
1443
|
+
{
|
|
1444
|
+
remarkPlugins: REMARK_PLUGINS,
|
|
1445
|
+
rehypePlugins,
|
|
1446
|
+
urlTransform: docLinks ? URL_TRANSFORM_WITH_DOC_LINKS : URL_TRANSFORM,
|
|
1447
|
+
components: components({
|
|
1448
|
+
onLinkClick,
|
|
1449
|
+
embeds,
|
|
1450
|
+
mermaid: untrusted ? false : mermaid,
|
|
1451
|
+
links,
|
|
1452
|
+
...resolveImage ? { resolveImage: (src) => resolvedImage(resolveImage, src) } : {}
|
|
1453
|
+
}),
|
|
1454
|
+
children: text
|
|
1455
|
+
}
|
|
1456
|
+
) });
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
// packages/canvas-api-docs/src/docs/markdown.tsx
|
|
1460
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1461
|
+
var DocsLinkContext = createContext(void 0);
|
|
1462
|
+
var DOCS_HEADING_ID_PREFIX = "docs-";
|
|
1463
|
+
function DocsMarkdown({
|
|
1464
|
+
text,
|
|
1465
|
+
className,
|
|
1466
|
+
idPrefix,
|
|
1467
|
+
resolveImage
|
|
1468
|
+
}) {
|
|
1469
|
+
const scope = useId2().replace(/[^a-zA-Z0-9]/g, "");
|
|
1470
|
+
const resolveRelativeLink = useContext(DocsLinkContext);
|
|
1471
|
+
return /* @__PURE__ */ jsx6(
|
|
1472
|
+
RichMarkdown,
|
|
1473
|
+
{
|
|
1474
|
+
text,
|
|
1475
|
+
className,
|
|
1476
|
+
untrusted: true,
|
|
1477
|
+
headingIdPrefix: idPrefix ?? `${DOCS_HEADING_ID_PREFIX}${scope}-`,
|
|
1478
|
+
...resolveRelativeLink ? { resolveRelativeLink } : {},
|
|
1479
|
+
...resolveImage ? { resolveImage } : {}
|
|
1480
|
+
}
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// packages/canvas-api-docs/src/docs/events-view.tsx
|
|
1485
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1486
|
+
function EventsView({ catalog, focus }) {
|
|
1487
|
+
const { delivery } = catalog;
|
|
1488
|
+
return /* @__PURE__ */ jsxs4("div", { className: "space-y-6", children: [
|
|
1489
|
+
/* @__PURE__ */ jsxs4("header", { className: "space-y-1", children: [
|
|
1490
|
+
/* @__PURE__ */ jsx7("h1", { className: "text-2xl font-semibold", children: "Events" }),
|
|
1491
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-sm text-muted-foreground", children: [
|
|
1492
|
+
catalog.types.length,
|
|
1493
|
+
" event ",
|
|
1494
|
+
catalog.types.length === 1 ? "type" : "types",
|
|
1495
|
+
", delivered by",
|
|
1496
|
+
" ",
|
|
1497
|
+
/* @__PURE__ */ jsx7("code", { className: "font-mono", children: catalog.service }),
|
|
1498
|
+
"."
|
|
1499
|
+
] })
|
|
1500
|
+
] }),
|
|
1501
|
+
delivery && /* @__PURE__ */ jsxs4("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-4 gap-y-1 rounded-md border p-3 text-sm", children: [
|
|
1502
|
+
delivery.payload && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1503
|
+
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Payload" }),
|
|
1504
|
+
/* @__PURE__ */ jsx7("dd", { children: delivery.payload === "thin" ? "Identifiers only \u2014 fetch the resource from the API." : "The resource inline." })
|
|
1505
|
+
] }),
|
|
1506
|
+
delivery.retry?.maxAttempts && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1507
|
+
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Retries" }),
|
|
1508
|
+
/* @__PURE__ */ jsxs4("dd", { children: [
|
|
1509
|
+
"Up to ",
|
|
1510
|
+
delivery.retry.maxAttempts,
|
|
1511
|
+
" attempts per endpoint."
|
|
1512
|
+
] })
|
|
1513
|
+
] }),
|
|
1514
|
+
delivery.signing && delivery.signing.length > 0 && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1515
|
+
/* @__PURE__ */ jsx7("dt", { className: "text-muted-foreground", children: "Signing" }),
|
|
1516
|
+
/* @__PURE__ */ jsx7("dd", { children: delivery.signing.join(", ") })
|
|
1517
|
+
] })
|
|
1518
|
+
] }),
|
|
1519
|
+
catalog.types.map((type) => /* @__PURE__ */ jsxs4("section", { id: `event-${type.name}`, className: "space-y-2", "data-focused": focus === type.name || void 0, children: [
|
|
1520
|
+
/* @__PURE__ */ jsxs4("h2", { className: "flex flex-wrap items-baseline gap-2 font-mono text-base font-semibold", children: [
|
|
1521
|
+
type.name,
|
|
1522
|
+
type.schemaVersion !== void 0 && /* @__PURE__ */ jsxs4("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[11px] font-normal text-muted-foreground", children: [
|
|
1523
|
+
"v",
|
|
1524
|
+
type.schemaVersion
|
|
1525
|
+
] })
|
|
1526
|
+
] }),
|
|
1527
|
+
type.description && /* @__PURE__ */ jsx7(DocsMarkdown, { text: type.description, className: "text-sm" }),
|
|
1528
|
+
type.payloadSchema ? /* @__PURE__ */ jsx7(FieldRows, { fields: jsonSchemaFields(type.payloadSchema), renderText: (t) => /* @__PURE__ */ jsx7(DocsMarkdown, { text: t }) }) : /* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: "This event's payload schema is not published." })
|
|
1529
|
+
] }, type.name))
|
|
1530
|
+
] });
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// packages/canvas-api-docs/src/docs/guide-page.tsx
|
|
1534
|
+
import { useCallback } from "react";
|
|
1535
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1536
|
+
function GuidePage({
|
|
1537
|
+
page,
|
|
1538
|
+
pageId,
|
|
1539
|
+
images
|
|
1540
|
+
}) {
|
|
1541
|
+
const resolveImage = useCallback(
|
|
1542
|
+
(src) => pageId === void 0 ? null : docsImageUrl({ images }, pageId, src),
|
|
1543
|
+
[images, pageId]
|
|
1544
|
+
);
|
|
1545
|
+
const toc = extractHeadings(page.markdown, { idPrefix: DOCS_HEADING_ID_PREFIX }).filter((h) => h.depth === 2 || h.depth === 3);
|
|
1546
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex gap-8", children: [
|
|
1547
|
+
/* @__PURE__ */ jsxs5("article", { className: "min-w-0 max-w-3xl flex-1 space-y-4", children: [
|
|
1548
|
+
/* @__PURE__ */ jsxs5("header", { className: "space-y-1", children: [
|
|
1549
|
+
/* @__PURE__ */ jsx8("h1", { className: "text-2xl font-semibold", children: page.title }),
|
|
1550
|
+
page.description && /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: page.description })
|
|
1551
|
+
] }),
|
|
1552
|
+
/* @__PURE__ */ jsx8(DocsMarkdown, { text: page.markdown, className: "text-sm", idPrefix: DOCS_HEADING_ID_PREFIX, resolveImage })
|
|
1553
|
+
] }),
|
|
1554
|
+
toc.length > 1 && /* @__PURE__ */ jsxs5("nav", { "aria-label": "On this page", className: "hidden w-48 shrink-0 text-sm xl:block", children: [
|
|
1555
|
+
/* @__PURE__ */ jsx8("p", { className: "mb-2 font-medium", children: "On this page" }),
|
|
1556
|
+
/* @__PURE__ */ jsx8("ul", { className: "space-y-1", children: toc.map((h) => /* @__PURE__ */ jsx8("li", { className: h.depth === 3 ? "pl-3" : void 0, children: /* @__PURE__ */ jsx8("a", { href: `#${h.id}`, className: "text-muted-foreground hover:text-foreground", children: h.text }) }, h.id)) })
|
|
1557
|
+
] })
|
|
1558
|
+
] });
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// packages/canvas-api-docs/src/docs/model.ts
|
|
1562
|
+
function initialLocation(bundle) {
|
|
1563
|
+
const landing = bundle?.config.landing;
|
|
1564
|
+
return landing && docsPage(bundle, landing) ? { kind: "page", page: landing } : { kind: "reference" };
|
|
1565
|
+
}
|
|
1566
|
+
function locationToParams(location) {
|
|
1567
|
+
const params = new URLSearchParams();
|
|
1568
|
+
if (location.kind === "page") params.set("page", location.page);
|
|
1569
|
+
else if (location.kind === "reference") {
|
|
1570
|
+
params.set("section", "reference");
|
|
1571
|
+
if (location.op) params.set("op", location.op);
|
|
1572
|
+
} else {
|
|
1573
|
+
params.set("section", "events");
|
|
1574
|
+
if (location.event) params.set("event", location.event);
|
|
1575
|
+
}
|
|
1576
|
+
return params;
|
|
1577
|
+
}
|
|
1578
|
+
function locationFromParams(params, bundle) {
|
|
1579
|
+
const page = params.get("page");
|
|
1580
|
+
if (page && docsPage(bundle, page)) return { kind: "page", page };
|
|
1581
|
+
const section = params.get("section");
|
|
1582
|
+
if (section === "reference" && bundle?.config.reference !== false) {
|
|
1583
|
+
const op = params.get("op");
|
|
1584
|
+
return op ? { kind: "reference", op } : { kind: "reference" };
|
|
1585
|
+
}
|
|
1586
|
+
if (section === "events" && bundle?.events) {
|
|
1587
|
+
const event = params.get("event");
|
|
1588
|
+
return event ? { kind: "events", event } : { kind: "events" };
|
|
1589
|
+
}
|
|
1590
|
+
return initialLocation(bundle);
|
|
1591
|
+
}
|
|
1592
|
+
function navGroups(bundle) {
|
|
1593
|
+
if (!bundle) return [];
|
|
1594
|
+
return (bundle.config.nav ?? []).map((g) => ({
|
|
1595
|
+
group: g.group,
|
|
1596
|
+
pages: g.pages.flatMap((id) => {
|
|
1597
|
+
const page = docsPage(bundle, id);
|
|
1598
|
+
return page ? [{ id, title: page.title }] : [];
|
|
1599
|
+
})
|
|
1600
|
+
})).filter((g) => g.pages.length > 0);
|
|
1601
|
+
}
|
|
1602
|
+
var UNTAGGED_GROUP = "Other";
|
|
1603
|
+
function referenceGroups(index, tags) {
|
|
1604
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1605
|
+
for (const t of tags) groups.set(t.name, { tag: t.name, ...t.description ? { description: t.description } : {}, ops: [] });
|
|
1606
|
+
const untagged = [];
|
|
1607
|
+
for (const entry of index) {
|
|
1608
|
+
const tag = entry.tags?.[0];
|
|
1609
|
+
if (!tag) {
|
|
1610
|
+
untagged.push(entry);
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
if (!groups.has(tag)) groups.set(tag, { tag, ops: [] });
|
|
1614
|
+
groups.get(tag).ops.push(entry);
|
|
1615
|
+
}
|
|
1616
|
+
const out = [...groups.values()].filter((g) => g.ops.length > 0);
|
|
1617
|
+
if (untagged.length > 0) out.push({ tag: UNTAGGED_GROUP, ops: untagged });
|
|
1618
|
+
return out;
|
|
1619
|
+
}
|
|
1620
|
+
var AVAILABILITY_LABELS = {
|
|
1621
|
+
available: "Available",
|
|
1622
|
+
not_enabled: "Not enabled here",
|
|
1623
|
+
not_implemented: "Not implemented",
|
|
1624
|
+
not_supported: "Not supported"
|
|
1625
|
+
};
|
|
1626
|
+
function availabilityOf(bundle, opId) {
|
|
1627
|
+
const availability = bundle?.availability;
|
|
1628
|
+
return availability && Object.hasOwn(availability, opId) ? availability[opId] : void 0;
|
|
1629
|
+
}
|
|
1630
|
+
function tryItGate(input) {
|
|
1631
|
+
const { availability } = input;
|
|
1632
|
+
if (!availability && input.availabilityPublished) {
|
|
1633
|
+
return { runnable: false, reason: "This deployment publishes no availability for this operation." };
|
|
1634
|
+
}
|
|
1635
|
+
if (availability && availability.state !== "available") {
|
|
1636
|
+
const label = AVAILABILITY_LABELS[availability.state];
|
|
1637
|
+
return { runnable: false, reason: availability.reason ? `${label} \u2014 ${availability.reason}` : label };
|
|
1638
|
+
}
|
|
1639
|
+
if (input.sandbox === null) return { runnable: true };
|
|
1640
|
+
const sandbox = input.sandbox ?? DEFAULT_SANDBOX_MODE;
|
|
1641
|
+
const allowed = input.write === void 0 ? tryItAllows(input.method, sandbox) : !input.write || sandbox !== "live";
|
|
1642
|
+
if (!allowed) {
|
|
1643
|
+
return { runnable: false, reason: `Read-only here: this deployment is ${sandbox}, so writes do not run.` };
|
|
1644
|
+
}
|
|
1645
|
+
return { runnable: true };
|
|
1646
|
+
}
|
|
1647
|
+
var SANDBOX_LABELS = {
|
|
1648
|
+
live: "Live \u2014 reads only",
|
|
1649
|
+
mock: "Mock \u2014 writes run against fixtures",
|
|
1650
|
+
"vendor-sandbox": "Vendor sandbox \u2014 writes run against the vendor's sandbox"
|
|
1651
|
+
};
|
|
1652
|
+
var MAX_DOCS_HREF_LENGTH = 2048;
|
|
1653
|
+
function resolveDocsLink(href, bundle) {
|
|
1654
|
+
if (!href || href.length > MAX_DOCS_HREF_LENGTH) return null;
|
|
1655
|
+
if (href.startsWith("/") || href.startsWith("?") || href.startsWith("#")) return null;
|
|
1656
|
+
let path = href.split("#")[0].split("?")[0];
|
|
1657
|
+
while (path.startsWith("./") || path.startsWith("../")) path = path.slice(path.startsWith("./") ? 2 : 3);
|
|
1658
|
+
let end = path.length;
|
|
1659
|
+
while (end > 0 && path[end - 1] === "/") end--;
|
|
1660
|
+
path = path.slice(0, end);
|
|
1661
|
+
if (path.endsWith(".md")) path = path.slice(0, -3);
|
|
1662
|
+
if (!path) return null;
|
|
1663
|
+
if (path === "api") return { kind: "reference" };
|
|
1664
|
+
if (path.startsWith("api/")) {
|
|
1665
|
+
const op = path.slice(4);
|
|
1666
|
+
return op && !op.includes("/") ? { kind: "reference", op } : { kind: "reference" };
|
|
1667
|
+
}
|
|
1668
|
+
if (path === "events") return bundle?.events ? { kind: "events" } : null;
|
|
1669
|
+
return docsPage(bundle, path) ? { kind: "page", page: path } : null;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// packages/canvas-api-docs/src/docs/reference-view.tsx
|
|
1673
|
+
import { useEffect as useEffect3, useState as useState5 } from "react";
|
|
1674
|
+
|
|
1675
|
+
// packages/canvas-api-docs/src/docs/availability-badge.tsx
|
|
1676
|
+
import { jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1677
|
+
var TONE = {
|
|
1678
|
+
available: "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
|
|
1679
|
+
not_enabled: "border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300",
|
|
1680
|
+
not_implemented: "border-muted-foreground/30 bg-muted text-muted-foreground",
|
|
1681
|
+
not_supported: "border-muted-foreground/30 bg-muted text-muted-foreground"
|
|
1682
|
+
};
|
|
1683
|
+
function AvailabilityBadge({ availability }) {
|
|
1684
|
+
if (!availability) return null;
|
|
1685
|
+
const label = AVAILABILITY_LABELS[availability.state];
|
|
1686
|
+
return /* @__PURE__ */ jsxs6(
|
|
1687
|
+
"span",
|
|
1688
|
+
{
|
|
1689
|
+
className: `inline-flex items-center rounded border px-1.5 py-0.5 text-[11px] font-medium ${TONE[availability.state]}`,
|
|
1690
|
+
title: availability.reason ?? label,
|
|
1691
|
+
"data-availability": availability.state,
|
|
1692
|
+
children: [
|
|
1693
|
+
label,
|
|
1694
|
+
availability.reason ? /* @__PURE__ */ jsxs6("span", { className: "sr-only", children: [
|
|
1695
|
+
" \u2014 ",
|
|
1696
|
+
availability.reason
|
|
1697
|
+
] }) : null
|
|
1698
|
+
]
|
|
1699
|
+
}
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// packages/canvas-api-docs/src/docs/op-doc.tsx
|
|
1704
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1705
|
+
var md = (text) => /* @__PURE__ */ jsx9(DocsMarkdown, { text });
|
|
1706
|
+
function Section({ title, children }) {
|
|
1707
|
+
return /* @__PURE__ */ jsxs7("section", { className: "space-y-2", children: [
|
|
1708
|
+
/* @__PURE__ */ jsx9("h3", { className: "text-sm font-semibold", children: title }),
|
|
1709
|
+
children
|
|
1710
|
+
] });
|
|
1711
|
+
}
|
|
1712
|
+
function JsonBlock({ value, label }) {
|
|
1713
|
+
return /* @__PURE__ */ jsxs7("figure", { className: "space-y-1", children: [
|
|
1714
|
+
label && /* @__PURE__ */ jsx9("figcaption", { className: "text-xs text-muted-foreground", children: label }),
|
|
1715
|
+
/* @__PURE__ */ jsx9("pre", { className: "max-h-96 overflow-auto rounded-md border bg-muted/40 p-3 font-mono text-xs", children: JSON.stringify(value, null, 2) })
|
|
1716
|
+
] });
|
|
1717
|
+
}
|
|
1718
|
+
function isShownSample(value) {
|
|
1719
|
+
if (value === void 0) return false;
|
|
1720
|
+
return !(typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0);
|
|
1721
|
+
}
|
|
1722
|
+
function ParametersTable({ parameters }) {
|
|
1723
|
+
return /* @__PURE__ */ jsx9("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs7("table", { className: "min-w-full border-collapse text-sm", children: [
|
|
1724
|
+
/* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "border-b bg-muted/40 text-left text-[11px] uppercase tracking-wide text-muted-foreground", children: [
|
|
1725
|
+
/* @__PURE__ */ jsx9("th", { className: "px-3 py-1.5 font-medium", children: "Name" }),
|
|
1726
|
+
/* @__PURE__ */ jsx9("th", { className: "px-3 py-1.5 font-medium", children: "In" }),
|
|
1727
|
+
/* @__PURE__ */ jsx9("th", { className: "px-3 py-1.5 font-medium", children: "Type" }),
|
|
1728
|
+
/* @__PURE__ */ jsx9("th", { className: "w-full px-3 py-1.5 font-medium", children: "Description" })
|
|
1729
|
+
] }) }),
|
|
1730
|
+
/* @__PURE__ */ jsx9("tbody", { children: parameters.map((p) => /* @__PURE__ */ jsxs7("tr", { className: "border-b align-top last:border-0", children: [
|
|
1731
|
+
/* @__PURE__ */ jsxs7("td", { className: "whitespace-nowrap px-3 py-1.5 font-mono text-xs", children: [
|
|
1732
|
+
p.name,
|
|
1733
|
+
p.required && /* @__PURE__ */ jsx9("span", { className: "ml-1 text-destructive", title: "Required", children: "*" }),
|
|
1734
|
+
p.deprecated && /* @__PURE__ */ jsx9("span", { className: "ml-1 text-[10px] text-muted-foreground", children: "deprecated" })
|
|
1735
|
+
] }),
|
|
1736
|
+
/* @__PURE__ */ jsx9("td", { className: "px-3 py-1.5 text-xs text-muted-foreground", children: p.in }),
|
|
1737
|
+
/* @__PURE__ */ jsx9("td", { className: "whitespace-nowrap px-3 py-1.5", children: /* @__PURE__ */ jsxs7("span", { className: "inline-block rounded bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground", children: [
|
|
1738
|
+
p.schema?.type ?? "string",
|
|
1739
|
+
p.schema?.format ? ` (${p.schema.format})` : ""
|
|
1740
|
+
] }) }),
|
|
1741
|
+
/* @__PURE__ */ jsxs7("td", { className: "px-3 py-1.5 text-xs text-muted-foreground", children: [
|
|
1742
|
+
p.description && md(p.description),
|
|
1743
|
+
p.schema?.enum && /* @__PURE__ */ jsxs7("p", { children: [
|
|
1744
|
+
"One of: ",
|
|
1745
|
+
/* @__PURE__ */ jsx9("code", { className: "font-mono", children: p.schema.enum.map(String).join(", ") })
|
|
1746
|
+
] }),
|
|
1747
|
+
p.schema?.default !== void 0 && /* @__PURE__ */ jsxs7("p", { children: [
|
|
1748
|
+
"Default: ",
|
|
1749
|
+
/* @__PURE__ */ jsx9("code", { className: "font-mono", children: String(p.schema.default) })
|
|
1750
|
+
] }),
|
|
1751
|
+
(p.schema?.minimum !== void 0 || p.schema?.maximum !== void 0) && /* @__PURE__ */ jsxs7("p", { children: [
|
|
1752
|
+
"Range: ",
|
|
1753
|
+
p.schema?.minimum ?? "\u2026",
|
|
1754
|
+
"\u2013",
|
|
1755
|
+
p.schema?.maximum ?? "\u2026"
|
|
1756
|
+
] })
|
|
1757
|
+
] })
|
|
1758
|
+
] }, `${p.in}:${p.name}`)) })
|
|
1759
|
+
] }) });
|
|
1760
|
+
}
|
|
1761
|
+
function Examples({ examples }) {
|
|
1762
|
+
return /* @__PURE__ */ jsx9("div", { className: "space-y-2", children: examples.map((ex) => /* @__PURE__ */ jsx9(JsonBlock, { value: ex.value, label: ex.summary ?? ex.name }, ex.name)) });
|
|
1763
|
+
}
|
|
1764
|
+
function ResponseBlock({
|
|
1765
|
+
response,
|
|
1766
|
+
loadType
|
|
1767
|
+
}) {
|
|
1768
|
+
const ok = response.status.startsWith("2");
|
|
1769
|
+
return /* @__PURE__ */ jsxs7("details", { className: "rounded-md border", open: ok || void 0, children: [
|
|
1770
|
+
/* @__PURE__ */ jsxs7("summary", { className: "flex cursor-pointer select-none items-center gap-2 px-3 py-2 text-sm", children: [
|
|
1771
|
+
/* @__PURE__ */ jsx9(
|
|
1772
|
+
"span",
|
|
1773
|
+
{
|
|
1774
|
+
className: `rounded px-1.5 py-0.5 font-mono text-[11px] font-semibold ${ok ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300" : "bg-muted text-muted-foreground"}`,
|
|
1775
|
+
children: response.status
|
|
1776
|
+
}
|
|
1777
|
+
),
|
|
1778
|
+
/* @__PURE__ */ jsx9("span", { className: "text-muted-foreground", children: response.description ?? "" }),
|
|
1779
|
+
response.contentType && /* @__PURE__ */ jsx9("span", { className: "ml-auto font-mono text-[11px] text-muted-foreground", children: response.contentType })
|
|
1780
|
+
] }),
|
|
1781
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-3 border-t p-3", children: [
|
|
1782
|
+
response.headers && response.headers.length > 0 && /* @__PURE__ */ jsxs7("div", { className: "space-y-1", "data-response-headers": true, children: [
|
|
1783
|
+
/* @__PURE__ */ jsx9("p", { className: "text-xs font-medium text-muted-foreground", children: "Headers" }),
|
|
1784
|
+
/* @__PURE__ */ jsx9("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-xs", children: response.headers.map((h) => /* @__PURE__ */ jsxs7("div", { className: "contents", children: [
|
|
1785
|
+
/* @__PURE__ */ jsxs7("dt", { className: "font-mono", children: [
|
|
1786
|
+
h.name,
|
|
1787
|
+
h.schema?.type ? /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
1788
|
+
" ",
|
|
1789
|
+
h.schema.type
|
|
1790
|
+
] }) : null
|
|
1791
|
+
] }),
|
|
1792
|
+
/* @__PURE__ */ jsx9("dd", { className: "text-muted-foreground", children: h.description ? md(h.description) : null })
|
|
1793
|
+
] }, h.name)) })
|
|
1794
|
+
] }),
|
|
1795
|
+
response.fields && response.fields.length > 0 && /* @__PURE__ */ jsx9(FieldRows, { fields: response.fields, loadType, renderText: md }),
|
|
1796
|
+
response.examples && response.examples.length > 0 ? /* @__PURE__ */ jsx9(Examples, { examples: response.examples }) : isShownSample(response.sample) ? /* @__PURE__ */ jsx9(JsonBlock, { value: response.sample, label: "Example" }) : null
|
|
1797
|
+
] })
|
|
1798
|
+
] });
|
|
1799
|
+
}
|
|
1800
|
+
function SchemeLine({ scheme }) {
|
|
1801
|
+
return /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
1802
|
+
/* @__PURE__ */ jsx9("code", { className: "font-mono", children: scheme.scheme }),
|
|
1803
|
+
scheme.schemeDetail ? /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
1804
|
+
" \u2014 ",
|
|
1805
|
+
scheme.schemeDetail
|
|
1806
|
+
] }) : null,
|
|
1807
|
+
scheme.scopes && scheme.scopes.length > 0 ? /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
1808
|
+
" (scopes: ",
|
|
1809
|
+
scheme.scopes.join(", "),
|
|
1810
|
+
")"
|
|
1811
|
+
] }) : null
|
|
1812
|
+
] });
|
|
1813
|
+
}
|
|
1814
|
+
function SecurityRequirements({ requirements }) {
|
|
1815
|
+
if (requirements.length === 0) {
|
|
1816
|
+
return /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: "None \u2014 this operation is public." });
|
|
1817
|
+
}
|
|
1818
|
+
const optional = requirements.some((r) => r.schemes.length === 0);
|
|
1819
|
+
const alternatives = requirements.filter((r) => r.schemes.length > 0);
|
|
1820
|
+
if (alternatives.length === 0) {
|
|
1821
|
+
return /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", "data-security": "none", children: "None \u2014 this operation needs no credential." });
|
|
1822
|
+
}
|
|
1823
|
+
const lead = optional ? "Optional \u2014 a request may carry no credential, or:" : alternatives.length > 1 ? "Any one of:" : null;
|
|
1824
|
+
return /* @__PURE__ */ jsxs7("div", { className: "space-y-1 text-sm", "data-security": optional ? "optional" : alternatives.length > 1 ? "any-of" : "required", children: [
|
|
1825
|
+
lead && /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground", children: lead }),
|
|
1826
|
+
/* @__PURE__ */ jsx9("ul", { className: "space-y-1", children: alternatives.map((r, i) => /* @__PURE__ */ jsx9("li", { children: r.schemes.map((scheme, j) => /* @__PURE__ */ jsxs7("span", { children: [
|
|
1827
|
+
j > 0 ? /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground", children: " and " }) : null,
|
|
1828
|
+
/* @__PURE__ */ jsx9(SchemeLine, { scheme })
|
|
1829
|
+
] }, scheme.scheme)) }, i)) })
|
|
1830
|
+
] });
|
|
1831
|
+
}
|
|
1832
|
+
function OpDoc({
|
|
1833
|
+
op,
|
|
1834
|
+
availability,
|
|
1835
|
+
availabilityPublished,
|
|
1836
|
+
sandbox,
|
|
1837
|
+
host
|
|
1838
|
+
}) {
|
|
1839
|
+
const gate = tryItGate({
|
|
1840
|
+
method: op.method,
|
|
1841
|
+
...host.isWrite ? { write: host.isWrite(op) } : {},
|
|
1842
|
+
availability,
|
|
1843
|
+
availabilityPublished,
|
|
1844
|
+
sandbox
|
|
1845
|
+
});
|
|
1846
|
+
const requestExamples = op.requestExamples ?? [];
|
|
1847
|
+
const loadType = host.loadType ? (ref) => host.loadType(ref) : void 0;
|
|
1848
|
+
return /* @__PURE__ */ jsxs7("article", { className: "space-y-5", "data-op": op.id, children: [
|
|
1849
|
+
/* @__PURE__ */ jsxs7("header", { className: "space-y-2", children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1851
|
+
/* @__PURE__ */ jsx9(MethodLabel, { method: op.method }),
|
|
1852
|
+
/* @__PURE__ */ jsx9("code", { className: "font-mono text-sm", children: op.route }),
|
|
1853
|
+
/* @__PURE__ */ jsx9(AvailabilityBadge, { availability }),
|
|
1854
|
+
op.deprecated && /* @__PURE__ */ jsx9("span", { className: "rounded border px-1.5 py-0.5 text-[11px] text-muted-foreground", children: "Deprecated" })
|
|
1855
|
+
] }),
|
|
1856
|
+
/* @__PURE__ */ jsx9("h2", { className: "text-lg font-semibold", children: op.summary ?? op.name }),
|
|
1857
|
+
op.operationId && /* @__PURE__ */ jsx9("p", { className: "font-mono text-[11px] text-muted-foreground", children: op.operationId })
|
|
1858
|
+
] }),
|
|
1859
|
+
op.description && /* @__PURE__ */ jsx9(DocsMarkdown, { text: op.description, className: "text-sm" }),
|
|
1860
|
+
op.security && /* @__PURE__ */ jsx9(Section, { title: "Authentication", children: /* @__PURE__ */ jsx9(SecurityRequirements, { requirements: op.security }) }),
|
|
1861
|
+
op.parameters && op.parameters.length > 0 && /* @__PURE__ */ jsx9(Section, { title: "Parameters", children: /* @__PURE__ */ jsx9(ParametersTable, { parameters: op.parameters }) }),
|
|
1862
|
+
(op.requestBody || op.request.length > 0) && /* @__PURE__ */ jsxs7(Section, { title: "Request body", children: [
|
|
1863
|
+
op.requestBody && /* @__PURE__ */ jsxs7("p", { className: "text-xs text-muted-foreground", "data-request-body": true, children: [
|
|
1864
|
+
op.requestBody.required ? "Required" : "Optional",
|
|
1865
|
+
op.requestBody.contentType ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
1866
|
+
" \xB7 ",
|
|
1867
|
+
/* @__PURE__ */ jsx9("code", { className: "font-mono", children: op.requestBody.contentType })
|
|
1868
|
+
] }) : null
|
|
1869
|
+
] }),
|
|
1870
|
+
op.requestBody?.description && /* @__PURE__ */ jsx9(DocsMarkdown, { text: op.requestBody.description, className: "text-sm" }),
|
|
1871
|
+
op.request.length > 0 && /* @__PURE__ */ jsx9(FieldRows, { fields: op.request, loadType, renderText: md }),
|
|
1872
|
+
requestExamples.length > 0 ? /* @__PURE__ */ jsx9(Examples, { examples: requestExamples }) : isShownSample(op.sampleRequest) ? /* @__PURE__ */ jsx9(JsonBlock, { value: op.sampleRequest, label: "Example" }) : null
|
|
1873
|
+
] }),
|
|
1874
|
+
op.responses && op.responses.length > 0 && /* @__PURE__ */ jsx9(Section, { title: "Responses", children: /* @__PURE__ */ jsx9("div", { className: "space-y-2", children: op.responses.map((r) => /* @__PURE__ */ jsx9(ResponseBlock, { response: r, loadType }, r.status)) }) }),
|
|
1875
|
+
host.renderTryIt ? /* @__PURE__ */ jsxs7(Section, { title: "Try it", children: [
|
|
1876
|
+
!gate.runnable && /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", "data-try-it-refused": true, children: gate.reason }),
|
|
1877
|
+
host.renderTryIt(op, gate)
|
|
1878
|
+
] }) : null
|
|
1879
|
+
] });
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// packages/canvas-api-docs/src/docs/reference-view.tsx
|
|
1883
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1884
|
+
var METHOD_TONE = {
|
|
1885
|
+
GET: "text-emerald-700 dark:text-emerald-400",
|
|
1886
|
+
POST: "text-blue-700 dark:text-blue-400",
|
|
1887
|
+
PUT: "text-amber-700 dark:text-amber-400",
|
|
1888
|
+
PATCH: "text-amber-700 dark:text-amber-400",
|
|
1889
|
+
DELETE: "text-red-700 dark:text-red-400"
|
|
1890
|
+
};
|
|
1891
|
+
function MethodLabel({ method }) {
|
|
1892
|
+
const m = method.toUpperCase();
|
|
1893
|
+
return /* @__PURE__ */ jsx10("span", { className: `inline-block w-14 shrink-0 font-mono text-[11px] font-semibold ${METHOD_TONE[m] ?? "text-muted-foreground"}`, children: m });
|
|
1894
|
+
}
|
|
1895
|
+
function ReferenceView(props) {
|
|
1896
|
+
const { selected, host } = props;
|
|
1897
|
+
const loadOp = host.loadOp;
|
|
1898
|
+
const groups = referenceGroups(props.index, props.tags);
|
|
1899
|
+
const [fetched, setFetched] = useState5(null);
|
|
1900
|
+
const preloaded = selected && props.preloadedOp?.id === selected ? props.preloadedOp : void 0;
|
|
1901
|
+
const loaded = preloaded ? { id: preloaded.id, op: preloaded } : fetched;
|
|
1902
|
+
useEffect3(() => {
|
|
1903
|
+
if (!selected) {
|
|
1904
|
+
setFetched(null);
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (preloaded) return;
|
|
1908
|
+
let cancelled = false;
|
|
1909
|
+
loadOp(selected).then(
|
|
1910
|
+
(op) => !cancelled && setFetched({ id: selected, op }),
|
|
1911
|
+
() => !cancelled && setFetched({ id: selected, error: "This operation's documentation could not be read." })
|
|
1912
|
+
);
|
|
1913
|
+
return () => {
|
|
1914
|
+
cancelled = true;
|
|
1915
|
+
};
|
|
1916
|
+
}, [selected, loadOp, preloaded]);
|
|
1917
|
+
return /* @__PURE__ */ jsxs8("div", { className: "space-y-6", children: [
|
|
1918
|
+
/* @__PURE__ */ jsxs8("header", { className: "space-y-1", children: [
|
|
1919
|
+
/* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold", children: props.label }),
|
|
1920
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-sm text-muted-foreground", children: [
|
|
1921
|
+
props.index.length,
|
|
1922
|
+
" operations",
|
|
1923
|
+
props.serverUrl ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
1924
|
+
" ",
|
|
1925
|
+
"on ",
|
|
1926
|
+
/* @__PURE__ */ jsx10("code", { className: "font-mono", children: props.serverUrl })
|
|
1927
|
+
] }) : null,
|
|
1928
|
+
".",
|
|
1929
|
+
!props.serverUrl && props.calledThrough ? /* @__PURE__ */ jsxs8("span", { "data-testid": "docs-called-through", children: [
|
|
1930
|
+
" ",
|
|
1931
|
+
props.calledThrough,
|
|
1932
|
+
"."
|
|
1933
|
+
] }) : null
|
|
1934
|
+
] })
|
|
1935
|
+
] }),
|
|
1936
|
+
selected && /* @__PURE__ */ jsx10("section", { "aria-live": "polite", className: "rounded-md border p-4", children: !loaded || loaded.id !== selected ? /* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground", children: "Loading\u2026" }) : "error" in loaded ? /* @__PURE__ */ jsx10("p", { className: "text-sm text-destructive", children: loaded.error }) : /* @__PURE__ */ jsx10(
|
|
1937
|
+
OpDoc,
|
|
1938
|
+
{
|
|
1939
|
+
op: loaded.op,
|
|
1940
|
+
availability: availabilityOf(props.bundle, selected),
|
|
1941
|
+
availabilityPublished: Boolean(props.bundle?.availability),
|
|
1942
|
+
sandbox: props.sandbox,
|
|
1943
|
+
host
|
|
1944
|
+
}
|
|
1945
|
+
) }),
|
|
1946
|
+
/* @__PURE__ */ jsx10("div", { className: "space-y-2", children: groups.map((g) => /* @__PURE__ */ jsxs8("details", { className: "rounded-md border", open: g.ops.some((o) => o.id === selected) || void 0, children: [
|
|
1947
|
+
/* @__PURE__ */ jsxs8("summary", { className: "cursor-pointer select-none px-3 py-2 text-sm font-medium", children: [
|
|
1948
|
+
g.tag,
|
|
1949
|
+
" ",
|
|
1950
|
+
/* @__PURE__ */ jsxs8("span", { className: "text-muted-foreground", children: [
|
|
1951
|
+
"(",
|
|
1952
|
+
g.ops.length,
|
|
1953
|
+
")"
|
|
1954
|
+
] })
|
|
1955
|
+
] }),
|
|
1956
|
+
g.description && /* @__PURE__ */ jsx10(DocsMarkdown, { text: g.description, className: "px-3 pb-2 text-xs text-muted-foreground" }),
|
|
1957
|
+
/* @__PURE__ */ jsx10("ul", { className: "border-t", children: g.ops.map((o) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsxs8(
|
|
1958
|
+
"a",
|
|
1959
|
+
{
|
|
1960
|
+
href: props.hrefFor(o.id),
|
|
1961
|
+
"aria-current": o.id === selected ? "true" : void 0,
|
|
1962
|
+
onClick: (e) => {
|
|
1963
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
|
1964
|
+
e.preventDefault();
|
|
1965
|
+
props.onSelect(o.id);
|
|
1966
|
+
},
|
|
1967
|
+
className: `flex items-center gap-2 px-3 py-1.5 text-sm hover:bg-muted/50 ${o.id === selected ? "bg-muted" : ""}`,
|
|
1968
|
+
children: [
|
|
1969
|
+
/* @__PURE__ */ jsx10(MethodLabel, { method: o.method }),
|
|
1970
|
+
/* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: o.route }),
|
|
1971
|
+
/* @__PURE__ */ jsx10("span", { className: "hidden truncate text-xs text-muted-foreground md:inline", children: o.summary ?? o.name }),
|
|
1972
|
+
/* @__PURE__ */ jsx10(AvailabilityBadge, { availability: availabilityOf(props.bundle, o.id) })
|
|
1973
|
+
]
|
|
1974
|
+
}
|
|
1975
|
+
) }, o.id)) })
|
|
1976
|
+
] }, g.tag)) })
|
|
1977
|
+
] });
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// packages/canvas-api-docs/src/docs/docs-source-view.tsx
|
|
1981
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1982
|
+
function NavLink({
|
|
1983
|
+
active,
|
|
1984
|
+
href,
|
|
1985
|
+
onClick,
|
|
1986
|
+
children
|
|
1987
|
+
}) {
|
|
1988
|
+
return /* @__PURE__ */ jsx11(
|
|
1989
|
+
"a",
|
|
1990
|
+
{
|
|
1991
|
+
href,
|
|
1992
|
+
"aria-current": active ? "page" : void 0,
|
|
1993
|
+
onClick: (e) => {
|
|
1994
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
|
1995
|
+
e.preventDefault();
|
|
1996
|
+
onClick();
|
|
1997
|
+
},
|
|
1998
|
+
className: `block rounded px-2 py-1 text-sm ${active ? "bg-muted font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"}`,
|
|
1999
|
+
children
|
|
2000
|
+
}
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
function DocsSourceView(props) {
|
|
2004
|
+
const { bundle, location, onNavigate, hrefFor, sandbox } = props;
|
|
2005
|
+
const groups = navGroups(bundle);
|
|
2006
|
+
const landing = bundle?.config.landing;
|
|
2007
|
+
const title = bundle?.config.title ?? props.title;
|
|
2008
|
+
const reference = bundle?.config.reference;
|
|
2009
|
+
const hasReference = reference !== false;
|
|
2010
|
+
const referenceLabel = (reference || void 0)?.label ?? "API Reference";
|
|
2011
|
+
const go = (loc) => () => onNavigate(loc);
|
|
2012
|
+
const page = location.kind === "page" ? docsPage(bundle, location.page) : void 0;
|
|
2013
|
+
const landingPage = landing ? docsPage(bundle, landing) : void 0;
|
|
2014
|
+
let main;
|
|
2015
|
+
if (page) {
|
|
2016
|
+
main = /* @__PURE__ */ jsx11(GuidePage, { page, pageId: location.kind === "page" ? location.page : void 0, images: bundle?.images });
|
|
2017
|
+
} else if (location.kind === "events" && bundle?.events) {
|
|
2018
|
+
main = /* @__PURE__ */ jsx11(EventsView, { catalog: bundle.events, focus: location.event });
|
|
2019
|
+
} else if (!hasReference) {
|
|
2020
|
+
main = landingPage ? /* @__PURE__ */ jsx11(GuidePage, { page: landingPage, pageId: landing, images: bundle?.images }) : null;
|
|
2021
|
+
} else {
|
|
2022
|
+
main = /* @__PURE__ */ jsx11(
|
|
2023
|
+
ReferenceView,
|
|
2024
|
+
{
|
|
2025
|
+
label: referenceLabel,
|
|
2026
|
+
index: props.index,
|
|
2027
|
+
tags: props.tags,
|
|
2028
|
+
bundle,
|
|
2029
|
+
sandbox,
|
|
2030
|
+
serverUrl: bundle?.config.serverUrl,
|
|
2031
|
+
calledThrough: props.host.calledThrough,
|
|
2032
|
+
selected: location.kind === "reference" ? location.op : void 0,
|
|
2033
|
+
preloadedOp: props.preloadedOp,
|
|
2034
|
+
onSelect: (op) => onNavigate({ kind: "reference", ...op ? { op } : {} }),
|
|
2035
|
+
hrefFor: (op) => hrefFor({ kind: "reference", op }),
|
|
2036
|
+
host: props.host
|
|
2037
|
+
}
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
const resolveRelativeLink = (href) => {
|
|
2041
|
+
const loc = resolveDocsLink(href, bundle);
|
|
2042
|
+
if (!loc) return null;
|
|
2043
|
+
return {
|
|
2044
|
+
href: hrefFor(loc),
|
|
2045
|
+
onClick: (e) => {
|
|
2046
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
|
2047
|
+
e.preventDefault();
|
|
2048
|
+
onNavigate(loc);
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
};
|
|
2052
|
+
return /* @__PURE__ */ jsx11(DocsLinkContext.Provider, { value: resolveRelativeLink, children: /* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 gap-6", "data-docs-source": title, children: [
|
|
2053
|
+
/* @__PURE__ */ jsxs9("aside", { className: "w-56 shrink-0 space-y-4", "aria-label": `${title} documentation`, children: [
|
|
2054
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-1 px-2", children: [
|
|
2055
|
+
/* @__PURE__ */ jsx11("p", { className: "font-semibold leading-tight", children: title }),
|
|
2056
|
+
sandbox !== null && /* @__PURE__ */ jsx11("p", { className: "text-[11px] text-muted-foreground", "data-sandbox": sandbox ?? "live", children: SANDBOX_LABELS[sandbox ?? "live"] })
|
|
2057
|
+
] }),
|
|
2058
|
+
landing && landingPage && /* @__PURE__ */ jsx11(
|
|
2059
|
+
NavLink,
|
|
2060
|
+
{
|
|
2061
|
+
active: location.kind === "page" && location.page === landing,
|
|
2062
|
+
href: hrefFor({ kind: "page", page: landing }),
|
|
2063
|
+
onClick: go({ kind: "page", page: landing }),
|
|
2064
|
+
children: landingPage.title
|
|
2065
|
+
}
|
|
2066
|
+
),
|
|
2067
|
+
groups.map((g) => /* @__PURE__ */ jsxs9("div", { className: "space-y-0.5", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx11("p", { className: "px-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: g.group }),
|
|
2069
|
+
g.pages.map((p) => /* @__PURE__ */ jsx11(
|
|
2070
|
+
NavLink,
|
|
2071
|
+
{
|
|
2072
|
+
active: location.kind === "page" && location.page === p.id,
|
|
2073
|
+
href: hrefFor({ kind: "page", page: p.id }),
|
|
2074
|
+
onClick: go({ kind: "page", page: p.id }),
|
|
2075
|
+
children: p.title
|
|
2076
|
+
},
|
|
2077
|
+
p.id
|
|
2078
|
+
))
|
|
2079
|
+
] }, g.group)),
|
|
2080
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-0.5", children: [
|
|
2081
|
+
hasReference && /* @__PURE__ */ jsx11(
|
|
2082
|
+
NavLink,
|
|
2083
|
+
{
|
|
2084
|
+
active: location.kind === "reference",
|
|
2085
|
+
href: hrefFor({ kind: "reference" }),
|
|
2086
|
+
onClick: go({ kind: "reference" }),
|
|
2087
|
+
children: referenceLabel
|
|
2088
|
+
}
|
|
2089
|
+
),
|
|
2090
|
+
bundle?.events && /* @__PURE__ */ jsx11(NavLink, { active: location.kind === "events", href: hrefFor({ kind: "events" }), onClick: go({ kind: "events" }), children: "Events" })
|
|
2091
|
+
] })
|
|
2092
|
+
] }),
|
|
2093
|
+
/* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: main })
|
|
2094
|
+
] }) });
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// packages/mesh-cli/src/docs/preview-source.ts
|
|
2098
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2099
|
+
import { basename, join, resolve } from "node:path";
|
|
2100
|
+
function resolveDocsConfigPath(target) {
|
|
2101
|
+
const abs = resolve(target);
|
|
2102
|
+
let stat;
|
|
2103
|
+
try {
|
|
2104
|
+
stat = statSync(abs);
|
|
2105
|
+
} catch {
|
|
2106
|
+
throw new Error(`No docs source at ${abs} \u2014 pass the directory that holds docs.yaml, or docs.yaml itself`);
|
|
2107
|
+
}
|
|
2108
|
+
if (stat.isFile()) {
|
|
2109
|
+
if (basename(abs) !== "docs.yaml") throw new Error(`${abs} is not a docs.yaml`);
|
|
2110
|
+
return abs;
|
|
2111
|
+
}
|
|
2112
|
+
const configPath = join(abs, "docs.yaml");
|
|
2113
|
+
try {
|
|
2114
|
+
if (statSync(configPath).isFile()) return configPath;
|
|
2115
|
+
} catch {
|
|
2116
|
+
}
|
|
2117
|
+
throw new Error(`No docs.yaml in ${abs}`);
|
|
2118
|
+
}
|
|
2119
|
+
function missingEventsCatalogProblem(service) {
|
|
2120
|
+
return `docs.yaml names events "${service}", but no events catalog was supplied`;
|
|
2121
|
+
}
|
|
2122
|
+
function readPreviewSource(configPath, opts = {}) {
|
|
2123
|
+
const built = buildDocsSource({ configPath, ...opts.specPath ? { specPath: resolve(opts.specPath) } : {} });
|
|
2124
|
+
const { config } = built.bundle;
|
|
2125
|
+
const notes = [];
|
|
2126
|
+
let problems = built.problems;
|
|
2127
|
+
if (config.events) {
|
|
2128
|
+
const expected = missingEventsCatalogProblem(config.events);
|
|
2129
|
+
if (problems.includes(expected)) {
|
|
2130
|
+
problems = problems.filter((p) => p !== expected);
|
|
2131
|
+
notes.push(
|
|
2132
|
+
`events: the catalog of ${config.events} is built from the app's event definitions at deploy, so the preview has no Events section`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
notes.push("availability: each operation's availability is published by the deploy, so the preview shows none");
|
|
2137
|
+
let specText;
|
|
2138
|
+
try {
|
|
2139
|
+
specText = readFileSync(built.specPath, "utf8");
|
|
2140
|
+
} catch {
|
|
2141
|
+
specText = void 0;
|
|
2142
|
+
}
|
|
2143
|
+
return {
|
|
2144
|
+
configPath: resolve(configPath),
|
|
2145
|
+
specPath: built.specPath,
|
|
2146
|
+
bundle: built.bundle,
|
|
2147
|
+
...specText !== void 0 ? { specText } : {},
|
|
2148
|
+
problems,
|
|
2149
|
+
notes
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// packages/mesh-cli/src/docs/preview.ts
|
|
2154
|
+
globalThis.React ??= React;
|
|
2155
|
+
var PREVIEW_CSS_PATH = "/docs-preview.css";
|
|
2156
|
+
function previewCssFile() {
|
|
2157
|
+
return join2(findPackageRoot(), "assets", "docs-preview.css");
|
|
2158
|
+
}
|
|
2159
|
+
function displayPath(file) {
|
|
2160
|
+
const rel = relative(process.cwd(), file);
|
|
2161
|
+
return rel && !rel.startsWith("..") && !isAbsolute(rel) ? rel : file;
|
|
2162
|
+
}
|
|
2163
|
+
function escapeHtml(text) {
|
|
2164
|
+
return text.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
2165
|
+
}
|
|
2166
|
+
function engineCache() {
|
|
2167
|
+
let last;
|
|
2168
|
+
return (specText) => {
|
|
2169
|
+
if (specText === void 0) return void 0;
|
|
2170
|
+
if (last?.text === specText) return last.engine;
|
|
2171
|
+
let engine;
|
|
2172
|
+
try {
|
|
2173
|
+
engine = buildEngine(JSON.parse(specText));
|
|
2174
|
+
} catch {
|
|
2175
|
+
engine = void 0;
|
|
2176
|
+
}
|
|
2177
|
+
last = { text: specText, engine };
|
|
2178
|
+
return engine;
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
function renderPreviewPage(input) {
|
|
2182
|
+
const { source, engine, search } = input;
|
|
2183
|
+
const { bundle } = source;
|
|
2184
|
+
const title = bundle.config.title;
|
|
2185
|
+
const notices = [];
|
|
2186
|
+
let status = 200;
|
|
2187
|
+
let location = locationFromParams(search, bundle);
|
|
2188
|
+
const askedPage = search.get("page");
|
|
2189
|
+
if (askedPage && !(location.kind === "page" && location.page === askedPage)) {
|
|
2190
|
+
status = 404;
|
|
2191
|
+
notices.push(`This source has no page "${askedPage}".`);
|
|
2192
|
+
}
|
|
2193
|
+
if (search.get("section") === "events" && !bundle.events) {
|
|
2194
|
+
notices.push(
|
|
2195
|
+
bundle.config.events ? `The events of ${bundle.config.events} are documented from the catalog the deploy builds; the preview has none.` : "This source documents no events."
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
let op;
|
|
2199
|
+
if (location.kind === "reference" && location.op) {
|
|
2200
|
+
op = engine?.getOp(location.op) ?? void 0;
|
|
2201
|
+
if (!op) {
|
|
2202
|
+
status = 404;
|
|
2203
|
+
notices.push(`The spec has no operation "${location.op}".`);
|
|
2204
|
+
location = { kind: "reference" };
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
const view = React.createElement(DocsSourceView, {
|
|
2208
|
+
bundle,
|
|
2209
|
+
title,
|
|
2210
|
+
index: engine?.index() ?? [],
|
|
2211
|
+
tags: engine?.tags() ?? [],
|
|
2212
|
+
location,
|
|
2213
|
+
onNavigate: () => {
|
|
2214
|
+
},
|
|
2215
|
+
hrefFor: (loc) => `?${locationToParams(loc).toString()}`,
|
|
2216
|
+
host: {
|
|
2217
|
+
loadOp: (id) => {
|
|
2218
|
+
const slice = engine?.getOp(id);
|
|
2219
|
+
return slice ? Promise.resolve(slice) : Promise.reject(new Error(`no operation ${id}`));
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
...op ? { preloadedOp: op } : {}
|
|
2223
|
+
});
|
|
2224
|
+
const heading = location.kind === "page" ? docsPage(bundle, location.page)?.title ?? title : location.kind === "events" ? "Events" : op ? op.summary ?? op.name : (bundle.config.reference || void 0)?.label ?? "API Reference";
|
|
2225
|
+
return {
|
|
2226
|
+
status,
|
|
2227
|
+
html: previewDocument({
|
|
2228
|
+
title: heading === title ? title : `${heading} \xB7 ${title}`,
|
|
2229
|
+
source,
|
|
2230
|
+
notices,
|
|
2231
|
+
body: renderToString(view)
|
|
2232
|
+
})
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
function previewChrome(source) {
|
|
2236
|
+
const where = displayPath(source.configPath);
|
|
2237
|
+
return `<header class="border-b bg-muted/40">
|
|
2238
|
+
<div class="mx-auto flex max-w-7xl flex-wrap items-center gap-x-4 gap-y-1 px-6 py-3 text-sm">
|
|
2239
|
+
<span class="font-semibold">Docs preview</span>
|
|
2240
|
+
<code class="font-mono text-xs text-muted-foreground">${escapeHtml(where)}</code>
|
|
2241
|
+
<span class="ml-auto text-xs text-muted-foreground" data-preview-try-it>Try-it runs in the Hub \u2014 this preview renders the docs only.</span>
|
|
2242
|
+
</div>
|
|
2243
|
+
</header>`;
|
|
2244
|
+
}
|
|
2245
|
+
function list(items) {
|
|
2246
|
+
return `<ul class="mt-2 list-disc space-y-1 pl-5">${items.map((i) => `<li>${escapeHtml(i)}</li>`).join("")}</ul>`;
|
|
2247
|
+
}
|
|
2248
|
+
function previewDocument(input) {
|
|
2249
|
+
const { source, notices } = input;
|
|
2250
|
+
const problems = source.problems.length ? `<section role="alert" class="mx-auto max-w-7xl px-6 pt-4" data-preview-problems="${source.problems.length}">
|
|
2251
|
+
<div class="rounded-md border border-destructive/50 bg-destructive/5 p-4 text-sm">
|
|
2252
|
+
<p class="font-semibold text-destructive">${source.problems.length} problem${source.problems.length === 1 ? "" : "s"} \u2014 the deploy refuses this source until ${source.problems.length === 1 ? "it is" : "they are"} fixed</p>
|
|
2253
|
+
${list(source.problems)}
|
|
2254
|
+
</div>
|
|
2255
|
+
</section>` : "";
|
|
2256
|
+
const noticeBlock = notices.length ? `<section class="mx-auto max-w-7xl px-6 pt-4" data-preview-notices>
|
|
2257
|
+
<div class="rounded-md border bg-muted/40 p-4 text-sm">${list(notices)}</div>
|
|
2258
|
+
</section>` : "";
|
|
2259
|
+
const notes = source.notes.length ? `<details class="mx-auto max-w-7xl px-6 pt-4 text-xs text-muted-foreground">
|
|
2260
|
+
<summary class="cursor-pointer select-none">What the preview does not show</summary>
|
|
2261
|
+
${list(source.notes)}
|
|
2262
|
+
</details>` : "";
|
|
2263
|
+
return `<!doctype html>
|
|
2264
|
+
<html lang="en">
|
|
2265
|
+
<head>
|
|
2266
|
+
<meta charset="utf-8">
|
|
2267
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2268
|
+
<title>${escapeHtml(input.title)} \u2014 docs preview</title>
|
|
2269
|
+
<link rel="stylesheet" href="${PREVIEW_CSS_PATH}">
|
|
2270
|
+
</head>
|
|
2271
|
+
<body class="bg-background text-foreground antialiased">
|
|
2272
|
+
${previewChrome(source)}
|
|
2273
|
+
${problems}
|
|
2274
|
+
${noticeBlock}
|
|
2275
|
+
${notes}
|
|
2276
|
+
<main class="mx-auto max-w-7xl px-6 py-6">${input.body}</main>
|
|
2277
|
+
</body>
|
|
2278
|
+
</html>
|
|
2279
|
+
`;
|
|
2280
|
+
}
|
|
2281
|
+
function errorDocument(configPath, heading, message) {
|
|
2282
|
+
return `<!doctype html>
|
|
2283
|
+
<html lang="en">
|
|
2284
|
+
<head>
|
|
2285
|
+
<meta charset="utf-8">
|
|
2286
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2287
|
+
<title>${escapeHtml(heading)} \u2014 docs preview</title>
|
|
2288
|
+
<link rel="stylesheet" href="${PREVIEW_CSS_PATH}">
|
|
2289
|
+
</head>
|
|
2290
|
+
<body class="bg-background text-foreground antialiased">
|
|
2291
|
+
${previewChrome({ configPath })}
|
|
2292
|
+
<section role="alert" class="mx-auto max-w-7xl px-6 pt-4">
|
|
2293
|
+
<div class="rounded-md border border-destructive/50 bg-destructive/5 p-4 text-sm">
|
|
2294
|
+
<p class="font-semibold text-destructive">${escapeHtml(heading)}</p>
|
|
2295
|
+
<pre class="mt-2 whitespace-pre-wrap font-mono text-xs">${escapeHtml(message)}</pre>
|
|
2296
|
+
</div>
|
|
2297
|
+
</section>
|
|
2298
|
+
</body>
|
|
2299
|
+
</html>
|
|
2300
|
+
`;
|
|
2301
|
+
}
|
|
2302
|
+
async function startPreviewServer(args) {
|
|
2303
|
+
const engineFor = engineCache();
|
|
2304
|
+
const cssFile = args.cssFile ?? previewCssFile();
|
|
2305
|
+
const message = (error) => error instanceof Error ? error.message : String(error);
|
|
2306
|
+
const pageFor = (search) => {
|
|
2307
|
+
let source;
|
|
2308
|
+
try {
|
|
2309
|
+
source = readPreviewSource(args.configPath, { specPath: args.specPath });
|
|
2310
|
+
} catch (error) {
|
|
2311
|
+
return { status: 500, html: errorDocument(args.configPath, "The source cannot be read", message(error)) };
|
|
2312
|
+
}
|
|
2313
|
+
try {
|
|
2314
|
+
return renderPreviewPage({ source, engine: engineFor(source.specText), search });
|
|
2315
|
+
} catch (error) {
|
|
2316
|
+
return { status: 500, html: errorDocument(args.configPath, "The page cannot be rendered", message(error)) };
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
const handle = (req, res) => {
|
|
2320
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
2321
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
2322
|
+
res.writeHead(405, { allow: "GET, HEAD", "content-type": "text/plain; charset=utf-8" }).end("Method not allowed\n");
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
if (url.pathname === PREVIEW_CSS_PATH) {
|
|
2326
|
+
let css;
|
|
2327
|
+
try {
|
|
2328
|
+
css = readFileSync2(cssFile, "utf8");
|
|
2329
|
+
} catch {
|
|
2330
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" }).end("Stylesheet not found\n");
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
res.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-cache" }).end(css);
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (url.pathname !== "/") {
|
|
2337
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" }).end("Not found \u2014 the preview is served at /\n");
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
const page = pageFor(url.searchParams);
|
|
2341
|
+
res.writeHead(page.status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
|
|
2342
|
+
res.end(req.method === "HEAD" ? void 0 : page.html);
|
|
2343
|
+
};
|
|
2344
|
+
const server = createServer(handle);
|
|
2345
|
+
await new Promise((resolvePromise, reject) => {
|
|
2346
|
+
server.once("error", (error) => {
|
|
2347
|
+
reject(
|
|
2348
|
+
error.code === "EADDRINUSE" ? new Error(`Port ${args.port} is already in use \u2014 pass --port <n> for another one (0 picks a free port)`) : error
|
|
2349
|
+
);
|
|
2350
|
+
});
|
|
2351
|
+
server.listen(args.port, "127.0.0.1", () => resolvePromise());
|
|
2352
|
+
});
|
|
2353
|
+
const address = server.address();
|
|
2354
|
+
const port = typeof address === "object" && address ? address.port : args.port;
|
|
2355
|
+
return {
|
|
2356
|
+
url: `http://localhost:${port}`,
|
|
2357
|
+
port,
|
|
2358
|
+
close: () => new Promise((resolvePromise) => server.close(() => resolvePromise()))
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
function reportProblems(source) {
|
|
2362
|
+
if (source.problems.length === 0) {
|
|
2363
|
+
logSuccess(`${source.bundle.config.title}: no problems`);
|
|
2364
|
+
} else {
|
|
2365
|
+
logWarn(
|
|
2366
|
+
`${source.bundle.config.title}: ${source.problems.length} problem${source.problems.length === 1 ? "" : "s"} \u2014 the deploy refuses this source until ${source.problems.length === 1 ? "it is" : "they are"} fixed:`
|
|
2367
|
+
);
|
|
2368
|
+
for (const problem of source.problems) console.error(` - ${problem}`);
|
|
2369
|
+
}
|
|
2370
|
+
for (const note of source.notes) logInfo(`note \u2014 ${note}`);
|
|
2371
|
+
}
|
|
2372
|
+
async function runDocsPreview(opts) {
|
|
2373
|
+
const configPath = resolveDocsConfigPath(opts.target);
|
|
2374
|
+
if (opts.check || opts.out) {
|
|
2375
|
+
const source = readPreviewSource(configPath, { specPath: opts.spec });
|
|
2376
|
+
reportProblems(source);
|
|
2377
|
+
if (opts.out) {
|
|
2378
|
+
const outPath = resolve2(opts.out);
|
|
2379
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
2380
|
+
writeFileSync(outPath, `${JSON.stringify(source.bundle, null, 2)}
|
|
2381
|
+
`);
|
|
2382
|
+
logSuccess(`Docs source bundle \u2192 ${displayPath(outPath)}`);
|
|
2383
|
+
}
|
|
2384
|
+
return !opts.check || source.problems.length === 0;
|
|
2385
|
+
}
|
|
2386
|
+
const port = Number(opts.port);
|
|
2387
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
2388
|
+
throw new Error(`--port must be a port number (0 picks a free one), not "${opts.port}"`);
|
|
2389
|
+
}
|
|
2390
|
+
try {
|
|
2391
|
+
reportProblems(readPreviewSource(configPath, { specPath: opts.spec }));
|
|
2392
|
+
} catch (error) {
|
|
2393
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
2394
|
+
}
|
|
2395
|
+
const server = await startPreviewServer({ configPath, ...opts.spec ? { specPath: opts.spec } : {}, port });
|
|
2396
|
+
logSuccess(`Docs preview \u2192 ${server.url}/ (re-reads ${displayPath(configPath)} on every request; Ctrl-C to stop)`);
|
|
2397
|
+
await new Promise((resolvePromise) => {
|
|
2398
|
+
const stop = () => void server.close().then(resolvePromise);
|
|
2399
|
+
process.once("SIGINT", stop);
|
|
2400
|
+
process.once("SIGTERM", stop);
|
|
2401
|
+
});
|
|
2402
|
+
return true;
|
|
2403
|
+
}
|
|
2404
|
+
export {
|
|
2405
|
+
PREVIEW_CSS_PATH,
|
|
2406
|
+
previewCssFile,
|
|
2407
|
+
renderPreviewPage,
|
|
2408
|
+
runDocsPreview,
|
|
2409
|
+
startPreviewServer
|
|
2410
|
+
};
|