radiant-docs 0.1.64 → 0.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/index.js +53 -10
  2. package/package.json +4 -3
  3. package/template/package-lock.json +1991 -4096
  4. package/template/package.json +7 -19
  5. package/template/src/components/OpenApiPage.astro +107 -18
  6. package/template/src/components/Sidebar.astro +1 -0
  7. package/template/src/components/endpoint/PlaygroundBar.astro +1 -1
  8. package/template/src/components/endpoint/PlaygroundField.astro +433 -71
  9. package/template/src/components/endpoint/PlaygroundForm.astro +470 -87
  10. package/template/src/components/endpoint/RequestSnippets.astro +6 -1
  11. package/template/src/components/endpoint/ResponseDisplay.astro +109 -2
  12. package/template/src/components/endpoint/ResponseFieldTree.astro +1 -1
  13. package/template/src/components/endpoint/ResponseFields.astro +97 -28
  14. package/template/src/components/endpoint/ResponseSnippets.astro +19 -7
  15. package/template/src/layouts/Layout.astro +94 -0
  16. package/template/src/lib/openapi/operation-doc.ts +308 -70
  17. package/template/src/lib/openapi/response-content.ts +133 -0
  18. package/template/src/lib/openapi/schema-variant-labels.ts +213 -0
  19. package/template/.vscode/extensions.json +0 -4
  20. package/template/.vscode/launch.json +0 -11
  21. package/template/scripts/generate-og-images.mjs +0 -667
  22. package/template/scripts/generate-og-metadata.mjs +0 -206
  23. package/template/scripts/generate-proxy-allowed-origins.mjs +0 -48
  24. package/template/scripts/generate-robots-txt.mjs +0 -47
  25. package/template/scripts/publish-shiki-platform-assets.mjs +0 -1177
  26. package/template/scripts/remove-assistant-for-non-pro.mjs +0 -28
  27. package/template/scripts/stamp-image-versions.mjs +0 -355
  28. package/template/scripts/stamp-og-image-versions.mjs +0 -199
  29. package/template/scripts/stamp-pagefind-runtime-version.mjs +0 -140
@@ -1,28 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = path.dirname(__filename);
7
- const FRAMEWORK_DIR = path.resolve(__dirname, "..");
8
- const ASSISTANT_DIST_DIR = path.join(FRAMEWORK_DIR, "dist", "-", "assistant");
9
-
10
- function getOrgTier() {
11
- const parsedTier = Number.parseInt((process.env.ORG_TIER ?? "1").trim(), 10);
12
- return Number.isFinite(parsedTier) && parsedTier > 0 ? parsedTier : 1;
13
- }
14
-
15
- const orgTier = getOrgTier();
16
-
17
- if (orgTier >= 3) {
18
- console.log("Assistant embed retained for Pro tier build.");
19
- process.exit(0);
20
- }
21
-
22
- if (!fs.existsSync(ASSISTANT_DIST_DIR)) {
23
- console.log("Assistant embed cleanup skipped: assistant output not found.");
24
- process.exit(0);
25
- }
26
-
27
- fs.rmSync(ASSISTANT_DIST_DIR, { recursive: true, force: true });
28
- console.log("Assistant embed removed for non-Pro tier build.");
@@ -1,355 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
-
5
- const CWD = process.cwd();
6
- const DIST_DIR = path.join(CWD, "dist");
7
- const VERSION_LENGTH = 12;
8
- const DIST_ROOT = path.resolve(DIST_DIR);
9
- const CONFIGURED_STATIC_HOST = (() => {
10
- const raw =
11
- typeof process.env.STATIC_ASSET_HOST === "string"
12
- ? process.env.STATIC_ASSET_HOST.trim()
13
- : "";
14
- if (!raw) return null;
15
- const withScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(raw)
16
- ? raw
17
- : `https://${raw}`;
18
-
19
- try {
20
- const parsed = new URL(withScheme);
21
- return parsed.hostname.toLowerCase();
22
- } catch {
23
- return null;
24
- }
25
- })();
26
- const CONFIGURED_PREFIX =
27
- typeof process.env.R2_BUCKET_PREFIX === "string" &&
28
- process.env.R2_BUCKET_PREFIX.trim().length > 0
29
- ? process.env.R2_BUCKET_PREFIX.trim().replace(/^\/+|\/+$/g, "")
30
- : null;
31
- const CONFIGURED_BASE_PATH = (() => {
32
- const raw =
33
- typeof process.env.DOCS_SITE_URL === "string"
34
- ? process.env.DOCS_SITE_URL.trim()
35
- : "";
36
- if (!raw) return null;
37
-
38
- try {
39
- const parsed = new URL(raw);
40
- const normalized = parsed.pathname
41
- .replace(/\/{2,}/g, "/")
42
- .replace(/\/+$/, "");
43
- return normalized && normalized !== "/" ? normalized : null;
44
- } catch {
45
- return null;
46
- }
47
- })();
48
- const VERSIONED_EXTENSIONS = new Set([
49
- ".avif",
50
- ".gif",
51
- ".ico",
52
- ".jpg",
53
- ".jpeg",
54
- ".m4a",
55
- ".mp3",
56
- ".mp4",
57
- ".oga",
58
- ".ogg",
59
- ".ogv",
60
- ".opus",
61
- ".png",
62
- ".svg",
63
- ".wav",
64
- ".webm",
65
- ".webp",
66
- ]);
67
-
68
- const hashCache = new Map();
69
-
70
- function findHtmlFiles(dir, files = []) {
71
- if (!fs.existsSync(dir)) return files;
72
-
73
- const entries = fs.readdirSync(dir, { withFileTypes: true });
74
- for (const entry of entries) {
75
- const fullPath = path.join(dir, entry.name);
76
-
77
- if (entry.isDirectory()) {
78
- findHtmlFiles(fullPath, files);
79
- continue;
80
- }
81
-
82
- if (entry.isFile() && entry.name.endsWith(".html")) {
83
- files.push(fullPath);
84
- }
85
- }
86
-
87
- return files;
88
- }
89
-
90
- function htmlBasePathname(filePath) {
91
- const relative = path.relative(DIST_DIR, filePath).replace(/\\/g, "/");
92
- if (relative === "index.html") return "/";
93
-
94
- if (relative.endsWith("/index.html")) {
95
- return `/${relative.slice(0, -"index.html".length)}`;
96
- }
97
-
98
- const slashIndex = relative.lastIndexOf("/");
99
- if (slashIndex === -1) return "/";
100
- return `/${relative.slice(0, slashIndex + 1)}`;
101
- }
102
-
103
- function getHash(filePath) {
104
- const cached = hashCache.get(filePath);
105
- if (cached) return cached;
106
-
107
- const buffer = fs.readFileSync(filePath);
108
- const hash = crypto
109
- .createHash("sha256")
110
- .update(buffer)
111
- .digest("hex")
112
- .slice(0, VERSION_LENGTH);
113
-
114
- hashCache.set(filePath, hash);
115
- return hash;
116
- }
117
-
118
- function normalizePathForDistLookup(pathname) {
119
- const normalizedPathname = path.posix.normalize(pathname);
120
-
121
- const withoutStaticPrefix = stripPathPrefix(
122
- normalizedPathname,
123
- CONFIGURED_PREFIX ? `/${CONFIGURED_PREFIX}` : null,
124
- );
125
- return stripPathPrefix(withoutStaticPrefix, CONFIGURED_BASE_PATH);
126
- }
127
-
128
- function stripPathPrefix(pathname, prefix) {
129
- if (!prefix) {
130
- return pathname;
131
- }
132
-
133
- if (pathname === prefix || pathname.startsWith(`${prefix}/`)) {
134
- const stripped = pathname.slice(prefix.length);
135
- return stripped.startsWith("/") ? stripped : `/${stripped}`;
136
- }
137
-
138
- return pathname;
139
- }
140
-
141
- function resolveLocalImagePath(urlValue, filePath) {
142
- const trimmed = urlValue.trim();
143
- if (!trimmed) return null;
144
-
145
- const decoded = trimmed.replace(/&/g, "&");
146
- if (
147
- decoded.startsWith("data:") ||
148
- decoded.startsWith("blob:") ||
149
- decoded.startsWith("mailto:") ||
150
- decoded.startsWith("tel:") ||
151
- decoded.startsWith("//")
152
- ) {
153
- return null;
154
- }
155
-
156
- let parsed;
157
- try {
158
- parsed = new URL(
159
- decoded,
160
- `https://radiant.invalid${htmlBasePathname(filePath)}`,
161
- );
162
- } catch {
163
- return null;
164
- }
165
-
166
- const isAbsoluteUrl = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(decoded);
167
- if (isAbsoluteUrl) {
168
- if (!CONFIGURED_STATIC_HOST) {
169
- return null;
170
- }
171
-
172
- if (parsed.protocol !== "https:") {
173
- return null;
174
- }
175
-
176
- if (parsed.hostname.toLowerCase() !== CONFIGURED_STATIC_HOST) {
177
- return null;
178
- }
179
- }
180
-
181
- const extension = path.extname(parsed.pathname).toLowerCase();
182
- if (!VERSIONED_EXTENSIONS.has(extension)) {
183
- return null;
184
- }
185
-
186
- const normalizedPathname = normalizePathForDistLookup(
187
- parsed.pathname,
188
- ).replace(/^\/+/, "");
189
- const absolutePath = path.resolve(DIST_DIR, normalizedPathname);
190
-
191
- if (
192
- absolutePath !== DIST_ROOT &&
193
- !absolutePath.startsWith(`${DIST_ROOT}${path.sep}`)
194
- ) {
195
- return null;
196
- }
197
-
198
- if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
199
- return null;
200
- }
201
-
202
- return { parsed, absolutePath, isAbsoluteUrl };
203
- }
204
-
205
- function formatUpdatedUrl(parsed, isAbsoluteUrl) {
206
- const pathnameWithParams = `${parsed.pathname}${parsed.search}${parsed.hash}`;
207
- const fullValue = isAbsoluteUrl
208
- ? `${parsed.origin}${pathnameWithParams}`
209
- : pathnameWithParams;
210
- return fullValue.replace(/&/g, "&");
211
- }
212
-
213
- function stampSingleUrl(urlValue, filePath) {
214
- const resolved = resolveLocalImagePath(urlValue, filePath);
215
- if (!resolved) {
216
- return { value: urlValue, changed: false };
217
- }
218
-
219
- const version = getHash(resolved.absolutePath);
220
- resolved.parsed.searchParams.set("v", version);
221
-
222
- const updated = formatUpdatedUrl(resolved.parsed, resolved.isAbsoluteUrl);
223
- return { value: updated, changed: updated !== urlValue };
224
- }
225
-
226
- function stampSrcset(srcsetValue, filePath) {
227
- const candidates = srcsetValue.split(",");
228
- let changed = false;
229
-
230
- const updated = candidates.map((candidate) => {
231
- const trimmed = candidate.trim();
232
- if (!trimmed) return candidate;
233
-
234
- const whitespaceIndex = trimmed.search(/\s/);
235
- const urlPart =
236
- whitespaceIndex === -1 ? trimmed : trimmed.slice(0, whitespaceIndex);
237
- const descriptor =
238
- whitespaceIndex === -1 ? "" : trimmed.slice(whitespaceIndex);
239
-
240
- const stamped = stampSingleUrl(urlPart, filePath);
241
- if (stamped.changed) changed = true;
242
-
243
- return `${stamped.value}${descriptor}`;
244
- });
245
-
246
- return {
247
- value: changed ? updated.join(", ") : srcsetValue,
248
- changed,
249
- };
250
- }
251
-
252
- function replaceAttribute(html, filePath, attribute) {
253
- let tagAlternation = "img|source";
254
- if (attribute === "src") {
255
- tagAlternation = "img|source|video|audio|track";
256
- } else if (attribute === "poster") {
257
- tagAlternation = "video";
258
- }
259
-
260
- const pattern = new RegExp(
261
- `(<(?:${tagAlternation})\\b[^>]*\\b${attribute}\\s*=\\s*["'])([^"']*)(["'][^>]*>)`,
262
- "gi",
263
- );
264
-
265
- let changed = false;
266
-
267
- const nextHtml = html.replace(pattern, (fullMatch, prefix, value, suffix) => {
268
- const stamped =
269
- attribute === "srcset"
270
- ? stampSrcset(value, filePath)
271
- : stampSingleUrl(value, filePath);
272
-
273
- if (!stamped.changed) return fullMatch;
274
-
275
- changed = true;
276
- return `${prefix}${stamped.value}${suffix}`;
277
- });
278
-
279
- return { html: nextHtml, changed };
280
- }
281
-
282
- function hasIconRel(tagSource) {
283
- const relMatch = tagSource.match(/\brel\s*=\s*["']([^"']+)["']/i);
284
- if (!relMatch) return false;
285
-
286
- const relTokens = relMatch[1].toLowerCase().split(/\s+/).filter(Boolean);
287
-
288
- return relTokens.includes("icon") || relTokens.includes("apple-touch-icon");
289
- }
290
-
291
- function replaceIconLinkHref(html, filePath) {
292
- const pattern = /(<link\b[^>]*\bhref\s*=\s*["'])([^"']*)(["'][^>]*>)/gi;
293
- let changed = false;
294
-
295
- const nextHtml = html.replace(pattern, (fullMatch, prefix, value, suffix) => {
296
- if (!hasIconRel(fullMatch)) {
297
- return fullMatch;
298
- }
299
-
300
- const stamped = stampSingleUrl(value, filePath);
301
- if (!stamped.changed) {
302
- return fullMatch;
303
- }
304
-
305
- changed = true;
306
- return `${prefix}${stamped.value}${suffix}`;
307
- });
308
-
309
- return { html: nextHtml, changed };
310
- }
311
-
312
- function main() {
313
- if (!fs.existsSync(DIST_DIR)) {
314
- console.warn("Skipping image version stamping: dist directory not found.");
315
- return;
316
- }
317
-
318
- const htmlFiles = findHtmlFiles(DIST_DIR).sort();
319
- if (htmlFiles.length === 0) {
320
- console.warn(
321
- "Skipping image version stamping: no HTML files found in dist.",
322
- );
323
- return;
324
- }
325
-
326
- let updatedCount = 0;
327
-
328
- for (const htmlFile of htmlFiles) {
329
- const sourceHtml = fs.readFileSync(htmlFile, "utf8");
330
- const srcResult = replaceAttribute(sourceHtml, htmlFile, "src");
331
- const srcSetResult = replaceAttribute(srcResult.html, htmlFile, "srcset");
332
- const posterResult = replaceAttribute(
333
- srcSetResult.html,
334
- htmlFile,
335
- "poster",
336
- );
337
- const iconHrefResult = replaceIconLinkHref(posterResult.html, htmlFile);
338
- const fileChanged =
339
- srcResult.changed ||
340
- srcSetResult.changed ||
341
- posterResult.changed ||
342
- iconHrefResult.changed;
343
-
344
- if (fileChanged) {
345
- fs.writeFileSync(htmlFile, iconHrefResult.html, "utf8");
346
- updatedCount += 1;
347
- }
348
- }
349
-
350
- console.log(
351
- `✅ Image version stamping complete. Updated ${updatedCount}/${htmlFiles.length} HTML files.`,
352
- );
353
- }
354
-
355
- main();
@@ -1,199 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
-
5
- const CWD = process.cwd();
6
- const DIST_DIR = path.join(CWD, "dist");
7
- const OG_IMAGES_DIR = path.join(DIST_DIR, "_og/images");
8
- const VERSION_LENGTH = 12;
9
-
10
- function findHtmlFiles(dir, files = []) {
11
- if (!fs.existsSync(dir)) return files;
12
-
13
- const entries = fs.readdirSync(dir, { withFileTypes: true });
14
- for (const entry of entries) {
15
- const fullPath = path.join(dir, entry.name);
16
-
17
- if (entry.isDirectory()) {
18
- findHtmlFiles(fullPath, files);
19
- continue;
20
- }
21
-
22
- if (entry.isFile() && entry.name.endsWith(".html")) {
23
- files.push(fullPath);
24
- }
25
- }
26
-
27
- return files;
28
- }
29
-
30
- function normalizeRoutePath(routePath) {
31
- if (!routePath || routePath === "/") return "/";
32
- if (!routePath.startsWith("/")) routePath = `/${routePath}`;
33
- return routePath.endsWith("/") ? routePath : `${routePath}/`;
34
- }
35
-
36
- function routePathFromHtmlPath(filePath) {
37
- const relative = path.relative(DIST_DIR, filePath).replace(/\\/g, "/");
38
-
39
- if (relative === "index.html") return "/";
40
- if (relative.endsWith("/index.html")) {
41
- const base = relative.slice(0, -"/index.html".length);
42
- return normalizeRoutePath(base);
43
- }
44
-
45
- if (relative.endsWith(".html")) {
46
- return normalizeRoutePath(relative.slice(0, -".html".length));
47
- }
48
-
49
- return "/";
50
- }
51
-
52
- function routePathToOgImageRelativePath(routePath) {
53
- const normalizedRoutePath = normalizeRoutePath(routePath);
54
- if (normalizedRoutePath === "/") return "index.png";
55
- return `${normalizedRoutePath.slice(1, -1)}.png`;
56
- }
57
-
58
- function hashFile(filePath) {
59
- const buffer = fs.readFileSync(filePath);
60
- return crypto
61
- .createHash("sha256")
62
- .update(buffer)
63
- .digest("hex")
64
- .slice(0, VERSION_LENGTH);
65
- }
66
-
67
- function updateVersionInUrl(urlValue, version) {
68
- if (typeof urlValue !== "string" || urlValue.trim().length === 0) {
69
- return urlValue;
70
- }
71
-
72
- const decoded = urlValue.trim().replace(/&amp;/g, "&");
73
- const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(decoded);
74
- const isProtocolRelative = decoded.startsWith("//");
75
- const isAbsolutePath = decoded.startsWith("/");
76
-
77
- let parsed;
78
- try {
79
- if (hasScheme) {
80
- parsed = new URL(decoded);
81
- } else if (isProtocolRelative) {
82
- parsed = new URL(`https:${decoded}`);
83
- } else {
84
- parsed = new URL(decoded, "https://radiant.invalid/");
85
- }
86
- } catch {
87
- return urlValue;
88
- }
89
-
90
- parsed.searchParams.set("v", version);
91
-
92
- let encoded;
93
- if (hasScheme) {
94
- encoded = parsed.toString();
95
- } else if (isProtocolRelative) {
96
- encoded = `//${parsed.host}${parsed.pathname}${parsed.search}${parsed.hash}`;
97
- } else if (isAbsolutePath) {
98
- encoded = `${parsed.pathname}${parsed.search}${parsed.hash}`;
99
- } else {
100
- encoded = `${parsed.pathname.replace(/^\//, "")}${parsed.search}${parsed.hash}`;
101
- }
102
-
103
- return encoded.replace(/&/g, "&amp;");
104
- }
105
-
106
- function replaceMetaImageUrl(html, attrName, attrValue, version) {
107
- const patterns = [
108
- new RegExp(
109
- `(<meta\\s+[^>]*${attrName}\\s*=\\s*["']${attrValue}["'][^>]*\\bcontent\\s*=\\s*["'])([^"']*)(["'][^>]*>)`,
110
- "gi",
111
- ),
112
- new RegExp(
113
- `(<meta\\s+[^>]*\\bcontent\\s*=\\s*["'])([^"']*)(["'][^>]*${attrName}\\s*=\\s*["']${attrValue}["'][^>]*>)`,
114
- "gi",
115
- ),
116
- ];
117
-
118
- let found = false;
119
- let changed = false;
120
- let nextHtml = html;
121
-
122
- for (const pattern of patterns) {
123
- nextHtml = nextHtml.replace(pattern, (_, prefix, currentUrl, suffix) => {
124
- found = true;
125
- const updatedUrl = updateVersionInUrl(currentUrl, version);
126
- if (updatedUrl !== currentUrl) {
127
- changed = true;
128
- }
129
- return `${prefix}${updatedUrl}${suffix}`;
130
- });
131
- }
132
-
133
- return { html: nextHtml, found, changed };
134
- }
135
-
136
- function main() {
137
- if (!fs.existsSync(DIST_DIR)) {
138
- console.warn("Skipping OG version stamping: dist directory not found.");
139
- return;
140
- }
141
-
142
- if (!fs.existsSync(OG_IMAGES_DIR)) {
143
- console.warn("Skipping OG version stamping: OG images directory not found.");
144
- return;
145
- }
146
-
147
- const htmlFiles = findHtmlFiles(DIST_DIR).sort();
148
- if (htmlFiles.length === 0) {
149
- console.warn("Skipping OG version stamping: no HTML files found in dist.");
150
- return;
151
- }
152
-
153
- let updatedCount = 0;
154
- let missingOgCount = 0;
155
- let missingMetaCount = 0;
156
-
157
- for (const htmlPath of htmlFiles) {
158
- const routePath = routePathFromHtmlPath(htmlPath);
159
- const ogImageRelativePath = routePathToOgImageRelativePath(routePath);
160
- const ogImagePath = path.join(OG_IMAGES_DIR, ...ogImageRelativePath.split("/"));
161
-
162
- if (!fs.existsSync(ogImagePath)) {
163
- missingOgCount += 1;
164
- continue;
165
- }
166
-
167
- const version = hashFile(ogImagePath);
168
- const sourceHtml = fs.readFileSync(htmlPath, "utf8");
169
-
170
- const ogResult = replaceMetaImageUrl(
171
- sourceHtml,
172
- "property",
173
- "og:image",
174
- version,
175
- );
176
- const twitterResult = replaceMetaImageUrl(
177
- ogResult.html,
178
- "name",
179
- "twitter:image",
180
- version,
181
- );
182
-
183
- if (!ogResult.found && !twitterResult.found) {
184
- missingMetaCount += 1;
185
- continue;
186
- }
187
-
188
- if (twitterResult.html !== sourceHtml) {
189
- fs.writeFileSync(htmlPath, twitterResult.html, "utf8");
190
- updatedCount += 1;
191
- }
192
- }
193
-
194
- console.log(
195
- `✅ OG version stamping complete. Updated ${updatedCount}/${htmlFiles.length} HTML files (missing OG image: ${missingOgCount}, missing meta tags: ${missingMetaCount}).`,
196
- );
197
- }
198
-
199
- main();