@emeryld/rrroutes-contract 2.1.11 → 2.1.13
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/dist/core/routesV3.core.d.ts +9 -9
- package/dist/docs/docs.d.ts +5 -1
- package/dist/docs/schemaIntrospection.d.ts +15 -0
- package/dist/docs/serializer.d.ts +6 -1
- package/dist/index.cjs +415 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +405 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z,
|
|
1
|
+
import { z, ZodType } from 'zod';
|
|
2
2
|
/** Supported HTTP verbs for the routes DSL. */
|
|
3
3
|
export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
4
4
|
/** Declarative description of a multipart upload field. */
|
|
@@ -16,13 +16,13 @@ export type NodeCfg = {
|
|
|
16
16
|
/** Per-method configuration merged with inherited node config. */
|
|
17
17
|
export type MethodCfg = {
|
|
18
18
|
/** Zod schema describing the request body. */
|
|
19
|
-
bodySchema?:
|
|
19
|
+
bodySchema?: ZodType;
|
|
20
20
|
/** Zod schema describing the query string. */
|
|
21
|
-
querySchema?:
|
|
21
|
+
querySchema?: ZodType;
|
|
22
22
|
/** Zod schema describing path params (overrides inferred params). */
|
|
23
|
-
paramsSchema?:
|
|
23
|
+
paramsSchema?: ZodType;
|
|
24
24
|
/** Zod schema describing the response payload. */
|
|
25
|
-
outputSchema?:
|
|
25
|
+
outputSchema?: ZodType;
|
|
26
26
|
/** Multipart upload definitions for the route. */
|
|
27
27
|
bodyFiles?: FileField[];
|
|
28
28
|
/** Marks the route as an infinite feed (enables cursor helpers). */
|
|
@@ -102,13 +102,13 @@ export declare function buildCacheKey<L extends AnyLeaf>(args: {
|
|
|
102
102
|
query?: InferQuery<L>;
|
|
103
103
|
}): readonly [HttpMethod, string, {}];
|
|
104
104
|
/** Infer params either from the explicit params schema or from the path literal. */
|
|
105
|
-
export type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends
|
|
105
|
+
export type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends ZodType ? z.infer<L['cfg']['paramsSchema']> : ExtractParamsFromPath<L['path']>;
|
|
106
106
|
/** Infer query shape from a Zod schema when present. */
|
|
107
|
-
export type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends
|
|
107
|
+
export type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends ZodType ? z.infer<L['cfg']['querySchema']> : never;
|
|
108
108
|
/** Infer request body shape from a Zod schema when present. */
|
|
109
|
-
export type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends
|
|
109
|
+
export type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends ZodType ? z.infer<L['cfg']['bodySchema']> : never;
|
|
110
110
|
/** Infer handler output shape from a Zod schema. Defaults to unknown. */
|
|
111
|
-
export type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends
|
|
111
|
+
export type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends ZodType ? z.infer<L['cfg']['outputSchema']> : unknown;
|
|
112
112
|
/** Render a type as if it were a simple object literal. */
|
|
113
113
|
export type Prettify<T> = {
|
|
114
114
|
[K in keyof T]: T[K];
|
package/dist/docs/docs.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { AnyLeaf } from "../core/routesV3.core";
|
|
2
|
-
|
|
2
|
+
interface RenderOptions {
|
|
3
|
+
cspNonce?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function renderLeafDocsHTML(leaves: AnyLeaf[], options?: RenderOptions): string;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
export type SerializableSchemaNode = {
|
|
3
|
+
kind: string;
|
|
4
|
+
optional?: boolean;
|
|
5
|
+
nullable?: boolean;
|
|
6
|
+
description?: string;
|
|
7
|
+
properties?: Record<string, SerializableSchemaNode>;
|
|
8
|
+
element?: SerializableSchemaNode;
|
|
9
|
+
union?: SerializableSchemaNode[];
|
|
10
|
+
literal?: unknown;
|
|
11
|
+
enumValues?: string[];
|
|
12
|
+
};
|
|
13
|
+
type ZodAny = z.ZodTypeAny;
|
|
14
|
+
export declare function introspectSchema(schema: ZodAny | undefined): SerializableSchemaNode | undefined;
|
|
15
|
+
export {};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { AnyLeaf, MethodCfg } from "../core/routesV3.core";
|
|
2
|
-
|
|
2
|
+
import { SerializableSchemaNode } from "./schemaIntrospection";
|
|
3
|
+
type SerializableMethodCfg = Pick<MethodCfg, "description" | "summary" | "docsGroup" | "tags" | "deprecated" | "stability" | "feed" | "docsMeta"> & {
|
|
3
4
|
hasBody: boolean;
|
|
4
5
|
hasQuery: boolean;
|
|
5
6
|
hasParams: boolean;
|
|
6
7
|
hasOutput: boolean;
|
|
8
|
+
bodySchema?: SerializableSchemaNode;
|
|
9
|
+
querySchema?: SerializableSchemaNode;
|
|
10
|
+
paramsSchema?: SerializableSchemaNode;
|
|
11
|
+
outputSchema?: SerializableSchemaNode;
|
|
7
12
|
};
|
|
8
13
|
export type SerializableLeaf = {
|
|
9
14
|
method: string;
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -299,6 +309,130 @@ function defineSocketEvents(config, events) {
|
|
|
299
309
|
return { config, events };
|
|
300
310
|
}
|
|
301
311
|
|
|
312
|
+
// src/docs/schemaIntrospection.ts
|
|
313
|
+
var z3 = __toESM(require("zod"), 1);
|
|
314
|
+
function getDef(schema) {
|
|
315
|
+
if (!schema || typeof schema !== "object") return void 0;
|
|
316
|
+
const anySchema = schema;
|
|
317
|
+
return anySchema._zod?.def ?? anySchema._def;
|
|
318
|
+
}
|
|
319
|
+
function getDescription(schema) {
|
|
320
|
+
const anyZ = z3;
|
|
321
|
+
const registry = anyZ.globalRegistry?.get ? anyZ.globalRegistry.get(schema) : void 0;
|
|
322
|
+
if (registry && typeof registry.description === "string") {
|
|
323
|
+
return registry.description;
|
|
324
|
+
}
|
|
325
|
+
const def = getDef(schema);
|
|
326
|
+
if (def && typeof def.description === "string") {
|
|
327
|
+
return def.description;
|
|
328
|
+
}
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
331
|
+
function unwrap(schema) {
|
|
332
|
+
let s = schema;
|
|
333
|
+
let optional = false;
|
|
334
|
+
let nullable = false;
|
|
335
|
+
const ZodEffectsCtor = z3.ZodEffects;
|
|
336
|
+
while (true) {
|
|
337
|
+
if (ZodEffectsCtor && s instanceof ZodEffectsCtor) {
|
|
338
|
+
const def = getDef(s) || {};
|
|
339
|
+
const sourceType = typeof s.sourceType === "function" ? s.sourceType() : def.schema;
|
|
340
|
+
if (!sourceType) break;
|
|
341
|
+
s = sourceType;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (s instanceof z3.ZodOptional) {
|
|
345
|
+
optional = true;
|
|
346
|
+
const def = getDef(s);
|
|
347
|
+
s = def && def.innerType || s;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (s instanceof z3.ZodNullable) {
|
|
351
|
+
nullable = true;
|
|
352
|
+
const def = getDef(s);
|
|
353
|
+
s = def && def.innerType || s;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (s instanceof z3.ZodDefault) {
|
|
357
|
+
const def = getDef(s);
|
|
358
|
+
s = def && def.innerType || s;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
return { base: s, optional, nullable };
|
|
364
|
+
}
|
|
365
|
+
function introspectSchema(schema) {
|
|
366
|
+
if (!schema) return void 0;
|
|
367
|
+
const { base, optional, nullable } = unwrap(schema);
|
|
368
|
+
const def = getDef(base);
|
|
369
|
+
const node = {
|
|
370
|
+
kind: inferKind(base),
|
|
371
|
+
optional: optional || void 0,
|
|
372
|
+
nullable: nullable || void 0,
|
|
373
|
+
description: getDescription(base)
|
|
374
|
+
};
|
|
375
|
+
if (base instanceof z3.ZodObject) {
|
|
376
|
+
const rawShape = base.shape ?? (def && typeof def.shape === "function" ? def.shape() : def?.shape);
|
|
377
|
+
const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
|
|
378
|
+
const props = {};
|
|
379
|
+
for (const key of Object.keys(shape)) {
|
|
380
|
+
const child = shape[key];
|
|
381
|
+
const childNode = introspectSchema(child);
|
|
382
|
+
if (childNode) props[key] = childNode;
|
|
383
|
+
}
|
|
384
|
+
node.properties = props;
|
|
385
|
+
}
|
|
386
|
+
if (base instanceof z3.ZodArray) {
|
|
387
|
+
const inner = def && def.element || def && def.type || void 0;
|
|
388
|
+
if (inner) {
|
|
389
|
+
node.element = introspectSchema(inner);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (base instanceof z3.ZodUnion) {
|
|
393
|
+
const options = def && def.options || [];
|
|
394
|
+
node.union = options.map((opt) => introspectSchema(opt)).filter(Boolean);
|
|
395
|
+
}
|
|
396
|
+
if (base instanceof z3.ZodLiteral) {
|
|
397
|
+
if (def) {
|
|
398
|
+
if (Array.isArray(def.values)) {
|
|
399
|
+
node.literal = def.values.length === 1 ? def.values[0] : def.values.slice();
|
|
400
|
+
} else {
|
|
401
|
+
node.literal = def.value;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (base instanceof z3.ZodEnum) {
|
|
406
|
+
if (def) {
|
|
407
|
+
if (Array.isArray(def.values)) {
|
|
408
|
+
node.enumValues = def.values.slice();
|
|
409
|
+
} else if (def.entries && typeof def.entries === "object") {
|
|
410
|
+
node.enumValues = Object.values(def.entries).map(
|
|
411
|
+
(v) => String(v)
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return node;
|
|
417
|
+
}
|
|
418
|
+
function inferKind(schema) {
|
|
419
|
+
if (schema instanceof z3.ZodString) return "string";
|
|
420
|
+
if (schema instanceof z3.ZodNumber) return "number";
|
|
421
|
+
if (schema instanceof z3.ZodBoolean) return "boolean";
|
|
422
|
+
if (schema instanceof z3.ZodBigInt) return "bigint";
|
|
423
|
+
if (schema instanceof z3.ZodDate) return "date";
|
|
424
|
+
if (schema instanceof z3.ZodArray) return "array";
|
|
425
|
+
if (schema instanceof z3.ZodObject) return "object";
|
|
426
|
+
if (schema instanceof z3.ZodUnion) return "union";
|
|
427
|
+
if (schema instanceof z3.ZodLiteral) return "literal";
|
|
428
|
+
if (schema instanceof z3.ZodEnum) return "enum";
|
|
429
|
+
if (schema instanceof z3.ZodRecord) return "record";
|
|
430
|
+
if (schema instanceof z3.ZodTuple) return "tuple";
|
|
431
|
+
if (schema instanceof z3.ZodUnknown) return "unknown";
|
|
432
|
+
if (schema instanceof z3.ZodAny) return "any";
|
|
433
|
+
return "unknown";
|
|
434
|
+
}
|
|
435
|
+
|
|
302
436
|
// src/docs/serializer.ts
|
|
303
437
|
function serializeLeaf(leaf) {
|
|
304
438
|
const cfg = leaf.cfg;
|
|
@@ -319,22 +453,17 @@ function serializeLeaf(leaf) {
|
|
|
319
453
|
hasBody: !!cfg.bodySchema || !!cfg.bodyFiles?.length,
|
|
320
454
|
hasQuery: !!cfg.querySchema,
|
|
321
455
|
hasParams: !!cfg.paramsSchema,
|
|
322
|
-
hasOutput: !!cfg.outputSchema
|
|
456
|
+
hasOutput: !!cfg.outputSchema,
|
|
457
|
+
bodySchema: introspectSchema(cfg.bodySchema),
|
|
458
|
+
querySchema: introspectSchema(cfg.querySchema),
|
|
459
|
+
paramsSchema: introspectSchema(cfg.paramsSchema),
|
|
460
|
+
outputSchema: introspectSchema(cfg.outputSchema)
|
|
323
461
|
}
|
|
324
462
|
};
|
|
325
463
|
}
|
|
326
464
|
|
|
327
465
|
// src/docs/docs.ts
|
|
328
|
-
|
|
329
|
-
const docsLeaves = leaves.map(serializeLeaf);
|
|
330
|
-
const leafJson = JSON.stringify(docsLeaves).replace(/</g, "\\u003c");
|
|
331
|
-
return `<!DOCTYPE html>
|
|
332
|
-
<html lang="en">
|
|
333
|
-
<head>
|
|
334
|
-
<meta charset="utf-8" />
|
|
335
|
-
<title>API Routes</title>
|
|
336
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
337
|
-
<style>
|
|
466
|
+
var styles = `
|
|
338
467
|
:root {
|
|
339
468
|
--bg: #020617;
|
|
340
469
|
--bg-elevated: #020617;
|
|
@@ -363,6 +492,7 @@ function renderLeafDocsHTML(leaves) {
|
|
|
363
492
|
-webkit-font-smoothing: antialiased;
|
|
364
493
|
}
|
|
365
494
|
|
|
495
|
+
|
|
366
496
|
.page {
|
|
367
497
|
min-height: 100vh;
|
|
368
498
|
padding: 24px;
|
|
@@ -727,12 +857,121 @@ function renderLeafDocsHTML(leaves) {
|
|
|
727
857
|
border: 1px dashed rgba(148, 163, 184, 0.5);
|
|
728
858
|
background: rgba(15, 23, 42, 0.9);
|
|
729
859
|
}
|
|
860
|
+
.schema-section {
|
|
861
|
+
margin-top: 8px;
|
|
862
|
+
font-size: 11px;
|
|
863
|
+
border-top: 1px dashed rgba(148, 163, 184, 0.5);
|
|
864
|
+
padding-top: 6px;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.schema-details {
|
|
868
|
+
margin-top: 6px;
|
|
869
|
+
border-radius: 10px;
|
|
870
|
+
border: 1px solid rgba(148, 163, 184, 0.4);
|
|
871
|
+
background: rgba(15, 23, 42, 0.95);
|
|
872
|
+
padding: 8px 10px;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.schema-details summary {
|
|
876
|
+
cursor: pointer;
|
|
877
|
+
list-style: none;
|
|
878
|
+
font-size: 11px;
|
|
879
|
+
color: var(--text-muted);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.schema-details summary::-webkit-details-marker {
|
|
883
|
+
display: none;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.schema-details summary::before {
|
|
887
|
+
content: "\u25B8";
|
|
888
|
+
display: inline-block;
|
|
889
|
+
margin-right: 6px;
|
|
890
|
+
font-size: 9px;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.schema-details[open] summary::before {
|
|
894
|
+
content: "\u25BE";
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.schema-table {
|
|
898
|
+
width: 100%;
|
|
899
|
+
border-collapse: collapse;
|
|
900
|
+
margin-top: 6px;
|
|
901
|
+
font-size: 11px;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.schema-table th,
|
|
905
|
+
.schema-table td {
|
|
906
|
+
padding: 3px 4px;
|
|
907
|
+
border-bottom: 1px solid rgba(30, 64, 175, 0.35);
|
|
908
|
+
text-align: left;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.schema-table th {
|
|
912
|
+
font-weight: 500;
|
|
913
|
+
color: var(--text-muted);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
.schema-field-name {
|
|
917
|
+
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
|
|
918
|
+
Consolas, "Liberation Mono", "Courier New", monospace;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.schema-type {
|
|
922
|
+
color: var(--accent);
|
|
923
|
+
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco,
|
|
924
|
+
Consolas, "Liberation Mono", "Courier New", monospace;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.schema-required {
|
|
928
|
+
font-size: 10px;
|
|
929
|
+
text-transform: uppercase;
|
|
930
|
+
letter-spacing: 0.12em;
|
|
931
|
+
padding: 1px 5px;
|
|
932
|
+
border-radius: 999px;
|
|
933
|
+
border: 1px solid rgba(52, 211, 153, 0.7);
|
|
934
|
+
color: #bbf7d0;
|
|
935
|
+
background: rgba(22, 163, 74, 0.1);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.schema-optional {
|
|
939
|
+
font-size: 10px;
|
|
940
|
+
text-transform: uppercase;
|
|
941
|
+
letter-spacing: 0.12em;
|
|
942
|
+
padding: 1px 5px;
|
|
943
|
+
border-radius: 999px;
|
|
944
|
+
border: 1px solid rgba(148, 163, 184, 0.6);
|
|
945
|
+
color: var(--text-muted);
|
|
946
|
+
background: rgba(15, 23, 42, 0.95);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.schema-meta {
|
|
950
|
+
margin-top: 6px;
|
|
951
|
+
font-size: 10px;
|
|
952
|
+
color: var(--text-muted);
|
|
953
|
+
white-space: pre-wrap;
|
|
954
|
+
word-break: break-word;
|
|
955
|
+
}
|
|
956
|
+
`;
|
|
957
|
+
function renderLeafDocsHTML(leaves, options = {}) {
|
|
958
|
+
const docsLeaves = leaves.map(serializeLeaf);
|
|
959
|
+
const leafJson = JSON.stringify(docsLeaves).replace(/</g, "\\u003c");
|
|
960
|
+
const nonceAttr = options.cspNonce ? ` nonce="${options.cspNonce}"` : "";
|
|
961
|
+
return `<!DOCTYPE html>
|
|
962
|
+
<html lang="en">
|
|
963
|
+
<head>
|
|
964
|
+
<meta charset="utf-8" />
|
|
965
|
+
<title>API Routes</title>
|
|
966
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
967
|
+
<style${nonceAttr}>
|
|
968
|
+
${styles}
|
|
730
969
|
</style>
|
|
731
970
|
</head>
|
|
732
971
|
<body>
|
|
733
972
|
<div class="page">
|
|
734
973
|
<div class="shell">
|
|
735
|
-
|
|
974
|
+
<header class="header">
|
|
736
975
|
<div class="header-main">
|
|
737
976
|
<div class="title-row">
|
|
738
977
|
<div class="title">
|
|
@@ -746,8 +985,7 @@ function renderLeafDocsHTML(leaves) {
|
|
|
746
985
|
</div>
|
|
747
986
|
<div id="stats" class="stats"></div>
|
|
748
987
|
</header>
|
|
749
|
-
|
|
750
|
-
<div class="layout">
|
|
988
|
+
<div class="layout">
|
|
751
989
|
<aside class="sidebar">
|
|
752
990
|
<h2>Filters</h2>
|
|
753
991
|
|
|
@@ -784,9 +1022,102 @@ function renderLeafDocsHTML(leaves) {
|
|
|
784
1022
|
</div>
|
|
785
1023
|
</div>
|
|
786
1024
|
|
|
787
|
-
<script id="leaf-data" type="application/json">${leafJson}</script>
|
|
788
|
-
<script>
|
|
1025
|
+
<script id="leaf-data" type="application/json"${nonceAttr}>${leafJson}</script>
|
|
1026
|
+
<script${nonceAttr}>
|
|
789
1027
|
(function () {
|
|
1028
|
+
|
|
1029
|
+
function typeLabel(node) {
|
|
1030
|
+
if (!node) return "unknown";
|
|
1031
|
+
var base = node.kind || "unknown";
|
|
1032
|
+
if (node.literal !== undefined) {
|
|
1033
|
+
return JSON.stringify(node.literal);
|
|
1034
|
+
}
|
|
1035
|
+
if (node.enumValues && node.enumValues.length) {
|
|
1036
|
+
return "enum[" + node.enumValues.join(" | ") + "]";
|
|
1037
|
+
}
|
|
1038
|
+
if (node.kind === "array" && node.element) {
|
|
1039
|
+
return "Array<" + typeLabel(node.element) + ">";
|
|
1040
|
+
}
|
|
1041
|
+
if (node.kind === "union" && node.union && node.union.length) {
|
|
1042
|
+
return node.union.map(typeLabel).join(" | ");
|
|
1043
|
+
}
|
|
1044
|
+
return base;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function renderObjectRows(node, prefix) {
|
|
1048
|
+
if (!node || node.kind !== "object" || !node.properties) return "";
|
|
1049
|
+
var rows = "";
|
|
1050
|
+
Object.keys(node.properties).forEach(function (key) {
|
|
1051
|
+
var child = node.properties[key];
|
|
1052
|
+
var fullName = prefix ? prefix + "." + key : key;
|
|
1053
|
+
var required = !child.optional;
|
|
1054
|
+
rows +=
|
|
1055
|
+
"<tr>" +
|
|
1056
|
+
'<td class="schema-field-name">' +
|
|
1057
|
+
escapeHtml(fullName) +
|
|
1058
|
+
"</td>" +
|
|
1059
|
+
'<td class="schema-type">' +
|
|
1060
|
+
escapeHtml(typeLabel(child)) +
|
|
1061
|
+
"</td>" +
|
|
1062
|
+
"<td>" +
|
|
1063
|
+
(required
|
|
1064
|
+
? '<span class="schema-required">required</span>'
|
|
1065
|
+
: '<span class="schema-optional">optional</span>') +
|
|
1066
|
+
"</td>" +
|
|
1067
|
+
"<td>" +
|
|
1068
|
+
(child.description
|
|
1069
|
+
? escapeHtml(String(child.description))
|
|
1070
|
+
: "") +
|
|
1071
|
+
"</td>" +
|
|
1072
|
+
"</tr>";
|
|
1073
|
+
|
|
1074
|
+
// Recursively show nested objects, arrays-of-objects, etc.
|
|
1075
|
+
if (child.kind === "object") {
|
|
1076
|
+
rows += renderObjectRows(child, fullName);
|
|
1077
|
+
} else if (child.kind === "array" && child.element && child.element.kind === "object") {
|
|
1078
|
+
rows += renderObjectRows(
|
|
1079
|
+
child.element,
|
|
1080
|
+
fullName + "[]."
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
return rows;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function renderSchemaSection(label, node) {
|
|
1088
|
+
if (!node) return "";
|
|
1089
|
+
// For non-object schemas, just show a one-line type.
|
|
1090
|
+
var isObject = node.kind === "object" && node.properties;
|
|
1091
|
+
var content = "";
|
|
1092
|
+
|
|
1093
|
+
if (isObject) {
|
|
1094
|
+
var rows = renderObjectRows(node, "");
|
|
1095
|
+
content =
|
|
1096
|
+
'<table class="schema-table">' +
|
|
1097
|
+
"<thead><tr>" +
|
|
1098
|
+
"<th>Field</th><th>Type</th><th></th><th>Description</th>" +
|
|
1099
|
+
"</tr></thead>" +
|
|
1100
|
+
"<tbody>" +
|
|
1101
|
+
rows +
|
|
1102
|
+
"</tbody></table>";
|
|
1103
|
+
} else {
|
|
1104
|
+
content =
|
|
1105
|
+
'<div class="schema-meta">' +
|
|
1106
|
+
"<strong>Type:</strong> " +
|
|
1107
|
+
escapeHtml(typeLabel(node)) +
|
|
1108
|
+
"</div>";
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return (
|
|
1112
|
+
'<div class="schema-section">' +
|
|
1113
|
+
"<div><strong>" +
|
|
1114
|
+
escapeHtml(label) +
|
|
1115
|
+
"</strong></div>" +
|
|
1116
|
+
content +
|
|
1117
|
+
"</div>"
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
790
1121
|
function escapeHtml(str) {
|
|
791
1122
|
return String(str)
|
|
792
1123
|
.replace(/&/g, '&')
|
|
@@ -882,40 +1213,40 @@ function renderLeafDocsHTML(leaves) {
|
|
|
882
1213
|
}
|
|
883
1214
|
|
|
884
1215
|
function routeToHTML(route) {
|
|
885
|
-
var method = String(route.method ||
|
|
886
|
-
var methodClass =
|
|
1216
|
+
var method = String(route.method || "").toUpperCase();
|
|
1217
|
+
var methodClass = "method-" + String(route.method || "").toLowerCase();
|
|
887
1218
|
|
|
888
1219
|
var cfg = route.cfg || {};
|
|
889
|
-
var group = cfg.docsGroup ||
|
|
1220
|
+
var group = cfg.docsGroup || "Ungrouped";
|
|
890
1221
|
|
|
891
|
-
var summary = cfg.summary || cfg.description ||
|
|
1222
|
+
var summary = cfg.summary || cfg.description || "";
|
|
892
1223
|
var description =
|
|
893
1224
|
cfg.description && cfg.summary !== cfg.description
|
|
894
1225
|
? cfg.description
|
|
895
|
-
:
|
|
1226
|
+
: "";
|
|
896
1227
|
|
|
897
1228
|
var tags = Array.isArray(cfg.tags) ? cfg.tags : [];
|
|
898
1229
|
var tagHtml = tags
|
|
899
1230
|
.map(function (tag) {
|
|
900
1231
|
var t = String(tag);
|
|
901
|
-
var extraClass = t ===
|
|
902
|
-
return '<span class="tag' + extraClass + '">' + escapeHtml(t) +
|
|
1232
|
+
var extraClass = t === "not-implemented" ? " tag-not-impl" : "";
|
|
1233
|
+
return '<span class="tag' + extraClass + '">' + escapeHtml(t) + "</span>";
|
|
903
1234
|
})
|
|
904
|
-
.join(
|
|
1235
|
+
.join("");
|
|
905
1236
|
|
|
906
|
-
var metaBadges =
|
|
1237
|
+
var metaBadges = "";
|
|
907
1238
|
if (cfg.feed) {
|
|
908
1239
|
metaBadges += '<span class="badge badge-feed">Feed</span>';
|
|
909
1240
|
}
|
|
910
|
-
if (cfg.deprecated || cfg.stability ===
|
|
1241
|
+
if (cfg.deprecated || cfg.stability === "deprecated") {
|
|
911
1242
|
metaBadges += '<span class="badge badge-deprecated">Deprecated</span>';
|
|
912
|
-
} else if (cfg.stability && cfg.stability !==
|
|
1243
|
+
} else if (cfg.stability && cfg.stability !== "stable") {
|
|
913
1244
|
metaBadges +=
|
|
914
1245
|
'<span class="badge badge-' +
|
|
915
1246
|
escapeHtml(cfg.stability) +
|
|
916
1247
|
'">' +
|
|
917
1248
|
escapeHtml(String(cfg.stability)) +
|
|
918
|
-
|
|
1249
|
+
"</span>";
|
|
919
1250
|
}
|
|
920
1251
|
|
|
921
1252
|
var schemaPills = [];
|
|
@@ -926,8 +1257,48 @@ function renderLeafDocsHTML(leaves) {
|
|
|
926
1257
|
|
|
927
1258
|
var schemaHtml =
|
|
928
1259
|
schemaPills.length > 0
|
|
929
|
-
? '<div class="schema-row">' + schemaPills.join(
|
|
930
|
-
:
|
|
1260
|
+
? '<div class="schema-row">' + schemaPills.join("") + "</div>"
|
|
1261
|
+
: "";
|
|
1262
|
+
|
|
1263
|
+
// NEW: full expandable details including field-level schemas + docsMeta
|
|
1264
|
+
var hasDetailedSchemas =
|
|
1265
|
+
cfg.bodySchema || cfg.querySchema || cfg.paramsSchema || cfg.outputSchema;
|
|
1266
|
+
var detailsInner = "";
|
|
1267
|
+
|
|
1268
|
+
if (cfg.bodySchema) {
|
|
1269
|
+
detailsInner += renderSchemaSection("Request body", cfg.bodySchema);
|
|
1270
|
+
}
|
|
1271
|
+
if (cfg.querySchema) {
|
|
1272
|
+
detailsInner += renderSchemaSection("Query params", cfg.querySchema);
|
|
1273
|
+
}
|
|
1274
|
+
if (cfg.paramsSchema) {
|
|
1275
|
+
detailsInner += renderSchemaSection("Path params", cfg.paramsSchema);
|
|
1276
|
+
}
|
|
1277
|
+
if (cfg.outputSchema) {
|
|
1278
|
+
detailsInner += renderSchemaSection("Response", cfg.outputSchema);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if (cfg.docsMeta) {
|
|
1282
|
+
try {
|
|
1283
|
+
var metaJson = JSON.stringify(cfg.docsMeta, null, 2);
|
|
1284
|
+
detailsInner +=
|
|
1285
|
+
'<div class="schema-section">' +
|
|
1286
|
+
"<div><strong>Metadata</strong></div>" +
|
|
1287
|
+
'<div class="schema-meta">' +
|
|
1288
|
+
escapeHtml(metaJson) +
|
|
1289
|
+
"</div>" +
|
|
1290
|
+
"</div>";
|
|
1291
|
+
} catch (e) {}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
var detailsHtml = "";
|
|
1295
|
+
if (hasDetailedSchemas || cfg.docsMeta) {
|
|
1296
|
+
detailsHtml =
|
|
1297
|
+
'<details class="schema-details">' +
|
|
1298
|
+
"<summary>Inspect schemas & metadata</summary>" +
|
|
1299
|
+
detailsInner +
|
|
1300
|
+
"</details>";
|
|
1301
|
+
}
|
|
931
1302
|
|
|
932
1303
|
return (
|
|
933
1304
|
'<article class="route-card">' +
|
|
@@ -936,31 +1307,33 @@ function renderLeafDocsHTML(leaves) {
|
|
|
936
1307
|
methodClass +
|
|
937
1308
|
'">' +
|
|
938
1309
|
method +
|
|
939
|
-
|
|
1310
|
+
"</span>" +
|
|
940
1311
|
'<code class="route-path">' +
|
|
941
|
-
escapeHtml(route.path ||
|
|
942
|
-
|
|
1312
|
+
escapeHtml(route.path || "") +
|
|
1313
|
+
"</code>" +
|
|
943
1314
|
(group
|
|
944
|
-
? '<span class="group-label">' + escapeHtml(String(group)) +
|
|
945
|
-
:
|
|
946
|
-
|
|
1315
|
+
? '<span class="group-label">' + escapeHtml(String(group)) + "</span>"
|
|
1316
|
+
: "") +
|
|
1317
|
+
"</header>" +
|
|
947
1318
|
'<div class="route-tags">' +
|
|
948
1319
|
tagHtml +
|
|
949
1320
|
metaBadges +
|
|
950
|
-
|
|
1321
|
+
"</div>" +
|
|
951
1322
|
(summary
|
|
952
|
-
? '<p class="route-summary">' + escapeHtml(String(summary)) +
|
|
953
|
-
:
|
|
1323
|
+
? '<p class="route-summary">' + escapeHtml(String(summary)) + "</p>"
|
|
1324
|
+
: "") +
|
|
954
1325
|
(description
|
|
955
1326
|
? '<p class="route-description">' +
|
|
956
1327
|
escapeHtml(String(description)) +
|
|
957
|
-
|
|
958
|
-
:
|
|
1328
|
+
"</p>"
|
|
1329
|
+
: "") +
|
|
959
1330
|
schemaHtml +
|
|
960
|
-
|
|
1331
|
+
(detailsHtml ? detailsHtml : "") +
|
|
1332
|
+
"</article>"
|
|
961
1333
|
);
|
|
962
1334
|
}
|
|
963
1335
|
|
|
1336
|
+
|
|
964
1337
|
function applyFilters() {
|
|
965
1338
|
var query = searchInput.value.toLowerCase().trim();
|
|
966
1339
|
|