@emeryld/rrroutes-contract 2.2.0 → 2.2.2
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/docs/LeafDocsPage.d.ts +0 -5
- package/dist/docs/serializer.d.ts +1 -1
- package/dist/index.cjs +14 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +14 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -4,10 +4,5 @@ interface RenderOptions {
|
|
|
4
4
|
inlineCss?: string;
|
|
5
5
|
inlineClientScript?: string;
|
|
6
6
|
}
|
|
7
|
-
interface RenderOptions {
|
|
8
|
-
cspNonce?: string;
|
|
9
|
-
inlineCss?: string;
|
|
10
|
-
inlineClientScript?: string;
|
|
11
|
-
}
|
|
12
7
|
export declare function renderLeafDocsHTML(leaves: AnyLeaf[], options?: RenderOptions): string;
|
|
13
8
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyLeaf, MethodCfg } from "../core/routesV3.core";
|
|
2
|
-
import { SerializableSchemaNode } from "./schemaIntrospection
|
|
2
|
+
import { SerializableSchemaNode } from "./schemaIntrospection";
|
|
3
3
|
type SerializableMethodCfg = Pick<MethodCfg, "description" | "summary" | "docsGroup" | "tags" | "deprecated" | "stability" | "feed" | "docsMeta"> & {
|
|
4
4
|
hasBody: boolean;
|
|
5
5
|
hasQuery: boolean;
|
package/dist/index.cjs
CHANGED
|
@@ -1204,6 +1204,7 @@ var DOCS_JS = `
|
|
|
1204
1204
|
`;
|
|
1205
1205
|
function renderLeafDocsHTML(leaves, options = {}) {
|
|
1206
1206
|
const leavesJson = JSON.stringify(leaves.map(serializeLeaf));
|
|
1207
|
+
const nonceAttr = options.cspNonce ? ` nonce="${options.cspNonce}"` : "";
|
|
1207
1208
|
return `
|
|
1208
1209
|
<!DOCTYPE html>
|
|
1209
1210
|
<html lang="en">
|
|
@@ -1211,8 +1212,9 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1211
1212
|
<meta charset="UTF-8">
|
|
1212
1213
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1213
1214
|
<title>API Documentation</title>
|
|
1214
|
-
|
|
1215
|
-
${
|
|
1215
|
+
|
|
1216
|
+
<style${nonceAttr}>${CSS_STYLES}</style>
|
|
1217
|
+
${options.inlineCss ? `<style${nonceAttr}>${options.inlineCss}</style>` : ""}
|
|
1216
1218
|
</head>
|
|
1217
1219
|
<body>
|
|
1218
1220
|
<div class="page">
|
|
@@ -1222,24 +1224,24 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1222
1224
|
</div>
|
|
1223
1225
|
</header>
|
|
1224
1226
|
|
|
1225
|
-
<div class="controls-container">
|
|
1227
|
+
<div class="controls-container">
|
|
1226
1228
|
<div class="filters-row">
|
|
1227
1229
|
|
|
1228
1230
|
<div class="left-column">
|
|
1229
1231
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
+
<div class="filter-group method-filters-container">
|
|
1233
|
+
<div class="filter-label">Search</div>
|
|
1232
1234
|
|
|
1233
1235
|
<div class="search-box">
|
|
1234
1236
|
<span class="search-icon">\u{1F50D}</span>
|
|
1235
1237
|
<input type="text" id="searchInput" class="search-input" placeholder="Filter endpoints...">
|
|
1236
1238
|
</div>
|
|
1237
|
-
|
|
1239
|
+
</div>
|
|
1238
1240
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1241
|
+
<div class="filter-group method-filters-container">
|
|
1242
|
+
<div class="filter-label">Methods</div>
|
|
1243
|
+
<div class="checkbox-group" id="methodFilters"></div>
|
|
1244
|
+
</div>
|
|
1243
1245
|
</div>
|
|
1244
1246
|
|
|
1245
1247
|
<div class="filter-group tag-filters-container" id="tagFilterContainer">
|
|
@@ -1257,8 +1259,8 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1257
1259
|
</div>
|
|
1258
1260
|
|
|
1259
1261
|
<script id="leaf-data" type="application/json">${leavesJson}</script>
|
|
1260
|
-
<script>${DOCS_JS}</script>
|
|
1261
|
-
${options.inlineClientScript ? `<script>${options.inlineClientScript}</script>` : ""}
|
|
1262
|
+
<script${nonceAttr}>${DOCS_JS}</script>
|
|
1263
|
+
${options.inlineClientScript ? `<script${nonceAttr}>${options.inlineClientScript}</script>` : ""}
|
|
1262
1264
|
</body>
|
|
1263
1265
|
</html>
|
|
1264
1266
|
`;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/crud/routesV3.crud.ts","../src/sockets/socket.index.ts","../src/docs/schemaIntrospection.ts","../src/docs/serializer.ts","../src/docs/LeafDocsPage.tsx","../src/docs/docs.ts"],"sourcesContent":["export * from './core/routesV3.builder';\nexport * from './core/routesV3.core';\nexport * from './core/routesV3.finalize';\nexport * from './crud/routesV3.crud';\nexport * from './sockets/socket.index';\nexport * from './docs/docs';","import { z } from 'zod';\nimport {\n AnyLeaf,\n Append,\n HttpMethod,\n Leaf,\n Merge,\n MergeArray,\n MethodCfg,\n NodeCfg,\n Prettify,\n} from './routesV3.core';\n\nconst defaultFeedQuerySchema = z.object({\n cursor: z.string().optional(),\n limit: z.coerce.number().min(1).max(100).default(20),\n});\n\nconst paginationField = defaultFeedQuerySchema.optional().default(defaultFeedQuerySchema.parse({}));\ntype PaginationField = typeof paginationField;\n\ntype ZodTypeAny = z.ZodTypeAny;\ntype ParamZod<Name extends string, S extends ZodTypeAny> = z.ZodObject<{ [K in Name]: S }>;\ntype ZodArrayAny = z.ZodArray<ZodTypeAny>;\ntype ZodObjectAny = z.ZodObject<any>;\ntype AnyZodObject = z.ZodObject<any>;\ntype MergedParamsResult<PS, Name extends string, P extends ZodTypeAny> = PS extends ZodTypeAny\n ? z.ZodIntersection<PS, ParamZod<Name, P>>\n : ParamZod<Name, P>;\n\nconst defaultFeedOutputSchema = z.object({\n items: z.array(z.unknown()),\n nextCursor: z.string().optional(),\n});\n\nfunction augmentFeedQuerySchema<Q extends ZodTypeAny | undefined>(schema: Q) {\n if (schema && !(schema instanceof z.ZodObject)) {\n console.warn('Feed queries must be a ZodObject; default pagination applied.');\n return z.object({\n pagination: paginationField,\n });\n }\n\n const base = (schema as ZodObjectAny) ?? z.object({});\n return base.extend({\n pagination: paginationField,\n });\n}\n\nfunction augmentFeedOutputSchema<O extends ZodTypeAny | undefined>(schema: O) {\n if (!schema) return defaultFeedOutputSchema;\n if (schema instanceof z.ZodArray) {\n return z.object({\n items: schema,\n nextCursor: z.string().optional(),\n });\n }\n if (schema instanceof z.ZodObject) {\n const shape = (schema as any).shape ? (schema as any).shape : (schema as any)._def?.shape?.();\n const hasItems = Boolean(shape?.items);\n if (hasItems) {\n return schema.extend({ nextCursor: z.string().optional() });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nfunction mergeSchemas<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodTypeAny;\nfunction mergeSchemas<A extends ZodTypeAny>(a: A, b: undefined): A;\nfunction mergeSchemas<B extends ZodTypeAny>(a: undefined, b: B): B;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined): ZodTypeAny | undefined;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined) {\n if (a && b) return z.intersection(a as any, b as any);\n return (a ?? b) as ZodTypeAny | undefined;\n}\n\ntype FeedQuerySchema<C extends MethodCfg> = z.ZodObject<any>;\n\ntype FeedOutputSchema<C extends MethodCfg> = C['outputSchema'] extends ZodArrayAny\n ? z.ZodObject<{\n items: C['outputSchema'];\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : C['outputSchema'] extends ZodTypeAny\n ? z.ZodObject<{\n items: z.ZodArray<C['outputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : typeof defaultFeedOutputSchema;\n\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>;\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true ? { feed: true } : { feed?: boolean };\n\ntype AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<\n infer Shape\n>\n ? z.ZodObject<Shape & { pagination: PaginationField }>\n : z.ZodObject<{ pagination: PaginationField }>;\n\ntype FeedQueryField<C extends MethodCfg> = {\n querySchema: AddPaginationToQuery<\n C['querySchema'] extends AnyZodObject ? C['querySchema'] : undefined\n >;\n};\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodTypeAny\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined };\n\ntype FeedOutputField<C extends MethodCfg> = { outputSchema: FeedOutputSchema<C> };\n\ntype NonFeedOutputField<C extends MethodCfg> = C['outputSchema'] extends ZodTypeAny\n ? { outputSchema: C['outputSchema'] }\n : { outputSchema?: undefined };\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodTypeAny\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS };\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & FeedOutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & NonFeedOutputField<C> & ParamsField<C, PS>;\n\ntype EffectiveCfg<C extends MethodCfg, PS> = Prettify<\n Merge<MethodCfg, BaseMethodCfg<C> & EffectiveFeedFields<C, PS>>\n>;\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<M, Base, Merge<I, EffectiveCfg<C, PS>>>;\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodTypeAny | undefined,\n> {\n // --- structure ---\n /**\n * Enter or define a static child segment.\n * Optionally accepts extra config and/or a builder to populate nested routes.\n * @param name Child segment literal (no leading slash).\n * @param cfg Optional node configuration to merge for descendants.\n * @param builder Callback to produce leaves for the child branch.\n */\n sub<Name extends string, J extends NodeCfg>(\n name: Name,\n cfg?: J,\n ): Branch<`${Base}/${Name}`, Acc, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, J extends NodeCfg | undefined, R extends readonly AnyLeaf[]>(\n name: Name,\n cfg: J,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], Merge<I, NonNullable<J>>, PS>) => R,\n ): Branch<Base, Append<Acc, R[number]>, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, R extends readonly AnyLeaf[]>(\n name: Name,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], I, PS>) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, PS>;\n\n // --- parameterized segment (single signature) ---\n /**\n * Introduce a `:param` segment and merge its schema into downstream leaves.\n * @param name Parameter key (without leading colon).\n * @param paramsSchema Zod schema for the parameter value.\n * @param builder Callback that produces leaves beneath the parameterized segment.\n */\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base}/:${Name}`,\n readonly [],\n I,\n MergedParamsResult<PS, Name, P>\n >,\n ) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, MergedParamsResult<PS, Name, P>>;\n\n // --- flags inheritance ---\n /**\n * Merge additional node configuration that subsequent leaves will inherit.\n * @param cfg Partial node configuration.\n */\n with<J extends NodeCfg>(cfg: J): Branch<Base, Acc, Merge<I, J>, PS>;\n\n // --- methods (return Branch to keep chaining) ---\n /**\n * Register a GET leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n get<C extends MethodCfg>(\n cfg: C,\n ): Branch<\n Base,\n Append<Acc, Prettify<BuiltLeaf<'get', Base, I, C, PS>>>,\n I,\n PS\n >;\n\n /**\n * Register a POST leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n post<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'post', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PUT leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n put<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'put', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PATCH leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n patch<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'patch', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a DELETE leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n delete<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'delete', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n // --- finalize this subtree ---\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>;\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited?: I,\n): Branch<Base, readonly [], I, undefined> {\n const rootBase = base;\n const rootInherited: NodeCfg = { ...(inherited as NodeCfg) };\n\n function makeBranch<Base2 extends string, I2 extends NodeCfg, PS2 extends ZodTypeAny | undefined>(\n base2: Base2,\n inherited2: I2,\n mergedParamsSchema?: PS2,\n ) {\n const stack: AnyLeaf[] = [];\n let currentBase: string = base2;\n let inheritedCfg: NodeCfg = { ...(inherited2 as NodeCfg) };\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2;\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n // If the method didn’t provide a paramsSchema, inject the active merged one.\n const effectiveParamsSchema = (cfg.paramsSchema ?? currentParamsSchema) as PS2 | undefined;\n const effectiveQuerySchema =\n cfg.feed === true ? augmentFeedQuerySchema(cfg.querySchema) : cfg.querySchema;\n const effectiveOutputSchema =\n cfg.feed === true ? augmentFeedOutputSchema(cfg.outputSchema) : cfg.outputSchema;\n\n const fullCfg = (\n effectiveParamsSchema\n ? {\n ...inheritedCfg,\n ...cfg,\n paramsSchema: effectiveParamsSchema,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n : {\n ...inheritedCfg,\n ...cfg,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n ) as any;\n\n const leaf = {\n method,\n path: currentBase as Base2,\n cfg: fullCfg,\n } as const;\n\n stack.push(leaf as unknown as AnyLeaf);\n\n // Return same runtime obj, but with Acc including this new leaf\n return api as unknown as Branch<Base2, Append<readonly [], typeof leaf>, I2, PS2>;\n }\n\n const api: any = {\n // compose a plain subpath (optional cfg) with optional builder\n sub<Name extends string, J extends NodeCfg | undefined = undefined>(\n name: Name,\n cfgOrBuilder?: J | ((r: any) => readonly AnyLeaf[]),\n maybeBuilder?: (r: any) => readonly AnyLeaf[],\n ) {\n let cfg: NodeCfg | undefined;\n let builder: ((r: any) => readonly AnyLeaf[]) | undefined;\n\n if (typeof cfgOrBuilder === 'function') {\n builder = cfgOrBuilder as any;\n } else {\n cfg = cfgOrBuilder as NodeCfg | undefined;\n builder = maybeBuilder;\n }\n\n const childBase = `${currentBase}/${name}` as const;\n const childInherited = { ...inheritedCfg, ...(cfg ?? {}) } as Merge<I2, NonNullable<J>>;\n\n if (builder) {\n // params schema PS2 flows through unchanged on plain sub\n const child = makeBranch(childBase, childInherited, currentParamsSchema);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n typeof childInherited,\n PS2\n >;\n } else {\n currentBase = childBase;\n inheritedCfg = childInherited;\n return api as Branch<`${Base2}/${Name}`, readonly [], typeof childInherited, PS2>;\n }\n },\n\n // the single param function: name + schema + builder\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base2}/:${Name}`,\n readonly [],\n I2,\n MergedParamsResult<PS2, Name, P>\n >,\n ) => R,\n ) {\n const childBase = `${currentBase}/:${name}` as const;\n // Wrap the value schema under the param name before merging with inherited params schema\n const paramObj: ParamZod<Name, P> = z.object({ [name]: paramsSchema } as Record<Name, P>);\n const childParams = (currentParamsSchema\n ? mergeSchemas(currentParamsSchema, paramObj)\n : paramObj) as MergedParamsResult<PS2, Name, P>;\n const child = makeBranch(childBase, inheritedCfg as I2, childParams);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n I2,\n MergedParamsResult<PS2, Name, P>\n >;\n },\n\n with<J extends NodeCfg>(cfg: J) {\n inheritedCfg = { ...inheritedCfg, ...cfg };\n return api as Branch<Base2, readonly [], Merge<I2, J>, PS2>;\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg);\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false });\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false });\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false });\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false });\n },\n\n done() {\n return stack;\n },\n };\n\n return api as Branch<Base2, readonly [], I2, PS2>;\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(rootBase, rootInherited, undefined);\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S];\n};\n","import { z, ZodType } from 'zod';\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string;\n /** Maximum number of files accepted for this field. */\n maxCount: number;\n};\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport type NodeCfg = {\n /** @deprecated. Does nothing. */\n authenticated?: boolean;\n};\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType;\n /** Zod schema describing the query string. */\n querySchema?: ZodType;\n /** Zod schema describing path params (overrides inferred params). */\n paramsSchema?: ZodType;\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType;\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[];\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean;\n\n /** Optional human-readable description for docs/debugging. */\n description?: string;\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string;\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string;\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[];\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean;\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated';\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean;\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>;\n};\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<M extends HttpMethod, P extends string, C extends MethodCfg> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M;\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P;\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>;\n};\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>;\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>;\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X];\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<A extends readonly unknown[], B extends readonly unknown[]> = [\n ...A,\n ...B,\n];\n\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never;\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S];\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>;\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never ? never : Record<ExtractParamNames<Path>, string | number>;\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(path: Path, params: ExtractParamsFromPath<Path>) {\n if (!params) return path;\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k];\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`);\n return String(v);\n });\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P];\nexport function buildCacheKey<L extends AnyLeaf>(args: {\n leaf: L;\n params?: ExtractParamsFromPath<L['path']>;\n query?: InferQuery<L>;\n}) {\n let p = args.leaf.path;\n if (args.params) {\n p = compilePath<L['path']>(p, args.params);\n }\n return [args.leaf.method, ...(p.split('/').filter(Boolean) as SplitPath<typeof p>), args.query ?? {}] as const;\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends ZodType\n ? z.infer<L['cfg']['paramsSchema']>\n : ExtractParamsFromPath<L['path']>;\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends ZodType\n ? z.infer<L['cfg']['querySchema']>\n : never;\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends ZodType\n ? z.infer<L['cfg']['bodySchema']>\n : never;\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\nexport type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends ZodType\n ? z.infer<L['cfg']['outputSchema']>\n : unknown;\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n","// TODO:\n// * use this as a transform that infers the types from Zod, to only pass the types around instead of the full schemas -> make converters that go both ways (data to schema, schema to data)\n// * consider moving to core package\n// * update server and client side to use this type instead of raw zod schemas \nimport { AnyLeaf, HttpMethod, Prettify } from './routesV3.core';\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeaf> = L extends AnyLeaf\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never;\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeaf[]> = KeyOf<Leaves[number]>;\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<Leaves extends readonly AnyLeaf[], K extends string> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/**\n * Freeze a leaf tuple into a registry with typed key lookups.\n * @param leaves Readonly tuple of leaves produced by the builder DSL.\n * @returns Registry containing the leaves array and a `byKey` lookup map.\n */\nexport function finalize<const L extends readonly AnyLeaf[]>(leaves: L) {\n type Keys = KeysOf<L>;\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> };\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey;\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:');\n (Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k];\n logger.system(`- ${k}`);\n });\n };\n\n return { all: leaves, byKey, log };\n}\n\n/** Nominal type alias for a finalized registry. */\nexport type Registry<R extends ReturnType<typeof finalize>> = R;\n\ntype FilterRoute<\n T extends readonly AnyLeaf[],\n F extends string,\n Acc extends readonly AnyLeaf[] = [],\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc;\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S;\n\ntype Routes<T extends readonly AnyLeaf[], F extends string> = FilterRoute<T, F>;\ntype ByKey<\n T extends readonly AnyLeaf[],\n Acc extends Record<string, AnyLeaf> = {},\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? ByKey<Rest, Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }>\n : Acc;\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<T extends readonly AnyLeaf[], F extends string> = {\n byKey: ByKey<Routes<T, F>>;\n all: Routes<T, F>;\n};\n","// routesV3.crud.ts\n// -----------------------------------------------------------------------------\n// Add a first-class .crud() helper to the routesV3 DSL, without touching the\n// core builder. This file:\n// 1) Declares types + module augmentation to extend Branch<...> with .crud\n// 2) Provides a runtime decorator `withCrud(...)` that installs the method\n// 3) Exports a convenience `resourceWithCrud(...)` wrapper (optional)\n// -----------------------------------------------------------------------------\n\nimport { z, ZodType } from 'zod';\nimport {\n resource as baseResource,\n type Branch as _Branch, // import type so we can augment it\n} from '../core/routesV3.builder';\nimport { type AnyLeaf, type Leaf, type NodeCfg } from '../core/routesV3.core';\n\n// -----------------------------------------------------------------------------\n// Small utilities (runtime + types)\n// -----------------------------------------------------------------------------\n/** Default cursor pagination used by list (GET collection). */\nexport const CrudDefaultPagination = z.object({\n limit: z.coerce.number().min(1).max(100).default(20),\n cursor: z.string().optional(),\n});\nconst CrudPaginationField = CrudDefaultPagination.optional().default(CrudDefaultPagination.parse({}));\ntype CrudPaginationField = typeof CrudPaginationField;\ntype AnyCrudZodObject = z.ZodObject<any>;\ntype AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape>\n ? z.ZodObject<Shape & { pagination: CrudPaginationField }>\n : z.ZodObject<{ pagination: CrudPaginationField }>;\n\n/**\n * Merge two Zod schemas at runtime; matches the typing pattern used in builder.\n * @param a Existing params schema inherited from parent branches.\n * @param b Schema introduced at the current branch.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType | undefined, B extends ZodType | undefined>(\n a: A,\n b: B,\n): A extends ZodType ? (B extends ZodType ? ReturnType<typeof z.intersection<A, B>> : A) : B {\n if (a && b) return z.intersection(a as any, b as any) as any;\n return (a ?? b) as any;\n}\n\n/** Build { [Name]: S } Zod object at the type-level. */\nexport type ParamSchema<Name extends string, S extends ZodType> = z.ZodObject<{\n [K in Name]: S;\n}>;\n\n/** Inject active params schema into a leaf cfg (to match your Leaf typing). */\ntype WithParams<I, P> = P extends ZodType ? Omit<I, 'paramsSchema'> & { paramsSchema: P } : I;\n\n/** Resolve boolean flag: default D unless explicitly false. */\ntype Flag<E, D extends boolean> = E extends false ? false : D;\n\n// -----------------------------------------------------------------------------\n// CRUD config (few knobs, with DX comments)\n// -----------------------------------------------------------------------------\n\n/** Toggle individual CRUD methods; defaults enable all (create/update require body). */\nexport type CrudEnable = {\n /** GET /collection (feed). Defaults to true. */ list?: boolean;\n /** POST /collection. Defaults to true if `create` provided. */ create?: boolean;\n /** GET /collection/:id. Defaults to true. */ read?: boolean;\n /** PATCH /collection/:id. Defaults to true if `update` provided. */ update?: boolean;\n /** DELETE /collection/:id. Defaults to true. */ remove?: boolean;\n};\n\n/** Collection GET config. */\nexport type CrudListCfg = {\n /** Query schema (defaults to cursor pagination). */\n querySchema?: ZodType;\n /** Output schema (defaults to { items: Item[], nextCursor? }). */\n outputSchema?: ZodType;\n /** Optional description string. */\n description?: string;\n};\n\n/** Collection POST config. */\nexport type CrudCreateCfg = {\n /** Body schema for creating an item. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item GET config. */\nexport type CrudReadCfg = {\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item PATCH config. */\nexport type CrudUpdateCfg = {\n /** Body schema for partial updates. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item DELETE config. */\nexport type CrudRemoveCfg = {\n /** Output schema (defaults to { ok: true }). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/**\n * CRUD config for `.crud(\"resource\", cfg, extras?)`.\n *\n * It synthesizes `:[resourceName]Id` as the param key and uses `paramSchema` for the value.\n * Prefer passing a **value schema** (e.g. `z.string().uuid()`).\n * As a convenience, a ZodObject of shape `{ [resourceName]Id: schema }` is accepted at runtime too.\n */\nexport type CrudCfg<Name extends string = string> = {\n /**\n * Zod schema for the ID *value* (e.g. `z.string().uuid()`).\n * The parameter key is always `:${name}Id`.\n * (If you pass `z.object({ [name]Id: ... })`, it's accepted at runtime.)\n */\n paramSchema: ZodType;\n\n /**\n * The single-item output shape used by read/create/update by default.\n * List defaults to `{ items: z.array(itemOutputSchema), nextCursor? }`.\n */\n itemOutputSchema: ZodType;\n\n /** Per-method toggles (all enabled by default; create/update require bodies). */\n enable?: CrudEnable;\n\n /** GET /collection configuration. */ list?: CrudListCfg;\n /** POST /collection configuration (enables create). */ create?: CrudCreateCfg;\n /** GET /collection/:id configuration. */ read?: CrudReadCfg;\n /** PATCH /collection/:id configuration (enables update). */ update?: CrudUpdateCfg;\n /** DELETE /collection/:id configuration. */ remove?: CrudRemoveCfg;\n};\n\n// -----------------------------------------------------------------------------\n// Type plumbing to expose generated leaves (so finalize().byKey sees them)\n// -----------------------------------------------------------------------------\n\ntype CrudLeavesTuple<\n Base extends string,\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n> = `${Name}Id` extends infer IdName extends string\n ? ParamSchema<IdName, C['paramSchema']> extends infer IdParamZ extends ZodType\n ? [\n // GET /collection\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['list'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n feed: true;\n querySchema: C['list'] extends CrudListCfg\n ? AddCrudPagination<\n C['list']['querySchema'] extends AnyCrudZodObject\n ? C['list']['querySchema']\n : undefined\n >\n : AddCrudPagination<undefined>;\n outputSchema: C['list'] extends CrudListCfg\n ? C['list']['outputSchema'] extends ZodType\n ? C['list']['outputSchema']\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []),\n\n // POST /collection\n ...((C['create'] extends CrudCreateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['create'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'post',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['create'] extends CrudCreateCfg\n ? C['create']['bodySchema']\n : never;\n outputSchema: C['create'] extends CrudCreateCfg\n ? C['create']['outputSchema'] extends ZodType\n ? C['create']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []\n : []),\n\n // GET /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['read'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['read'] extends CrudReadCfg\n ? C['read']['outputSchema'] extends ZodType\n ? C['read']['outputSchema']\n : C['itemOutputSchema']\n : C['itemOutputSchema'];\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n\n // PATCH /collection/:id\n ...((C['update'] extends CrudUpdateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['update'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'patch',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['update'] extends CrudUpdateCfg\n ? C['update']['bodySchema']\n : never;\n outputSchema: C['update'] extends CrudUpdateCfg\n ? C['update']['outputSchema'] extends ZodType\n ? C['update']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []\n : []),\n\n // DELETE /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['remove'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'delete',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['remove'] extends CrudRemoveCfg\n ? C['remove']['outputSchema'] extends ZodType\n ? C['remove']['outputSchema']\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n ]\n : never\n : never;\n\n/** Merge generated leaves + extras into the branch accumulator. */\ntype CrudResultAcc<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[],\n> = [...Acc, ...CrudLeavesTuple<Base, I, PS, Name, C>, ...Extras];\n\n// -----------------------------------------------------------------------------\n// Module augmentation: add the typed `.crud(...)` to Branch<...>\n// -----------------------------------------------------------------------------\n\ndeclare module './../core/routesV3.builder' {\n interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n > {\n /**\n * Generate opinionated CRUD endpoints for a collection at `/.../<name>`\n * with an item at `/.../<name>/:<name>Id`.\n *\n * Creates (subject to `enable` + presence of create/update bodies):\n * - GET /<name> (feed list)\n * - POST /<name> (create)\n * - GET /<name>/:<name>Id (read item)\n * - PATCH /<name>/:<name>Id (update item)\n * - DELETE /<name>/:<name>Id (remove item)\n *\n * The `extras` callback receives live builders at both collection and item\n * levels so you can add custom subroutes; their leaves are included in the\n * returned Branch type.\n */\n crud<\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(\n name: Name,\n cfg: C,\n extras?: (ctx: {\n /** Builder at `/.../<name>` (collection scope). */\n collection: _Branch<`${Base}/${Name}`, readonly [], I, PS>;\n /** Builder at `/.../<name>/:<name>Id` (item scope). */\n item: _Branch<\n `${Base}/${Name}/:${`${Name}Id`}`,\n readonly [],\n I,\n ReturnType<typeof mergeSchemas<PS, ParamSchema<`${Name}Id`, C['paramSchema']>>>\n >;\n }) => Extras,\n ): _Branch<Base, CrudResultAcc<Base, Acc, I, PS, Name, C, Extras>, I, PS>;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Runtime: install .crud on any Branch via decorator\n// -----------------------------------------------------------------------------\n\n/**\n * Decorate a Branch instance so `.crud(...)` is available at runtime.\n * Tip: wrap the root: `withCrud(resource('/v1'))`.\n * @param branch Branch returned by `resource(...)`.\n * @returns Same branch with `.crud(...)` installed.\n */\nexport function withCrud<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n>(branch: _Branch<Base, Acc, I, PS>): _Branch<Base, Acc, I, PS> {\n const b = branch as any;\n if (typeof b.crud === 'function') return branch;\n\n b.crud = function <\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(this: any, name: Name, cfg: C, extras?: (ctx: { collection: any; item: any }) => Extras) {\n // Build inside a sub-branch at `/.../<name>` and harvest its leaves.\n return this.sub(name, (collection: any) => {\n const idKey = `${name}Id`;\n\n // Accept a value schema (preferred); but if a z.object({[name]Id: ...}) is provided, use it.\n const s = cfg.paramSchema;\n const isObj =\n s &&\n typeof (s as any)._def === 'object' &&\n (s as any)._def.typeName === 'ZodObject' &&\n typeof (s as any).shape === 'function';\n\n const paramValueSchema =\n isObj && (s as any).shape()[idKey] ? ((s as any).shape()[idKey] as ZodType) : (s as ZodType);\n\n const itemOut = cfg.itemOutputSchema;\n const listOut =\n cfg.list?.outputSchema ??\n z.object({\n items: z.array(itemOut),\n nextCursor: z.string().optional(),\n });\n\n const want = {\n list: cfg.enable?.list !== false,\n create: cfg.enable?.create !== false && !!cfg.create?.bodySchema,\n read: cfg.enable?.read !== false,\n update: cfg.enable?.update !== false && !!cfg.update?.bodySchema,\n remove: cfg.enable?.remove !== false,\n } as const;\n\n // Collection routes\n if (want.list) {\n collection.get({\n feed: true,\n querySchema: cfg.list?.querySchema ?? CrudDefaultPagination,\n outputSchema: listOut,\n description: cfg.list?.description ?? 'List',\n });\n }\n\n if (want.create) {\n collection.post({\n bodySchema: cfg.create!.bodySchema,\n outputSchema: cfg.create?.outputSchema ?? itemOut,\n description: cfg.create?.description ?? 'Create',\n });\n }\n\n // Item routes via :<name>Id\n collection.routeParameter(idKey as any, paramValueSchema as any, (item: any) => {\n if (want.read) {\n item.get({\n outputSchema: cfg.read?.outputSchema ?? itemOut,\n description: cfg.read?.description ?? 'Read',\n });\n }\n if (want.update) {\n item.patch({\n bodySchema: cfg.update!.bodySchema,\n outputSchema: cfg.update?.outputSchema ?? itemOut,\n description: cfg.update?.description ?? 'Update',\n });\n }\n if (want.remove) {\n item.delete({\n outputSchema: cfg.remove?.outputSchema ?? z.object({ ok: z.literal(true) }),\n description: cfg.remove?.description ?? 'Delete',\n });\n }\n\n // Give extras fully-capable builders (decorate so extras can call .crud again)\n if (extras) extras({ collection: withCrud(collection), item: withCrud(item) });\n\n return item.done();\n });\n\n return collection.done();\n });\n };\n\n return branch;\n}\n\n// -----------------------------------------------------------------------------\n// Optional convenience: re-export a resource() that already has .crud installed\n// -----------------------------------------------------------------------------\n\n/**\n * Drop-in replacement for `resource(...)` that returns a Branch with `.crud()` installed.\n * You can either use this or call `withCrud(resource(...))`.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Node configuration merged into every leaf.\n * @returns Branch builder that already includes the CRUD helper.\n */\nexport function resourceWithCrud<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited: I,\n) {\n return withCrud(baseResource(base, inherited));\n}\n\n// Re-export the original in case you want both styles from this module\nexport { baseResource as resource };\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod';\n\nexport type SocketSchema<Output =unknown> = z.ZodType & {\n __out: Output;\n};\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out;\n} ? Out : z.output<Schema>;\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny;\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out;\n};\n\nexport type EventMap = Record<string, SocketEvent<any>>;\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName = 'sys:connect' | 'sys:disconnect' | 'sys:reconnect' | 'sys:connect_error' | 'sys:ping' | 'sys:pong' | 'sys:room_join' | 'sys:room_leave';\nexport function defineSocketEvents<const C extends SocketConnectionConfig, const Schemas extends Record<string, {\n message: z.ZodTypeAny;\n}>>(config: C, events: Schemas): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>;\n };\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>;\n };\n};\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events };\n}\n\n\n\nexport type Payload<T extends EventMap, K extends keyof T> = T[K] extends SocketEvent<infer Out> ? Out : never;\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny;\n leaveMetaMessage: z.ZodTypeAny;\n pingPayload: z.ZodTypeAny;\n pongPayload: z.ZodTypeAny;\n};\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>;\n leaveMetaMessage: SocketSchema<any>;\n pingPayload: SocketSchema<any>;\n pongPayload: SocketSchema<any>;\n}","// schemaIntrospection.ts\nimport * as z from \"zod\";\n\nexport type SerializableSchemaNode = {\n kind: string; // \"object\" | \"string\" | \"number\" | ...\n optional?: boolean;\n nullable?: boolean;\n description?: string;\n\n // object\n properties?: Record<string, SerializableSchemaNode>;\n // array\n element?: SerializableSchemaNode;\n // union\n union?: SerializableSchemaNode[];\n // literal\n literal?: unknown;\n // enum\n enumValues?: string[];\n};\n\ntype ZodAny = z.ZodTypeAny;\n\n/**\n * Zod 3 uses `schema._def`, Zod 4 uses `schema._zod.def`.\n */\nfunction getDef(schema: unknown): any | undefined {\n if (!schema || typeof schema !== \"object\") return undefined;\n const anySchema = schema as any;\n return anySchema._zod?.def ?? anySchema._def;\n}\n\n/**\n * Try to get a human-readable description.\n * Zod 4: use metadata/registry; fallback to internal def.description.\n * Zod 3: only internal def.description exists.\n */\nfunction getDescription(schema: ZodAny): string | undefined {\n const anyZ: any = z as any;\n\n // Zod 4 global registry metadata, if present\n const registry = anyZ.globalRegistry?.get\n ? anyZ.globalRegistry.get(schema)\n : undefined;\n if (registry && typeof registry.description === \"string\") {\n return registry.description;\n }\n\n // Legacy / internal description\n const def = getDef(schema);\n if (def && typeof def.description === \"string\") {\n return def.description;\n }\n\n return undefined;\n}\n\n/**\n * Peel off wrappers (effects, optional, nullable, default) and\n * return the inner schema + flags.\n *\n * Supports:\n * - Zod 3: ZodEffects, ZodOptional, ZodNullable, ZodDefault\n * - Zod 4: ZodOptional, ZodNullable, ZodDefault\n */\nfunction unwrap(schema: ZodAny): {\n base: ZodAny;\n optional: boolean;\n nullable: boolean;\n} {\n let s: ZodAny = schema;\n let optional = false;\n let nullable = false;\n\n // Zod 3 only (undefined in Zod 4)\n const ZodEffectsCtor: any = (z as any).ZodEffects;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // Zod 3: ZodEffects wrapper\n if (ZodEffectsCtor && s instanceof ZodEffectsCtor) {\n const def = getDef(s) || {};\n const sourceType =\n typeof (s as any).sourceType === \"function\"\n ? (s as any).sourceType()\n : def.schema;\n if (!sourceType) break;\n s = sourceType;\n continue;\n }\n\n // Zod 3 + 4: optional/nullable/default wrappers\n if (s instanceof z.ZodOptional) {\n optional = true;\n const def = getDef(s);\n s = (def && def.innerType) || s; // innerType exists in both 3 & 4\n continue;\n }\n\n if (s instanceof z.ZodNullable) {\n nullable = true;\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n if (s instanceof z.ZodDefault) {\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n break;\n }\n\n return { base: s, optional, nullable };\n}\n\nexport function introspectSchema(\n schema: ZodAny | undefined\n): SerializableSchemaNode | undefined {\n if (!schema) return undefined;\n\n const { base, optional, nullable } = unwrap(schema);\n const def = getDef(base);\n\n const node: SerializableSchemaNode = {\n kind: inferKind(base),\n optional: optional || undefined,\n nullable: nullable || undefined,\n description: getDescription(base),\n };\n\n // OBJECT\n if (base instanceof z.ZodObject) {\n // Zod 3: _def.shape() (function)\n // Zod 4: .shape getter returns an object\n const rawShape: any =\n (base as any).shape ?? (def && typeof def.shape === \"function\"\n ? def.shape()\n : def?.shape);\n\n const shape =\n typeof rawShape === \"function\" ? rawShape() : rawShape ?? {};\n\n const props: Record<string, SerializableSchemaNode> = {};\n for (const key of Object.keys(shape)) {\n const child = shape[key] as ZodAny;\n const childNode = introspectSchema(child);\n if (childNode) props[key] = childNode;\n }\n node.properties = props;\n }\n\n // ARRAY\n if (base instanceof z.ZodArray) {\n // Zod 3: def.type is inner schema\n // Zod 4: def.element is inner schema\n const inner =\n (def && (def.element as ZodAny)) ||\n (def && (def.type as ZodAny)) ||\n undefined;\n if (inner) {\n node.element = introspectSchema(inner);\n }\n }\n\n // UNION\n if (base instanceof z.ZodUnion) {\n const options: ZodAny[] = (def && def.options) || [];\n node.union = options\n .map((opt) => introspectSchema(opt))\n .filter(Boolean) as SerializableSchemaNode[];\n }\n\n // LITERAL\n if (base instanceof z.ZodLiteral) {\n if (def) {\n // Zod 4: def.values (multi-literal)\n if (Array.isArray(def.values)) {\n node.literal =\n def.values.length === 1 ? def.values[0] : def.values.slice();\n } else {\n // Zod 3: def.value\n node.literal = def.value;\n }\n }\n }\n\n // ENUM\n if (base instanceof z.ZodEnum) {\n if (def) {\n if (Array.isArray(def.values)) {\n // Zod 3\n node.enumValues = def.values.slice();\n } else if (def.entries && typeof def.entries === \"object\") {\n // Zod 4: entries is a { key: value } map\n node.enumValues = Object.values(def.entries).map((v: unknown) =>\n String(v)\n );\n }\n }\n }\n\n return node;\n}\n\nfunction inferKind(schema: ZodAny): string {\n // This path still uses instanceof; it works with Zod 4 Classic\n // (importing from \"zod\"). Anything unknown falls back to \"unknown\".\n if (schema instanceof z.ZodString) return \"string\";\n if (schema instanceof z.ZodNumber) return \"number\";\n if (schema instanceof z.ZodBoolean) return \"boolean\";\n if (schema instanceof z.ZodBigInt) return \"bigint\";\n if (schema instanceof z.ZodDate) return \"date\";\n if (schema instanceof z.ZodArray) return \"array\";\n if (schema instanceof z.ZodObject) return \"object\";\n if (schema instanceof z.ZodUnion) return \"union\";\n if (schema instanceof z.ZodLiteral) return \"literal\";\n if (schema instanceof z.ZodEnum) return \"enum\";\n if (schema instanceof z.ZodRecord) return \"record\";\n if (schema instanceof z.ZodTuple) return \"tuple\";\n if (schema instanceof z.ZodUnknown) return \"unknown\";\n if (schema instanceof z.ZodAny) return \"any\";\n\n return \"unknown\";\n}\n","// serializer.ts\nimport { AnyLeaf, MethodCfg } from \"../core/routesV3.core\";\nimport { introspectSchema, SerializableSchemaNode } from \"./schemaIntrospection.js\";\n\ntype SerializableMethodCfg = Pick<\n MethodCfg,\n | \"description\"\n | \"summary\"\n | \"docsGroup\"\n | \"tags\"\n | \"deprecated\"\n | \"stability\"\n | \"feed\"\n | \"docsMeta\"\n> & {\n hasBody: boolean;\n hasQuery: boolean;\n hasParams: boolean;\n hasOutput: boolean;\n\n // NEW: full Zod ASTs\n bodySchema?: SerializableSchemaNode;\n querySchema?: SerializableSchemaNode;\n paramsSchema?: SerializableSchemaNode;\n outputSchema?: SerializableSchemaNode;\n};\n\nexport type SerializableLeaf = {\n method: string;\n path: string;\n cfg: SerializableMethodCfg;\n};\n\nexport function serializeLeaf(leaf: AnyLeaf): SerializableLeaf {\n const cfg = leaf.cfg;\n\n const tags = Array.isArray(cfg.tags) ? cfg.tags.slice() : [];\n\n return {\n method: leaf.method, // 'get' | 'post' | ...\n path: leaf.path,\n cfg: {\n description: cfg.description,\n summary: cfg.summary,\n docsGroup: cfg.docsGroup,\n tags,\n deprecated: cfg.deprecated,\n stability: cfg.stability,\n feed: !!cfg.feed,\n docsMeta: cfg.docsMeta,\n hasBody: !!cfg.bodySchema || !!cfg.bodyFiles?.length,\n hasQuery: !!cfg.querySchema,\n hasParams: !!cfg.paramsSchema,\n hasOutput: !!cfg.outputSchema,\n\n bodySchema: introspectSchema(cfg.bodySchema),\n querySchema: introspectSchema(cfg.querySchema),\n paramsSchema: introspectSchema(cfg.paramsSchema),\n outputSchema: introspectSchema(cfg.outputSchema),\n },\n };\n}\n","import { AnyLeaf } from \"../core/routesV3.core\";\n\nimport { serializeLeaf } from \"./serializer.js\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\n/**\n * Updated CSS including pastel tag generation,\n * refined header layouts, and sticky overview integration.\n */\nconst CSS_STYLES = `:root {\n --bg-root: #020617;\n --bg-card: rgba(15, 23, 42, 0.6);\n --bg-card-hover: rgba(30, 41, 59, 0.7);\n --bg-glass: rgba(15, 23, 42, 0.90);\n --border-subtle: rgba(148, 163, 184, 0.2);\n\n --text-main: #f8fafc;\n --text-muted: #94a3b8;\n --text-accent: #c4b5fd;\n\n --accent-primary: #a855f7;\n --accent-glow: rgba(168, 85, 247, 0.25);\n\n /* Method Colors */\n --method-get: #4ade80; --method-get-bg: rgba(34, 197, 94, 0.15);\n --method-post: #60a5fa; --method-post-bg: rgba(59, 130, 246, 0.15);\n --method-put: #facc15; --method-put-bg: rgba(234, 179, 8, 0.15);\n --method-patch: #2dd4bf; --method-patch-bg: rgba(45, 212, 191, 0.15);\n --method-delete: #f87171; --method-delete-bg: rgba(248, 113, 113, 0.15);\n\n --radius-card: 12px;\n --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.4);\n\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\n /* Tag Pastel Palette (Generated via class logic) */\n --tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5; /* Red */\n --tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d; /* Amber */\n --tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac; /* Green */\n --tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9; /* Cyan */\n --tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd; /* Blue */\n --tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe; /* Purple */\n --tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4; /* Pink */\n}\n\n* { box-sizing: border-box; }\n\nhtml, body {\n height: 100%;\n margin: 0;\n padding: 0;\n background-color: var(--bg-root);\n color: var(--text-main);\n font-family: system-ui, -apple-system, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n\nbody {\n background-image:\n radial-gradient(circle at 15% 10%, rgba(88, 28, 135, 0.15), transparent 40%),\n radial-gradient(circle at 85% 80%, rgba(15, 23, 42, 1), transparent 40%);\n background-attachment: fixed;\n}\n\n.page {\n min-height: 100vh;\n padding: 20px 40px 60px;\n max-width: 1600px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* --- Header --- */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.header-title h1 {\n font-size: 28px;\n margin: 0;\n font-weight: 700;\n letter-spacing: -0.02em;\n background: linear-gradient(to right, #e2e8f0, #a855f7);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* --- Sticky Controls & Overview --- */\n.controls-container {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-glass);\n backdrop-filter: blur(16px);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n padding: 12px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);\n}\n\n/* Top Row: Filters */\n.filters-row {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n width: 100%;\n}\n\n/* New Wrapper for Search and Methods */\n.left-column {\n display: flex;\n flex-direction: column; /* Stack Search and Methods vertically */\n gap: 16px; /* Gap between search and methods */\n flex-basis: 300px; /* Fixed width for the left column */\n min-width: 250px;\n}\n\n.search-box {\n /* No change to internal search box styling, but remove flex properties */\n width: 100%; /* Make search fill the left-column width */\n position: relative;\n}\n\n/* Tag Filter Group */\n.filter-group.tag-filters-container {\n flex: 1; /* Tags take up all remaining space on the right */\n min-width: 200px;\n}\n\n/* Method Filter Group */\n.filter-group.method-filters-container {\n /* Stays inside the left-column, no need for 100% basis */\n}\n.search-input {\n width: 100%;\n background: rgba(2, 6, 23, 0.6);\n border: 1px solid var(--border-subtle);\n color: var(--text-main);\n padding: 8px 12px 8px 32px;\n border-radius: 6px;\n font-size: 13px;\n transition: all 0.2s;\n}\n.search-input:focus {\n outline: none;\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 2px var(--accent-glow);\n}\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-muted);\n pointer-events: none;\n font-size: 12px;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.filter-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--text-muted);\n font-weight: 700;\n}\n.checkbox-group {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* Bottom Row: Group Overview Chips */\n.overview-row {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--border-subtle);\n align-items: center;\n}\n.overview-label {\n font-size: 10px;\n text-transform: uppercase;\n color: var(--text-muted);\n font-weight: 700;\n margin-right: 4px;\n}\n\n/* Chips/Pills */\n.pill-checkbox { cursor: pointer; user-select: none; }\n.pill-checkbox input { display: none; }\n.pill-checkbox span {\n display: inline-block;\n padding: 3px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: rgba(30, 41, 59, 0.5);\n color: var(--text-muted);\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n.pill-checkbox input:checked + span {\n background: rgba(168, 85, 247, 0.15);\n color: var(--accent-primary);\n border-color: var(--accent-primary);\n}\n\n/* Specialized Colored Tag Pills for Filters */\n.pill-checkbox.colored-tag input:checked + span {\n background: var(--tag-bg);\n color: var(--tag-fg);\n border-color: var(--tag-fg);\n}\n.pill-checkbox.colored-tag span {\n /* Subtle hint of color even when unchecked? Optional. kept neutral for now */\n}\n\n/* Group Jump Links */\n.group-chip {\n font-size: 11px;\n text-decoration: none;\n color: var(--text-accent);\n background: rgba(168, 85, 247, 0.08);\n padding: 3px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n border: 1px solid transparent;\n}\n.group-chip:hover {\n background: rgba(168, 85, 247, 0.2);\n border-color: var(--accent-primary);\n color: #fff;\n}\n\n/* --- Main Content --- */\n.api-group { margin-bottom: 40px; scroll-margin-top: 180px; }\n.group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.group-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--text-main);\n margin: 0;\n}\n.group-actions button {\n background: transparent;\n border: 1px solid var(--border-subtle);\n color: var(--text-muted);\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n margin-left: 8px;\n transition: all 0.2s;\n}\n.group-actions button:hover {\n color: var(--text-main);\n background: rgba(255,255,255,0.05);\n}\n\n.cards-list { display: flex; flex-direction: column; gap: 12px; }\n\n/* --- Endpoint Card --- */\n.endpoint-card {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n overflow: hidden;\n transition: all 0.2s ease;\n cursor: pointer;\n backdrop-filter: blur(10px);\n}\n.endpoint-card:hover {\n background: var(--bg-card-hover);\n border-color: rgba(148, 163, 184, 0.4);\n box-shadow: var(--shadow-card);\n}\n.endpoint-card[data-expanded=\"true\"] {\n background: rgba(30, 41, 59, 0.6);\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 1px var(--accent-glow), var(--shadow-card);\n}\n\n/* Card Header */\n.card-header {\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 12px; /* Space between method, path, tags */\n flex-wrap: wrap; /* Allow wrapping on very small screens if needed, but row pref */\n}\n\n/* Item 1: Method */\n.method-badge {\n font-size: 11px;\n font-weight: 800;\n text-transform: uppercase;\n padding: 4px 8px;\n border-radius: 4px;\n min-width: 55px;\n text-align: center;\n letter-spacing: 0.05em;\n flex-shrink: 0;\n}\n.m-GET { background: var(--method-get-bg); color: var(--method-get); border: 1px solid rgba(74, 222, 128, 0.3); }\n.m-POST { background: var(--method-post-bg); color: var(--method-post); border: 1px solid rgba(96, 165, 250, 0.3); }\n.m-PUT { background: var(--method-put-bg); color: var(--method-put); border: 1px solid rgba(250, 204, 21, 0.3); }\n.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }\n.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }\n\n/* Item 2: Path (Clickable) */\n.path-container {\n font-family: var(--font-mono);\n font-size: 13px;\n color: var(--text-main);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: background 0.2s;\n}\n.path-container:hover {\n background: rgba(255,255,255,0.05);\n}\n.path-text {\n font-weight: 500;\n}\n.copy-icon {\n opacity: 0;\n color: var(--text-muted);\n font-size: 10px;\n transition: opacity 0.2s;\n}\n.path-container:hover .copy-icon { opacity: 1; }\n\n/* Item 3: Tags (Aligned right of path) */\n.tags-container {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n.status-badge {\n font-size: 9px;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid transparent;\n font-weight: 600;\n letter-spacing: 0.03em;\n}\n/* Tag Colors mapped via JS classes */\n.tag-0 { background: var(--tag-0-bg); color: var(--tag-0-fg); border-color: var(--tag-0-fg); }\n.tag-1 { background: var(--tag-1-bg); color: var(--tag-1-fg); border-color: var(--tag-1-fg); }\n.tag-2 { background: var(--tag-2-bg); color: var(--tag-2-fg); border-color: var(--tag-2-fg); }\n.tag-3 { background: var(--tag-3-bg); color: var(--tag-3-fg); border-color: var(--tag-3-fg); }\n.tag-4 { background: var(--tag-4-bg); color: var(--tag-4-fg); border-color: var(--tag-4-fg); }\n.tag-5 { background: var(--tag-5-bg); color: var(--tag-5-fg); border-color: var(--tag-5-fg); }\n.tag-6 { background: var(--tag-6-bg); color: var(--tag-6-fg); border-color: var(--tag-6-fg); }\n\n.not-implemented { border-color: var(--method-delete); color: var(--method-delete); background: var(--method-delete-bg); }\n\n/* Spacer to push expand icon to right */\n.header-spacer { flex: 1; }\n\n.expand-icon {\n color: var(--text-muted);\n transition: transform 0.3s ease;\n font-size: 10px;\n margin-left: 8px;\n}\n.endpoint-card[data-expanded=\"true\"] .expand-icon {\n transform: rotate(180deg);\n color: var(--text-main);\n}\n\n/* Card Body */\n.card-body {\n display: none;\n padding: 0 16px 16px;\n border-top: 1px solid var(--border-subtle);\n margin-top: 4px;\n cursor: default;\n}\n.endpoint-card[data-expanded=\"true\"] .card-body {\n display: block;\n animation: slideDown 0.2s ease-out;\n}\n@keyframes slideDown {\n from { opacity: 0; transform: translateY(-5px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.section-block { margin-top: 18px; }\n.section-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--text-muted);\n margin-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n padding-bottom: 4px;\n}\n.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }\n\n/* Tables */\n.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }\n.schema-table th {\n text-align: left; color: var(--text-muted); font-weight: 600;\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); background: rgba(0,0,0,0.2);\n}\n.schema-table td {\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted);\n}\n.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 20%; }\n.col-type { font-family: var(--font-mono); color: #93c5fd !important; width: 15%; }\n.req-badge { font-size: 9px; text-transform: uppercase; padding: 2px 4px; border-radius: 2px; }\n.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }\n.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }\n\n.empty-message { text-align: center; padding: 40px; color: var(--text-muted); }\n`;\n\n/**\n * Client-side logic.\n */\nconst DOCS_JS = `\n(function() {\n let leaves = [];\n try {\n leaves = JSON.parse(document.getElementById('leaf-data').textContent || '[]');\n } catch(e) { console.error('Failed to parse docs', e); }\n\n // State\n let filters = {\n search: '',\n methods: new Set(),\n tags: new Set()\n };\n\n // DOM Elements\n const elRouteList = document.getElementById('routeList');\n const elOverview = document.getElementById('overviewList');\n const elSearch = document.getElementById('searchInput');\n const elMethodFilters = document.getElementById('methodFilters');\n const elTagFilters = document.getElementById('tagFilters');\n\n // Constants\n const PALETTE_SIZE = 7; // matches .tag-0 to .tag-6 CSS\n\n // Initialization\n function init() {\n populateFilters();\n render();\n setupGlobalListeners();\n }\n\n // Tag Coloring Logic\n function getTagColorClass(tagName) {\n if (tagName === 'not-implemented') return 'not-implemented';\n // Simple string hash to select color index\n let hash = 0;\n for (let i = 0; i < tagName.length; i++) {\n hash = tagName.charCodeAt(i) + ((hash << 5) - hash);\n }\n const index = Math.abs(hash % PALETTE_SIZE);\n return 'tag-' + index;\n }\n\n // Custom Group Sort: Alpha, but UNGROUPED last\n function sortGroups(a, b) {\n const nameA = a.toUpperCase();\n const nameB = b.toUpperCase();\n if (nameA === 'UNGROUPED') return 1;\n if (nameB === 'UNGROUPED') return -1;\n return nameA.localeCompare(nameB);\n }\n\n function populateFilters() {\n // Extract unique values\n const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));\n const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));\n\n // Setup Method Checkboxes\n const sortedMethods = Array.from(allMethods).sort();\n elMethodFilters.innerHTML = sortedMethods.map(m => \\`\n <label class=\"pill-checkbox\">\n <input type=\"checkbox\" value=\"\\${m}\" checked onchange=\"updateFilters()\">\n <span>\\${m}</span>\n </label>\n \\`).join('');\n sortedMethods.forEach(m => filters.methods.add(m));\n\n // Setup Tag Checkboxes (Left Aligned, Colored)\n if (allTags.size > 0) {\n elTagFilters.innerHTML = Array.from(allTags).sort().map(t => {\n const colorClass = getTagColorClass(t);\n const styleVars = 'style=\"--tag-bg:var(--' + colorClass + '-bg); --tag-fg:var(--' + colorClass + '-fg);\"';\n return \\`\n <label class=\"pill-checkbox colored-tag\" \\${styleVars}>\n <input type=\"checkbox\" value=\"\\${t}\" onchange=\"updateFilters()\">\n <span>\\${t}</span>\n </label>\n \\`;\n }).join('');\n }\n }\n\n window.updateFilters = function() {\n filters.search = elSearch.value.toLowerCase();\n filters.methods.clear();\n elMethodFilters.querySelectorAll('input:checked').forEach(cb => filters.methods.add(cb.value));\n filters.tags.clear();\n elTagFilters.querySelectorAll('input:checked').forEach(cb => filters.tags.add(cb.value));\n render();\n }\n\n // Core Render Logic\n function render() {\n // 1. Filter Data\n const filtered = leaves.filter(leaf => {\n const m = (leaf.method || 'GET').toUpperCase();\n const t = (leaf.cfg && leaf.cfg.tags) ? leaf.cfg.tags : [];\n const path = (leaf.path || '').toLowerCase();\n const summary = (leaf.cfg && leaf.cfg.summary || '').toLowerCase();\n\n if (!filters.methods.has(m)) return false;\n if (filters.tags.size > 0 && !t.some(tag => filters.tags.has(tag))) return false;\n if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;\n\n return true;\n });\n\n if (filtered.length === 0) {\n elRouteList.innerHTML = '<div class=\"empty-message\">No endpoints match your filters.</div>';\n elOverview.innerHTML = '';\n return;\n }\n\n // 2. Group Data\n const groups = {};\n filtered.forEach(leaf => {\n const gName = (leaf.cfg && leaf.cfg.docsGroup) ? leaf.cfg.docsGroup : 'UNGROUPED';\n if (!groups[gName]) groups[gName] = [];\n groups[gName].push(leaf);\n });\n\n const sortedGroupNames = Object.keys(groups).sort(sortGroups);\n\n // 3. Render Overview Chips (Inside Sticky Header)\n // Note: We include a small label \"GROUPS:\" or similar if needed, or just chips.\n elOverview.innerHTML = '<span class=\"overview-label\">JUMP TO:</span>' + sortedGroupNames.map(gName => \\`\n <a href=\"#group-\\${gName}\" class=\"group-chip\" onclick=\"scrollToGroup(event, '\\${gName}')\">\n \\${gName}\n </a>\n \\`).join('');\n\n // 4. Render Groups & Cards\n const html = sortedGroupNames.map(gName => {\n const routes = groups[gName];\n // Sort Routes: Path ASC, then Method\n routes.sort((a, b) => {\n const pA = a.path || '';\n const pB = b.path || '';\n if (pA < pB) return -1;\n if (pA > pB) return 1;\n return (a.method || '').localeCompare(b.method || '');\n });\n\n return \\`\n <div class=\"api-group\" id=\"group-\\${gName}\">\n <div class=\"group-header\">\n <h2 class=\"group-title\">\\${gName}</h2>\n <div class=\"group-actions\">\n <button onclick=\"toggleGroup('\\${gName}', true)\">Expand All</button>\n <button onclick=\"toggleGroup('\\${gName}', false)\">Collapse All</button>\n </div>\n </div>\n <div class=\"cards-list\">\n \\${routes.map(renderCard).join('')}\n </div>\n </div>\n \\`;\n }).join('');\n\n elRouteList.innerHTML = html;\n }\n\n // Card Renderer\n function renderCard(leaf) {\n const method = (leaf.method || 'GET').toUpperCase();\n const path = leaf.path || '/';\n const cfg = leaf.cfg || {};\n const summary = cfg.summary || '';\n const description = cfg.description || '';\n const tags = cfg.tags || [];\n\n // Header Construction: Method | Path | Tags | Spacer | Chevron\n const tagBadges = tags.map(t => {\n const cClass = getTagColorClass(t);\n return \\`<span class=\"status-badge \\${cClass}\">\\${t}</span>\\`;\n }).join('');\n\n const headerHtml = \\`\n <div class=\"card-header\">\n <span class=\"method-badge m-\\${method}\">\\${method}</span>\n\n <div class=\"path-container\" onclick=\"copyText(event, '\\${path}')\" title=\"Click to copy path\">\n <span class=\"path-text\">\\${path}</span>\n <span class=\"copy-icon\">📋</span>\n </div>\n\n <div class=\"tags-container\">\n \\${tagBadges}\n </div>\n\n <div class=\"header-spacer\"></div>\n <div class=\"expand-icon\">▼</div>\n </div>\n \\`;\n\n // Body Construction (Preserved structure)\n let contentHtml = \\`<div class=\"section-block\"><div class=\"summary-text\">\n <strong>\\${escape(summary)}</strong>\n \\${description ? '<br><span style=\"font-size:12px; opacity:0.7\">' + escape(description) + '</span>' : ''}\n </div></div>\\`;\n\n if (cfg.paramsSchema || cfg.querySchema) {\n contentHtml += '<div class=\"section-block\"><div class=\"section-title\">Parameters</div>';\n if (cfg.paramsSchema) contentHtml += renderSchemaTable(cfg.paramsSchema, 'Path Parameters');\n if (cfg.querySchema) contentHtml += renderSchemaTable(cfg.querySchema, 'Query Parameters');\n contentHtml += '</div>';\n }\n\n if (cfg.bodySchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Request Body \\${cfg.hasBody ? '' : '(Optional)'}</div>\n \\${renderSchemaTable(cfg.bodySchema)}\n </div>\n \\`;\n }\n\n if (cfg.outputSchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Response Schema</div>\n \\${renderSchemaTable(cfg.outputSchema)}\n </div>\n \\`;\n }\n\n return \\`\n <article class=\"endpoint-card\" data-expanded=\"false\" onclick=\"toggleCard(this, event)\">\n \\${headerHtml}\n <div class=\"card-body\" onclick=\"event.stopPropagation()\">\n \\${contentHtml}\n </div>\n </article>\n \\`;\n }\n\n function renderSchemaTable(node, title) {\n let rows = '';\n if (node.kind === 'object' && node.properties) {\n Object.keys(node.properties).forEach(key => {\n const prop = node.properties[key];\n const reqClass = prop.optional ? 'req-false' : 'req-true';\n const reqText = prop.optional ? 'OPT' : 'REQ';\n rows += \\`\n <tr>\n <td class=\"col-name\">\\${key}</td>\n <td class=\"col-type\">\\${getTypeLabel(prop)}</td>\n <td><span class=\"req-badge \\${reqClass}\">\\${reqText}</span></td>\n <td>\\${escape(prop.description || '')}</td>\n </tr>\n \\`;\n });\n } else {\n rows = \\`<tr><td colspan=\"4\">Type: \\${getTypeLabel(node)}</td></tr>\\`;\n }\n return \\`\n \\${title ? '<div style=\"font-size:11px; font-weight:600; margin-bottom:4px; color:#64748b\">' + title + '</div>' : ''}\n <table class=\"schema-table\">\\${rows}</table>\n \\`;\n }\n\n function getTypeLabel(node) {\n if (!node) return 'any';\n if (node.kind === 'array') return \\`\\${getTypeLabel(node.element)}[]\\`;\n if (node.enumValues) return \\`enum(\\${node.enumValues.join('|')})\\`;\n return node.kind || 'any';\n }\n\n function escape(str) {\n if (!str) return '';\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n window.toggleCard = function(card, e) {\n if (e.target.closest('.path-container')) return; // Don't toggle on path click\n const isExpanded = card.getAttribute('data-expanded') === 'true';\n card.setAttribute('data-expanded', !isExpanded);\n };\n\n window.toggleGroup = function(gName, expand) {\n const group = document.getElementById('group-' + gName);\n if (!group) return;\n group.querySelectorAll('.endpoint-card').forEach(c => c.setAttribute('data-expanded', expand));\n };\n\n window.copyText = function(e, text) {\n e.stopPropagation();\n navigator.clipboard.writeText(text);\n // Could add visual feedback here\n };\n\n window.scrollToGroup = function(e, gName) {\n e.preventDefault();\n const el = document.getElementById('group-' + gName);\n if(el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });\n }\n\n function setupGlobalListeners() {\n elSearch.addEventListener('input', updateFilters);\n }\n\n init();\n})();\n`;\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n const leavesJson = JSON.stringify(leaves.map(serializeLeaf));\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Documentation</title>\n <style>${CSS_STYLES}</style>\n ${options.inlineCss ? `<style>${options.inlineCss}</style>` : \"\"}\n</head>\n<body>\n <div class=\"page\">\n <header class=\"header\">\n <div class=\"header-title\">\n <h1>API Reference</h1>\n </div>\n </header>\n\n<div class=\"controls-container\">\n <div class=\"filters-row\">\n\n <div class=\"left-column\">\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Search</div>\n\n <div class=\"search-box\">\n <span class=\"search-icon\">🔍</span>\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Filter endpoints...\">\n </div>\n </div>\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Methods</div>\n <div class=\"checkbox-group\" id=\"methodFilters\"></div>\n </div>\n </div>\n\n <div class=\"filter-group tag-filters-container\" id=\"tagFilterContainer\">\n <div class=\"filter-label\">Tags</div>\n <div class=\"checkbox-group\" id=\"tagFilters\"></div>\n </div>\n </div>\n\n <div class=\"overview-row\" id=\"overviewList\"></div>\n </div>\n\n <main id=\"routeList\">\n <div style=\"text-align:center; padding:40px; color:#64748b\">Loading API definition...</div>\n </main>\n </div>\n\n <script id=\"leaf-data\" type=\"application/json\">${leavesJson}</script>\n <script>${DOCS_JS}</script>\n ${options.inlineClientScript ? `<script>${options.inlineClientScript}</script>` : \"\"}\n</body>\n</html>\n `;\n}\n","// renderLeafDocsHTML.ts\nimport { AnyLeaf } from \"../core/routesV3.core\";\nimport { renderLeafDocsHTML as LeafDocsPage } from \"./LeafDocsPage\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n return LeafDocsPage(leaves, options)\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAubnB,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiTT,SAAS,mBACd,QACA,UAAyB,CAAC,GAClB;AACR,QAAM,aAAa,KAAK,UAAU,OAAO,IAAI,aAAa,CAAC;AAE3D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAOE,UAAU;AAAA,IACjB,QAAQ,YAAY,UAAU,QAAQ,SAAS,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDA4Cf,UAAU;AAAA,YACjD,OAAO;AAAA,IACf,QAAQ,qBAAqB,WAAW,QAAQ,kBAAkB,cAAc,EAAE;AAAA;AAAA;AAAA;AAItF;;;ACjzBO,SAASC,oBACd,QACA,UAAyB,CAAC,GAClB;AACR,SAAO,mBAAa,QAAQ,OAAO;AACrC;","names":["mergeSchemas","renderLeafDocsHTML","import_zod","mergeSchemas","z","renderLeafDocsHTML"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/crud/routesV3.crud.ts","../src/sockets/socket.index.ts","../src/docs/schemaIntrospection.ts","../src/docs/serializer.ts","../src/docs/LeafDocsPage.tsx","../src/docs/docs.ts"],"sourcesContent":["export * from './core/routesV3.builder';\nexport * from './core/routesV3.core';\nexport * from './core/routesV3.finalize';\nexport * from './crud/routesV3.crud';\nexport * from './sockets/socket.index';\nexport * from './docs/docs';","import { z } from 'zod';\nimport {\n AnyLeaf,\n Append,\n HttpMethod,\n Leaf,\n Merge,\n MergeArray,\n MethodCfg,\n NodeCfg,\n Prettify,\n} from './routesV3.core';\n\nconst defaultFeedQuerySchema = z.object({\n cursor: z.string().optional(),\n limit: z.coerce.number().min(1).max(100).default(20),\n});\n\nconst paginationField = defaultFeedQuerySchema.optional().default(defaultFeedQuerySchema.parse({}));\ntype PaginationField = typeof paginationField;\n\ntype ZodTypeAny = z.ZodTypeAny;\ntype ParamZod<Name extends string, S extends ZodTypeAny> = z.ZodObject<{ [K in Name]: S }>;\ntype ZodArrayAny = z.ZodArray<ZodTypeAny>;\ntype ZodObjectAny = z.ZodObject<any>;\ntype AnyZodObject = z.ZodObject<any>;\ntype MergedParamsResult<PS, Name extends string, P extends ZodTypeAny> = PS extends ZodTypeAny\n ? z.ZodIntersection<PS, ParamZod<Name, P>>\n : ParamZod<Name, P>;\n\nconst defaultFeedOutputSchema = z.object({\n items: z.array(z.unknown()),\n nextCursor: z.string().optional(),\n});\n\nfunction augmentFeedQuerySchema<Q extends ZodTypeAny | undefined>(schema: Q) {\n if (schema && !(schema instanceof z.ZodObject)) {\n console.warn('Feed queries must be a ZodObject; default pagination applied.');\n return z.object({\n pagination: paginationField,\n });\n }\n\n const base = (schema as ZodObjectAny) ?? z.object({});\n return base.extend({\n pagination: paginationField,\n });\n}\n\nfunction augmentFeedOutputSchema<O extends ZodTypeAny | undefined>(schema: O) {\n if (!schema) return defaultFeedOutputSchema;\n if (schema instanceof z.ZodArray) {\n return z.object({\n items: schema,\n nextCursor: z.string().optional(),\n });\n }\n if (schema instanceof z.ZodObject) {\n const shape = (schema as any).shape ? (schema as any).shape : (schema as any)._def?.shape?.();\n const hasItems = Boolean(shape?.items);\n if (hasItems) {\n return schema.extend({ nextCursor: z.string().optional() });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nfunction mergeSchemas<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodTypeAny;\nfunction mergeSchemas<A extends ZodTypeAny>(a: A, b: undefined): A;\nfunction mergeSchemas<B extends ZodTypeAny>(a: undefined, b: B): B;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined): ZodTypeAny | undefined;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined) {\n if (a && b) return z.intersection(a as any, b as any);\n return (a ?? b) as ZodTypeAny | undefined;\n}\n\ntype FeedQuerySchema<C extends MethodCfg> = z.ZodObject<any>;\n\ntype FeedOutputSchema<C extends MethodCfg> = C['outputSchema'] extends ZodArrayAny\n ? z.ZodObject<{\n items: C['outputSchema'];\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : C['outputSchema'] extends ZodTypeAny\n ? z.ZodObject<{\n items: z.ZodArray<C['outputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : typeof defaultFeedOutputSchema;\n\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>;\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true ? { feed: true } : { feed?: boolean };\n\ntype AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<\n infer Shape\n>\n ? z.ZodObject<Shape & { pagination: PaginationField }>\n : z.ZodObject<{ pagination: PaginationField }>;\n\ntype FeedQueryField<C extends MethodCfg> = {\n querySchema: AddPaginationToQuery<\n C['querySchema'] extends AnyZodObject ? C['querySchema'] : undefined\n >;\n};\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodTypeAny\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined };\n\ntype FeedOutputField<C extends MethodCfg> = { outputSchema: FeedOutputSchema<C> };\n\ntype NonFeedOutputField<C extends MethodCfg> = C['outputSchema'] extends ZodTypeAny\n ? { outputSchema: C['outputSchema'] }\n : { outputSchema?: undefined };\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodTypeAny\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS };\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & FeedOutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & NonFeedOutputField<C> & ParamsField<C, PS>;\n\ntype EffectiveCfg<C extends MethodCfg, PS> = Prettify<\n Merge<MethodCfg, BaseMethodCfg<C> & EffectiveFeedFields<C, PS>>\n>;\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<M, Base, Merge<I, EffectiveCfg<C, PS>>>;\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodTypeAny | undefined,\n> {\n // --- structure ---\n /**\n * Enter or define a static child segment.\n * Optionally accepts extra config and/or a builder to populate nested routes.\n * @param name Child segment literal (no leading slash).\n * @param cfg Optional node configuration to merge for descendants.\n * @param builder Callback to produce leaves for the child branch.\n */\n sub<Name extends string, J extends NodeCfg>(\n name: Name,\n cfg?: J,\n ): Branch<`${Base}/${Name}`, Acc, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, J extends NodeCfg | undefined, R extends readonly AnyLeaf[]>(\n name: Name,\n cfg: J,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], Merge<I, NonNullable<J>>, PS>) => R,\n ): Branch<Base, Append<Acc, R[number]>, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, R extends readonly AnyLeaf[]>(\n name: Name,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], I, PS>) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, PS>;\n\n // --- parameterized segment (single signature) ---\n /**\n * Introduce a `:param` segment and merge its schema into downstream leaves.\n * @param name Parameter key (without leading colon).\n * @param paramsSchema Zod schema for the parameter value.\n * @param builder Callback that produces leaves beneath the parameterized segment.\n */\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base}/:${Name}`,\n readonly [],\n I,\n MergedParamsResult<PS, Name, P>\n >,\n ) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, MergedParamsResult<PS, Name, P>>;\n\n // --- flags inheritance ---\n /**\n * Merge additional node configuration that subsequent leaves will inherit.\n * @param cfg Partial node configuration.\n */\n with<J extends NodeCfg>(cfg: J): Branch<Base, Acc, Merge<I, J>, PS>;\n\n // --- methods (return Branch to keep chaining) ---\n /**\n * Register a GET leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n get<C extends MethodCfg>(\n cfg: C,\n ): Branch<\n Base,\n Append<Acc, Prettify<BuiltLeaf<'get', Base, I, C, PS>>>,\n I,\n PS\n >;\n\n /**\n * Register a POST leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n post<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'post', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PUT leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n put<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'put', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PATCH leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n patch<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'patch', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a DELETE leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n delete<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'delete', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n // --- finalize this subtree ---\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>;\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited?: I,\n): Branch<Base, readonly [], I, undefined> {\n const rootBase = base;\n const rootInherited: NodeCfg = { ...(inherited as NodeCfg) };\n\n function makeBranch<Base2 extends string, I2 extends NodeCfg, PS2 extends ZodTypeAny | undefined>(\n base2: Base2,\n inherited2: I2,\n mergedParamsSchema?: PS2,\n ) {\n const stack: AnyLeaf[] = [];\n let currentBase: string = base2;\n let inheritedCfg: NodeCfg = { ...(inherited2 as NodeCfg) };\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2;\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n // If the method didn’t provide a paramsSchema, inject the active merged one.\n const effectiveParamsSchema = (cfg.paramsSchema ?? currentParamsSchema) as PS2 | undefined;\n const effectiveQuerySchema =\n cfg.feed === true ? augmentFeedQuerySchema(cfg.querySchema) : cfg.querySchema;\n const effectiveOutputSchema =\n cfg.feed === true ? augmentFeedOutputSchema(cfg.outputSchema) : cfg.outputSchema;\n\n const fullCfg = (\n effectiveParamsSchema\n ? {\n ...inheritedCfg,\n ...cfg,\n paramsSchema: effectiveParamsSchema,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n : {\n ...inheritedCfg,\n ...cfg,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n ) as any;\n\n const leaf = {\n method,\n path: currentBase as Base2,\n cfg: fullCfg,\n } as const;\n\n stack.push(leaf as unknown as AnyLeaf);\n\n // Return same runtime obj, but with Acc including this new leaf\n return api as unknown as Branch<Base2, Append<readonly [], typeof leaf>, I2, PS2>;\n }\n\n const api: any = {\n // compose a plain subpath (optional cfg) with optional builder\n sub<Name extends string, J extends NodeCfg | undefined = undefined>(\n name: Name,\n cfgOrBuilder?: J | ((r: any) => readonly AnyLeaf[]),\n maybeBuilder?: (r: any) => readonly AnyLeaf[],\n ) {\n let cfg: NodeCfg | undefined;\n let builder: ((r: any) => readonly AnyLeaf[]) | undefined;\n\n if (typeof cfgOrBuilder === 'function') {\n builder = cfgOrBuilder as any;\n } else {\n cfg = cfgOrBuilder as NodeCfg | undefined;\n builder = maybeBuilder;\n }\n\n const childBase = `${currentBase}/${name}` as const;\n const childInherited = { ...inheritedCfg, ...(cfg ?? {}) } as Merge<I2, NonNullable<J>>;\n\n if (builder) {\n // params schema PS2 flows through unchanged on plain sub\n const child = makeBranch(childBase, childInherited, currentParamsSchema);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n typeof childInherited,\n PS2\n >;\n } else {\n currentBase = childBase;\n inheritedCfg = childInherited;\n return api as Branch<`${Base2}/${Name}`, readonly [], typeof childInherited, PS2>;\n }\n },\n\n // the single param function: name + schema + builder\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base2}/:${Name}`,\n readonly [],\n I2,\n MergedParamsResult<PS2, Name, P>\n >,\n ) => R,\n ) {\n const childBase = `${currentBase}/:${name}` as const;\n // Wrap the value schema under the param name before merging with inherited params schema\n const paramObj: ParamZod<Name, P> = z.object({ [name]: paramsSchema } as Record<Name, P>);\n const childParams = (currentParamsSchema\n ? mergeSchemas(currentParamsSchema, paramObj)\n : paramObj) as MergedParamsResult<PS2, Name, P>;\n const child = makeBranch(childBase, inheritedCfg as I2, childParams);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n I2,\n MergedParamsResult<PS2, Name, P>\n >;\n },\n\n with<J extends NodeCfg>(cfg: J) {\n inheritedCfg = { ...inheritedCfg, ...cfg };\n return api as Branch<Base2, readonly [], Merge<I2, J>, PS2>;\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg);\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false });\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false });\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false });\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false });\n },\n\n done() {\n return stack;\n },\n };\n\n return api as Branch<Base2, readonly [], I2, PS2>;\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(rootBase, rootInherited, undefined);\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S];\n};\n","import { z, ZodType } from 'zod';\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string;\n /** Maximum number of files accepted for this field. */\n maxCount: number;\n};\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport type NodeCfg = {\n /** @deprecated. Does nothing. */\n authenticated?: boolean;\n};\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType;\n /** Zod schema describing the query string. */\n querySchema?: ZodType;\n /** Zod schema describing path params (overrides inferred params). */\n paramsSchema?: ZodType;\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType;\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[];\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean;\n\n /** Optional human-readable description for docs/debugging. */\n description?: string;\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string;\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string;\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[];\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean;\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated';\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean;\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>;\n};\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<M extends HttpMethod, P extends string, C extends MethodCfg> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M;\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P;\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>;\n};\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>;\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>;\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X];\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<A extends readonly unknown[], B extends readonly unknown[]> = [\n ...A,\n ...B,\n];\n\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never;\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S];\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>;\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never ? never : Record<ExtractParamNames<Path>, string | number>;\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(path: Path, params: ExtractParamsFromPath<Path>) {\n if (!params) return path;\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k];\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`);\n return String(v);\n });\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P];\nexport function buildCacheKey<L extends AnyLeaf>(args: {\n leaf: L;\n params?: ExtractParamsFromPath<L['path']>;\n query?: InferQuery<L>;\n}) {\n let p = args.leaf.path;\n if (args.params) {\n p = compilePath<L['path']>(p, args.params);\n }\n return [args.leaf.method, ...(p.split('/').filter(Boolean) as SplitPath<typeof p>), args.query ?? {}] as const;\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends ZodType\n ? z.infer<L['cfg']['paramsSchema']>\n : ExtractParamsFromPath<L['path']>;\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends ZodType\n ? z.infer<L['cfg']['querySchema']>\n : never;\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends ZodType\n ? z.infer<L['cfg']['bodySchema']>\n : never;\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\nexport type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends ZodType\n ? z.infer<L['cfg']['outputSchema']>\n : unknown;\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n","// TODO:\n// * use this as a transform that infers the types from Zod, to only pass the types around instead of the full schemas -> make converters that go both ways (data to schema, schema to data)\n// * consider moving to core package\n// * update server and client side to use this type instead of raw zod schemas \nimport { AnyLeaf, HttpMethod, Prettify } from './routesV3.core';\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeaf> = L extends AnyLeaf\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never;\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeaf[]> = KeyOf<Leaves[number]>;\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<Leaves extends readonly AnyLeaf[], K extends string> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/**\n * Freeze a leaf tuple into a registry with typed key lookups.\n * @param leaves Readonly tuple of leaves produced by the builder DSL.\n * @returns Registry containing the leaves array and a `byKey` lookup map.\n */\nexport function finalize<const L extends readonly AnyLeaf[]>(leaves: L) {\n type Keys = KeysOf<L>;\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> };\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey;\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:');\n (Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k];\n logger.system(`- ${k}`);\n });\n };\n\n return { all: leaves, byKey, log };\n}\n\n/** Nominal type alias for a finalized registry. */\nexport type Registry<R extends ReturnType<typeof finalize>> = R;\n\ntype FilterRoute<\n T extends readonly AnyLeaf[],\n F extends string,\n Acc extends readonly AnyLeaf[] = [],\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc;\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S;\n\ntype Routes<T extends readonly AnyLeaf[], F extends string> = FilterRoute<T, F>;\ntype ByKey<\n T extends readonly AnyLeaf[],\n Acc extends Record<string, AnyLeaf> = {},\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? ByKey<Rest, Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }>\n : Acc;\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<T extends readonly AnyLeaf[], F extends string> = {\n byKey: ByKey<Routes<T, F>>;\n all: Routes<T, F>;\n};\n","// routesV3.crud.ts\n// -----------------------------------------------------------------------------\n// Add a first-class .crud() helper to the routesV3 DSL, without touching the\n// core builder. This file:\n// 1) Declares types + module augmentation to extend Branch<...> with .crud\n// 2) Provides a runtime decorator `withCrud(...)` that installs the method\n// 3) Exports a convenience `resourceWithCrud(...)` wrapper (optional)\n// -----------------------------------------------------------------------------\n\nimport { z, ZodType } from 'zod';\nimport {\n resource as baseResource,\n type Branch as _Branch, // import type so we can augment it\n} from '../core/routesV3.builder';\nimport { type AnyLeaf, type Leaf, type NodeCfg } from '../core/routesV3.core';\n\n// -----------------------------------------------------------------------------\n// Small utilities (runtime + types)\n// -----------------------------------------------------------------------------\n/** Default cursor pagination used by list (GET collection). */\nexport const CrudDefaultPagination = z.object({\n limit: z.coerce.number().min(1).max(100).default(20),\n cursor: z.string().optional(),\n});\nconst CrudPaginationField = CrudDefaultPagination.optional().default(CrudDefaultPagination.parse({}));\ntype CrudPaginationField = typeof CrudPaginationField;\ntype AnyCrudZodObject = z.ZodObject<any>;\ntype AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape>\n ? z.ZodObject<Shape & { pagination: CrudPaginationField }>\n : z.ZodObject<{ pagination: CrudPaginationField }>;\n\n/**\n * Merge two Zod schemas at runtime; matches the typing pattern used in builder.\n * @param a Existing params schema inherited from parent branches.\n * @param b Schema introduced at the current branch.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType | undefined, B extends ZodType | undefined>(\n a: A,\n b: B,\n): A extends ZodType ? (B extends ZodType ? ReturnType<typeof z.intersection<A, B>> : A) : B {\n if (a && b) return z.intersection(a as any, b as any) as any;\n return (a ?? b) as any;\n}\n\n/** Build { [Name]: S } Zod object at the type-level. */\nexport type ParamSchema<Name extends string, S extends ZodType> = z.ZodObject<{\n [K in Name]: S;\n}>;\n\n/** Inject active params schema into a leaf cfg (to match your Leaf typing). */\ntype WithParams<I, P> = P extends ZodType ? Omit<I, 'paramsSchema'> & { paramsSchema: P } : I;\n\n/** Resolve boolean flag: default D unless explicitly false. */\ntype Flag<E, D extends boolean> = E extends false ? false : D;\n\n// -----------------------------------------------------------------------------\n// CRUD config (few knobs, with DX comments)\n// -----------------------------------------------------------------------------\n\n/** Toggle individual CRUD methods; defaults enable all (create/update require body). */\nexport type CrudEnable = {\n /** GET /collection (feed). Defaults to true. */ list?: boolean;\n /** POST /collection. Defaults to true if `create` provided. */ create?: boolean;\n /** GET /collection/:id. Defaults to true. */ read?: boolean;\n /** PATCH /collection/:id. Defaults to true if `update` provided. */ update?: boolean;\n /** DELETE /collection/:id. Defaults to true. */ remove?: boolean;\n};\n\n/** Collection GET config. */\nexport type CrudListCfg = {\n /** Query schema (defaults to cursor pagination). */\n querySchema?: ZodType;\n /** Output schema (defaults to { items: Item[], nextCursor? }). */\n outputSchema?: ZodType;\n /** Optional description string. */\n description?: string;\n};\n\n/** Collection POST config. */\nexport type CrudCreateCfg = {\n /** Body schema for creating an item. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item GET config. */\nexport type CrudReadCfg = {\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item PATCH config. */\nexport type CrudUpdateCfg = {\n /** Body schema for partial updates. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item DELETE config. */\nexport type CrudRemoveCfg = {\n /** Output schema (defaults to { ok: true }). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/**\n * CRUD config for `.crud(\"resource\", cfg, extras?)`.\n *\n * It synthesizes `:[resourceName]Id` as the param key and uses `paramSchema` for the value.\n * Prefer passing a **value schema** (e.g. `z.string().uuid()`).\n * As a convenience, a ZodObject of shape `{ [resourceName]Id: schema }` is accepted at runtime too.\n */\nexport type CrudCfg<Name extends string = string> = {\n /**\n * Zod schema for the ID *value* (e.g. `z.string().uuid()`).\n * The parameter key is always `:${name}Id`.\n * (If you pass `z.object({ [name]Id: ... })`, it's accepted at runtime.)\n */\n paramSchema: ZodType;\n\n /**\n * The single-item output shape used by read/create/update by default.\n * List defaults to `{ items: z.array(itemOutputSchema), nextCursor? }`.\n */\n itemOutputSchema: ZodType;\n\n /** Per-method toggles (all enabled by default; create/update require bodies). */\n enable?: CrudEnable;\n\n /** GET /collection configuration. */ list?: CrudListCfg;\n /** POST /collection configuration (enables create). */ create?: CrudCreateCfg;\n /** GET /collection/:id configuration. */ read?: CrudReadCfg;\n /** PATCH /collection/:id configuration (enables update). */ update?: CrudUpdateCfg;\n /** DELETE /collection/:id configuration. */ remove?: CrudRemoveCfg;\n};\n\n// -----------------------------------------------------------------------------\n// Type plumbing to expose generated leaves (so finalize().byKey sees them)\n// -----------------------------------------------------------------------------\n\ntype CrudLeavesTuple<\n Base extends string,\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n> = `${Name}Id` extends infer IdName extends string\n ? ParamSchema<IdName, C['paramSchema']> extends infer IdParamZ extends ZodType\n ? [\n // GET /collection\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['list'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n feed: true;\n querySchema: C['list'] extends CrudListCfg\n ? AddCrudPagination<\n C['list']['querySchema'] extends AnyCrudZodObject\n ? C['list']['querySchema']\n : undefined\n >\n : AddCrudPagination<undefined>;\n outputSchema: C['list'] extends CrudListCfg\n ? C['list']['outputSchema'] extends ZodType\n ? C['list']['outputSchema']\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []),\n\n // POST /collection\n ...((C['create'] extends CrudCreateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['create'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'post',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['create'] extends CrudCreateCfg\n ? C['create']['bodySchema']\n : never;\n outputSchema: C['create'] extends CrudCreateCfg\n ? C['create']['outputSchema'] extends ZodType\n ? C['create']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []\n : []),\n\n // GET /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['read'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['read'] extends CrudReadCfg\n ? C['read']['outputSchema'] extends ZodType\n ? C['read']['outputSchema']\n : C['itemOutputSchema']\n : C['itemOutputSchema'];\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n\n // PATCH /collection/:id\n ...((C['update'] extends CrudUpdateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['update'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'patch',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['update'] extends CrudUpdateCfg\n ? C['update']['bodySchema']\n : never;\n outputSchema: C['update'] extends CrudUpdateCfg\n ? C['update']['outputSchema'] extends ZodType\n ? C['update']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []\n : []),\n\n // DELETE /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['remove'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'delete',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['remove'] extends CrudRemoveCfg\n ? C['remove']['outputSchema'] extends ZodType\n ? C['remove']['outputSchema']\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n ]\n : never\n : never;\n\n/** Merge generated leaves + extras into the branch accumulator. */\ntype CrudResultAcc<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[],\n> = [...Acc, ...CrudLeavesTuple<Base, I, PS, Name, C>, ...Extras];\n\n// -----------------------------------------------------------------------------\n// Module augmentation: add the typed `.crud(...)` to Branch<...>\n// -----------------------------------------------------------------------------\n\ndeclare module './../core/routesV3.builder' {\n interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n > {\n /**\n * Generate opinionated CRUD endpoints for a collection at `/.../<name>`\n * with an item at `/.../<name>/:<name>Id`.\n *\n * Creates (subject to `enable` + presence of create/update bodies):\n * - GET /<name> (feed list)\n * - POST /<name> (create)\n * - GET /<name>/:<name>Id (read item)\n * - PATCH /<name>/:<name>Id (update item)\n * - DELETE /<name>/:<name>Id (remove item)\n *\n * The `extras` callback receives live builders at both collection and item\n * levels so you can add custom subroutes; their leaves are included in the\n * returned Branch type.\n */\n crud<\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(\n name: Name,\n cfg: C,\n extras?: (ctx: {\n /** Builder at `/.../<name>` (collection scope). */\n collection: _Branch<`${Base}/${Name}`, readonly [], I, PS>;\n /** Builder at `/.../<name>/:<name>Id` (item scope). */\n item: _Branch<\n `${Base}/${Name}/:${`${Name}Id`}`,\n readonly [],\n I,\n ReturnType<typeof mergeSchemas<PS, ParamSchema<`${Name}Id`, C['paramSchema']>>>\n >;\n }) => Extras,\n ): _Branch<Base, CrudResultAcc<Base, Acc, I, PS, Name, C, Extras>, I, PS>;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Runtime: install .crud on any Branch via decorator\n// -----------------------------------------------------------------------------\n\n/**\n * Decorate a Branch instance so `.crud(...)` is available at runtime.\n * Tip: wrap the root: `withCrud(resource('/v1'))`.\n * @param branch Branch returned by `resource(...)`.\n * @returns Same branch with `.crud(...)` installed.\n */\nexport function withCrud<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n>(branch: _Branch<Base, Acc, I, PS>): _Branch<Base, Acc, I, PS> {\n const b = branch as any;\n if (typeof b.crud === 'function') return branch;\n\n b.crud = function <\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(this: any, name: Name, cfg: C, extras?: (ctx: { collection: any; item: any }) => Extras) {\n // Build inside a sub-branch at `/.../<name>` and harvest its leaves.\n return this.sub(name, (collection: any) => {\n const idKey = `${name}Id`;\n\n // Accept a value schema (preferred); but if a z.object({[name]Id: ...}) is provided, use it.\n const s = cfg.paramSchema;\n const isObj =\n s &&\n typeof (s as any)._def === 'object' &&\n (s as any)._def.typeName === 'ZodObject' &&\n typeof (s as any).shape === 'function';\n\n const paramValueSchema =\n isObj && (s as any).shape()[idKey] ? ((s as any).shape()[idKey] as ZodType) : (s as ZodType);\n\n const itemOut = cfg.itemOutputSchema;\n const listOut =\n cfg.list?.outputSchema ??\n z.object({\n items: z.array(itemOut),\n nextCursor: z.string().optional(),\n });\n\n const want = {\n list: cfg.enable?.list !== false,\n create: cfg.enable?.create !== false && !!cfg.create?.bodySchema,\n read: cfg.enable?.read !== false,\n update: cfg.enable?.update !== false && !!cfg.update?.bodySchema,\n remove: cfg.enable?.remove !== false,\n } as const;\n\n // Collection routes\n if (want.list) {\n collection.get({\n feed: true,\n querySchema: cfg.list?.querySchema ?? CrudDefaultPagination,\n outputSchema: listOut,\n description: cfg.list?.description ?? 'List',\n });\n }\n\n if (want.create) {\n collection.post({\n bodySchema: cfg.create!.bodySchema,\n outputSchema: cfg.create?.outputSchema ?? itemOut,\n description: cfg.create?.description ?? 'Create',\n });\n }\n\n // Item routes via :<name>Id\n collection.routeParameter(idKey as any, paramValueSchema as any, (item: any) => {\n if (want.read) {\n item.get({\n outputSchema: cfg.read?.outputSchema ?? itemOut,\n description: cfg.read?.description ?? 'Read',\n });\n }\n if (want.update) {\n item.patch({\n bodySchema: cfg.update!.bodySchema,\n outputSchema: cfg.update?.outputSchema ?? itemOut,\n description: cfg.update?.description ?? 'Update',\n });\n }\n if (want.remove) {\n item.delete({\n outputSchema: cfg.remove?.outputSchema ?? z.object({ ok: z.literal(true) }),\n description: cfg.remove?.description ?? 'Delete',\n });\n }\n\n // Give extras fully-capable builders (decorate so extras can call .crud again)\n if (extras) extras({ collection: withCrud(collection), item: withCrud(item) });\n\n return item.done();\n });\n\n return collection.done();\n });\n };\n\n return branch;\n}\n\n// -----------------------------------------------------------------------------\n// Optional convenience: re-export a resource() that already has .crud installed\n// -----------------------------------------------------------------------------\n\n/**\n * Drop-in replacement for `resource(...)` that returns a Branch with `.crud()` installed.\n * You can either use this or call `withCrud(resource(...))`.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Node configuration merged into every leaf.\n * @returns Branch builder that already includes the CRUD helper.\n */\nexport function resourceWithCrud<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited: I,\n) {\n return withCrud(baseResource(base, inherited));\n}\n\n// Re-export the original in case you want both styles from this module\nexport { baseResource as resource };\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod';\n\nexport type SocketSchema<Output =unknown> = z.ZodType & {\n __out: Output;\n};\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out;\n} ? Out : z.output<Schema>;\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny;\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out;\n};\n\nexport type EventMap = Record<string, SocketEvent<any>>;\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName = 'sys:connect' | 'sys:disconnect' | 'sys:reconnect' | 'sys:connect_error' | 'sys:ping' | 'sys:pong' | 'sys:room_join' | 'sys:room_leave';\nexport function defineSocketEvents<const C extends SocketConnectionConfig, const Schemas extends Record<string, {\n message: z.ZodTypeAny;\n}>>(config: C, events: Schemas): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>;\n };\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>;\n };\n};\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events };\n}\n\n\n\nexport type Payload<T extends EventMap, K extends keyof T> = T[K] extends SocketEvent<infer Out> ? Out : never;\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny;\n leaveMetaMessage: z.ZodTypeAny;\n pingPayload: z.ZodTypeAny;\n pongPayload: z.ZodTypeAny;\n};\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>;\n leaveMetaMessage: SocketSchema<any>;\n pingPayload: SocketSchema<any>;\n pongPayload: SocketSchema<any>;\n}","// schemaIntrospection.ts\nimport * as z from \"zod\";\n\nexport type SerializableSchemaNode = {\n kind: string; // \"object\" | \"string\" | \"number\" | ...\n optional?: boolean;\n nullable?: boolean;\n description?: string;\n\n // object\n properties?: Record<string, SerializableSchemaNode>;\n // array\n element?: SerializableSchemaNode;\n // union\n union?: SerializableSchemaNode[];\n // literal\n literal?: unknown;\n // enum\n enumValues?: string[];\n};\n\ntype ZodAny = z.ZodTypeAny;\n\n/**\n * Zod 3 uses `schema._def`, Zod 4 uses `schema._zod.def`.\n */\nfunction getDef(schema: unknown): any | undefined {\n if (!schema || typeof schema !== \"object\") return undefined;\n const anySchema = schema as any;\n return anySchema._zod?.def ?? anySchema._def;\n}\n\n/**\n * Try to get a human-readable description.\n * Zod 4: use metadata/registry; fallback to internal def.description.\n * Zod 3: only internal def.description exists.\n */\nfunction getDescription(schema: ZodAny): string | undefined {\n const anyZ: any = z as any;\n\n // Zod 4 global registry metadata, if present\n const registry = anyZ.globalRegistry?.get\n ? anyZ.globalRegistry.get(schema)\n : undefined;\n if (registry && typeof registry.description === \"string\") {\n return registry.description;\n }\n\n // Legacy / internal description\n const def = getDef(schema);\n if (def && typeof def.description === \"string\") {\n return def.description;\n }\n\n return undefined;\n}\n\n/**\n * Peel off wrappers (effects, optional, nullable, default) and\n * return the inner schema + flags.\n *\n * Supports:\n * - Zod 3: ZodEffects, ZodOptional, ZodNullable, ZodDefault\n * - Zod 4: ZodOptional, ZodNullable, ZodDefault\n */\nfunction unwrap(schema: ZodAny): {\n base: ZodAny;\n optional: boolean;\n nullable: boolean;\n} {\n let s: ZodAny = schema;\n let optional = false;\n let nullable = false;\n\n // Zod 3 only (undefined in Zod 4)\n const ZodEffectsCtor: any = (z as any).ZodEffects;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // Zod 3: ZodEffects wrapper\n if (ZodEffectsCtor && s instanceof ZodEffectsCtor) {\n const def = getDef(s) || {};\n const sourceType =\n typeof (s as any).sourceType === \"function\"\n ? (s as any).sourceType()\n : def.schema;\n if (!sourceType) break;\n s = sourceType;\n continue;\n }\n\n // Zod 3 + 4: optional/nullable/default wrappers\n if (s instanceof z.ZodOptional) {\n optional = true;\n const def = getDef(s);\n s = (def && def.innerType) || s; // innerType exists in both 3 & 4\n continue;\n }\n\n if (s instanceof z.ZodNullable) {\n nullable = true;\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n if (s instanceof z.ZodDefault) {\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n break;\n }\n\n return { base: s, optional, nullable };\n}\n\nexport function introspectSchema(\n schema: ZodAny | undefined\n): SerializableSchemaNode | undefined {\n if (!schema) return undefined;\n\n const { base, optional, nullable } = unwrap(schema);\n const def = getDef(base);\n\n const node: SerializableSchemaNode = {\n kind: inferKind(base),\n optional: optional || undefined,\n nullable: nullable || undefined,\n description: getDescription(base),\n };\n\n // OBJECT\n if (base instanceof z.ZodObject) {\n // Zod 3: _def.shape() (function)\n // Zod 4: .shape getter returns an object\n const rawShape: any =\n (base as any).shape ?? (def && typeof def.shape === \"function\"\n ? def.shape()\n : def?.shape);\n\n const shape =\n typeof rawShape === \"function\" ? rawShape() : rawShape ?? {};\n\n const props: Record<string, SerializableSchemaNode> = {};\n for (const key of Object.keys(shape)) {\n const child = shape[key] as ZodAny;\n const childNode = introspectSchema(child);\n if (childNode) props[key] = childNode;\n }\n node.properties = props;\n }\n\n // ARRAY\n if (base instanceof z.ZodArray) {\n // Zod 3: def.type is inner schema\n // Zod 4: def.element is inner schema\n const inner =\n (def && (def.element as ZodAny)) ||\n (def && (def.type as ZodAny)) ||\n undefined;\n if (inner) {\n node.element = introspectSchema(inner);\n }\n }\n\n // UNION\n if (base instanceof z.ZodUnion) {\n const options: ZodAny[] = (def && def.options) || [];\n node.union = options\n .map((opt) => introspectSchema(opt))\n .filter(Boolean) as SerializableSchemaNode[];\n }\n\n // LITERAL\n if (base instanceof z.ZodLiteral) {\n if (def) {\n // Zod 4: def.values (multi-literal)\n if (Array.isArray(def.values)) {\n node.literal =\n def.values.length === 1 ? def.values[0] : def.values.slice();\n } else {\n // Zod 3: def.value\n node.literal = def.value;\n }\n }\n }\n\n // ENUM\n if (base instanceof z.ZodEnum) {\n if (def) {\n if (Array.isArray(def.values)) {\n // Zod 3\n node.enumValues = def.values.slice();\n } else if (def.entries && typeof def.entries === \"object\") {\n // Zod 4: entries is a { key: value } map\n node.enumValues = Object.values(def.entries).map((v: unknown) =>\n String(v)\n );\n }\n }\n }\n\n return node;\n}\n\nfunction inferKind(schema: ZodAny): string {\n // This path still uses instanceof; it works with Zod 4 Classic\n // (importing from \"zod\"). Anything unknown falls back to \"unknown\".\n if (schema instanceof z.ZodString) return \"string\";\n if (schema instanceof z.ZodNumber) return \"number\";\n if (schema instanceof z.ZodBoolean) return \"boolean\";\n if (schema instanceof z.ZodBigInt) return \"bigint\";\n if (schema instanceof z.ZodDate) return \"date\";\n if (schema instanceof z.ZodArray) return \"array\";\n if (schema instanceof z.ZodObject) return \"object\";\n if (schema instanceof z.ZodUnion) return \"union\";\n if (schema instanceof z.ZodLiteral) return \"literal\";\n if (schema instanceof z.ZodEnum) return \"enum\";\n if (schema instanceof z.ZodRecord) return \"record\";\n if (schema instanceof z.ZodTuple) return \"tuple\";\n if (schema instanceof z.ZodUnknown) return \"unknown\";\n if (schema instanceof z.ZodAny) return \"any\";\n\n return \"unknown\";\n}\n","// serializer.ts\nimport { AnyLeaf, MethodCfg } from \"../core/routesV3.core\";\nimport { introspectSchema, SerializableSchemaNode } from \"./schemaIntrospection\";\n\ntype SerializableMethodCfg = Pick<\n MethodCfg,\n | \"description\"\n | \"summary\"\n | \"docsGroup\"\n | \"tags\"\n | \"deprecated\"\n | \"stability\"\n | \"feed\"\n | \"docsMeta\"\n> & {\n hasBody: boolean;\n hasQuery: boolean;\n hasParams: boolean;\n hasOutput: boolean;\n\n // NEW: full Zod ASTs\n bodySchema?: SerializableSchemaNode;\n querySchema?: SerializableSchemaNode;\n paramsSchema?: SerializableSchemaNode;\n outputSchema?: SerializableSchemaNode;\n};\n\nexport type SerializableLeaf = {\n method: string;\n path: string;\n cfg: SerializableMethodCfg;\n};\n\nexport function serializeLeaf(leaf: AnyLeaf): SerializableLeaf {\n const cfg = leaf.cfg;\n\n const tags = Array.isArray(cfg.tags) ? cfg.tags.slice() : [];\n\n return {\n method: leaf.method, // 'get' | 'post' | ...\n path: leaf.path,\n cfg: {\n description: cfg.description,\n summary: cfg.summary,\n docsGroup: cfg.docsGroup,\n tags,\n deprecated: cfg.deprecated,\n stability: cfg.stability,\n feed: !!cfg.feed,\n docsMeta: cfg.docsMeta,\n hasBody: !!cfg.bodySchema || !!cfg.bodyFiles?.length,\n hasQuery: !!cfg.querySchema,\n hasParams: !!cfg.paramsSchema,\n hasOutput: !!cfg.outputSchema,\n\n bodySchema: introspectSchema(cfg.bodySchema),\n querySchema: introspectSchema(cfg.querySchema),\n paramsSchema: introspectSchema(cfg.paramsSchema),\n outputSchema: introspectSchema(cfg.outputSchema),\n },\n };\n}\n","import { AnyLeaf } from \"../core/routesV3.core\";\n\nimport { serializeLeaf } from \"./serializer\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n/**\n * Updated CSS including pastel tag generation,\n * refined header layouts, and sticky overview integration.\n */\nconst CSS_STYLES = `:root {\n --bg-root: #020617;\n --bg-card: rgba(15, 23, 42, 0.6);\n --bg-card-hover: rgba(30, 41, 59, 0.7);\n --bg-glass: rgba(15, 23, 42, 0.90);\n --border-subtle: rgba(148, 163, 184, 0.2);\n\n --text-main: #f8fafc;\n --text-muted: #94a3b8;\n --text-accent: #c4b5fd;\n\n --accent-primary: #a855f7;\n --accent-glow: rgba(168, 85, 247, 0.25);\n\n /* Method Colors */\n --method-get: #4ade80; --method-get-bg: rgba(34, 197, 94, 0.15);\n --method-post: #60a5fa; --method-post-bg: rgba(59, 130, 246, 0.15);\n --method-put: #facc15; --method-put-bg: rgba(234, 179, 8, 0.15);\n --method-patch: #2dd4bf; --method-patch-bg: rgba(45, 212, 191, 0.15);\n --method-delete: #f87171; --method-delete-bg: rgba(248, 113, 113, 0.15);\n\n --radius-card: 12px;\n --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.4);\n\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\n /* Tag Pastel Palette (Generated via class logic) */\n --tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5; /* Red */\n --tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d; /* Amber */\n --tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac; /* Green */\n --tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9; /* Cyan */\n --tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd; /* Blue */\n --tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe; /* Purple */\n --tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4; /* Pink */\n}\n\n* { box-sizing: border-box; }\n\nhtml, body {\n height: 100%;\n margin: 0;\n padding: 0;\n background-color: var(--bg-root);\n color: var(--text-main);\n font-family: system-ui, -apple-system, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n\nbody {\n background-image:\n radial-gradient(circle at 15% 10%, rgba(88, 28, 135, 0.15), transparent 40%),\n radial-gradient(circle at 85% 80%, rgba(15, 23, 42, 1), transparent 40%);\n background-attachment: fixed;\n}\n\n.page {\n min-height: 100vh;\n padding: 20px 40px 60px;\n max-width: 1600px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* --- Header --- */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.header-title h1 {\n font-size: 28px;\n margin: 0;\n font-weight: 700;\n letter-spacing: -0.02em;\n background: linear-gradient(to right, #e2e8f0, #a855f7);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* --- Sticky Controls & Overview --- */\n.controls-container {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-glass);\n backdrop-filter: blur(16px);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n padding: 12px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);\n}\n\n/* Top Row: Filters */\n.filters-row {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n width: 100%;\n}\n\n/* New Wrapper for Search and Methods */\n.left-column {\n display: flex;\n flex-direction: column; /* Stack Search and Methods vertically */\n gap: 16px; /* Gap between search and methods */\n flex-basis: 300px; /* Fixed width for the left column */\n min-width: 250px;\n}\n\n.search-box {\n /* No change to internal search box styling, but remove flex properties */\n width: 100%; /* Make search fill the left-column width */\n position: relative;\n}\n\n/* Tag Filter Group */\n.filter-group.tag-filters-container {\n flex: 1; /* Tags take up all remaining space on the right */\n min-width: 200px;\n}\n\n/* Method Filter Group */\n.filter-group.method-filters-container {\n /* Stays inside the left-column, no need for 100% basis */\n}\n.search-input {\n width: 100%;\n background: rgba(2, 6, 23, 0.6);\n border: 1px solid var(--border-subtle);\n color: var(--text-main);\n padding: 8px 12px 8px 32px;\n border-radius: 6px;\n font-size: 13px;\n transition: all 0.2s;\n}\n.search-input:focus {\n outline: none;\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 2px var(--accent-glow);\n}\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-muted);\n pointer-events: none;\n font-size: 12px;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.filter-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--text-muted);\n font-weight: 700;\n}\n.checkbox-group {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* Bottom Row: Group Overview Chips */\n.overview-row {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--border-subtle);\n align-items: center;\n}\n.overview-label {\n font-size: 10px;\n text-transform: uppercase;\n color: var(--text-muted);\n font-weight: 700;\n margin-right: 4px;\n}\n\n/* Chips/Pills */\n.pill-checkbox { cursor: pointer; user-select: none; }\n.pill-checkbox input { display: none; }\n.pill-checkbox span {\n display: inline-block;\n padding: 3px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: rgba(30, 41, 59, 0.5);\n color: var(--text-muted);\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n.pill-checkbox input:checked + span {\n background: rgba(168, 85, 247, 0.15);\n color: var(--accent-primary);\n border-color: var(--accent-primary);\n}\n\n/* Specialized Colored Tag Pills for Filters */\n.pill-checkbox.colored-tag input:checked + span {\n background: var(--tag-bg);\n color: var(--tag-fg);\n border-color: var(--tag-fg);\n}\n.pill-checkbox.colored-tag span {\n /* Subtle hint of color even when unchecked? Optional. kept neutral for now */\n}\n\n/* Group Jump Links */\n.group-chip {\n font-size: 11px;\n text-decoration: none;\n color: var(--text-accent);\n background: rgba(168, 85, 247, 0.08);\n padding: 3px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n border: 1px solid transparent;\n}\n.group-chip:hover {\n background: rgba(168, 85, 247, 0.2);\n border-color: var(--accent-primary);\n color: #fff;\n}\n\n/* --- Main Content --- */\n.api-group { margin-bottom: 40px; scroll-margin-top: 180px; }\n.group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.group-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--text-main);\n margin: 0;\n}\n.group-actions button {\n background: transparent;\n border: 1px solid var(--border-subtle);\n color: var(--text-muted);\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n margin-left: 8px;\n transition: all 0.2s;\n}\n.group-actions button:hover {\n color: var(--text-main);\n background: rgba(255,255,255,0.05);\n}\n\n.cards-list { display: flex; flex-direction: column; gap: 12px; }\n\n/* --- Endpoint Card --- */\n.endpoint-card {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n overflow: hidden;\n transition: all 0.2s ease;\n cursor: pointer;\n backdrop-filter: blur(10px);\n}\n.endpoint-card:hover {\n background: var(--bg-card-hover);\n border-color: rgba(148, 163, 184, 0.4);\n box-shadow: var(--shadow-card);\n}\n.endpoint-card[data-expanded=\"true\"] {\n background: rgba(30, 41, 59, 0.6);\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 1px var(--accent-glow), var(--shadow-card);\n}\n\n/* Card Header */\n.card-header {\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 12px; /* Space between method, path, tags */\n flex-wrap: wrap; /* Allow wrapping on very small screens if needed, but row pref */\n}\n\n/* Item 1: Method */\n.method-badge {\n font-size: 11px;\n font-weight: 800;\n text-transform: uppercase;\n padding: 4px 8px;\n border-radius: 4px;\n min-width: 55px;\n text-align: center;\n letter-spacing: 0.05em;\n flex-shrink: 0;\n}\n.m-GET { background: var(--method-get-bg); color: var(--method-get); border: 1px solid rgba(74, 222, 128, 0.3); }\n.m-POST { background: var(--method-post-bg); color: var(--method-post); border: 1px solid rgba(96, 165, 250, 0.3); }\n.m-PUT { background: var(--method-put-bg); color: var(--method-put); border: 1px solid rgba(250, 204, 21, 0.3); }\n.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }\n.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }\n\n/* Item 2: Path (Clickable) */\n.path-container {\n font-family: var(--font-mono);\n font-size: 13px;\n color: var(--text-main);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: background 0.2s;\n}\n.path-container:hover {\n background: rgba(255,255,255,0.05);\n}\n.path-text {\n font-weight: 500;\n}\n.copy-icon {\n opacity: 0;\n color: var(--text-muted);\n font-size: 10px;\n transition: opacity 0.2s;\n}\n.path-container:hover .copy-icon { opacity: 1; }\n\n/* Item 3: Tags (Aligned right of path) */\n.tags-container {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n.status-badge {\n font-size: 9px;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid transparent;\n font-weight: 600;\n letter-spacing: 0.03em;\n}\n/* Tag Colors mapped via JS classes */\n.tag-0 { background: var(--tag-0-bg); color: var(--tag-0-fg); border-color: var(--tag-0-fg); }\n.tag-1 { background: var(--tag-1-bg); color: var(--tag-1-fg); border-color: var(--tag-1-fg); }\n.tag-2 { background: var(--tag-2-bg); color: var(--tag-2-fg); border-color: var(--tag-2-fg); }\n.tag-3 { background: var(--tag-3-bg); color: var(--tag-3-fg); border-color: var(--tag-3-fg); }\n.tag-4 { background: var(--tag-4-bg); color: var(--tag-4-fg); border-color: var(--tag-4-fg); }\n.tag-5 { background: var(--tag-5-bg); color: var(--tag-5-fg); border-color: var(--tag-5-fg); }\n.tag-6 { background: var(--tag-6-bg); color: var(--tag-6-fg); border-color: var(--tag-6-fg); }\n\n.not-implemented { border-color: var(--method-delete); color: var(--method-delete); background: var(--method-delete-bg); }\n\n/* Spacer to push expand icon to right */\n.header-spacer { flex: 1; }\n\n.expand-icon {\n color: var(--text-muted);\n transition: transform 0.3s ease;\n font-size: 10px;\n margin-left: 8px;\n}\n.endpoint-card[data-expanded=\"true\"] .expand-icon {\n transform: rotate(180deg);\n color: var(--text-main);\n}\n\n/* Card Body */\n.card-body {\n display: none;\n padding: 0 16px 16px;\n border-top: 1px solid var(--border-subtle);\n margin-top: 4px;\n cursor: default;\n}\n.endpoint-card[data-expanded=\"true\"] .card-body {\n display: block;\n animation: slideDown 0.2s ease-out;\n}\n@keyframes slideDown {\n from { opacity: 0; transform: translateY(-5px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.section-block { margin-top: 18px; }\n.section-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--text-muted);\n margin-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n padding-bottom: 4px;\n}\n.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }\n\n/* Tables */\n.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }\n.schema-table th {\n text-align: left; color: var(--text-muted); font-weight: 600;\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); background: rgba(0,0,0,0.2);\n}\n.schema-table td {\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted);\n}\n.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 20%; }\n.col-type { font-family: var(--font-mono); color: #93c5fd !important; width: 15%; }\n.req-badge { font-size: 9px; text-transform: uppercase; padding: 2px 4px; border-radius: 2px; }\n.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }\n.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }\n\n.empty-message { text-align: center; padding: 40px; color: var(--text-muted); }\n`;\n\n/**\n * Client-side logic.\n */\nconst DOCS_JS = `\n(function() {\n let leaves = [];\n try {\n leaves = JSON.parse(document.getElementById('leaf-data').textContent || '[]');\n } catch(e) { console.error('Failed to parse docs', e); }\n\n // State\n let filters = {\n search: '',\n methods: new Set(),\n tags: new Set()\n };\n\n // DOM Elements\n const elRouteList = document.getElementById('routeList');\n const elOverview = document.getElementById('overviewList');\n const elSearch = document.getElementById('searchInput');\n const elMethodFilters = document.getElementById('methodFilters');\n const elTagFilters = document.getElementById('tagFilters');\n\n // Constants\n const PALETTE_SIZE = 7; // matches .tag-0 to .tag-6 CSS\n\n // Initialization\n function init() {\n populateFilters();\n render();\n setupGlobalListeners();\n }\n\n // Tag Coloring Logic\n function getTagColorClass(tagName) {\n if (tagName === 'not-implemented') return 'not-implemented';\n // Simple string hash to select color index\n let hash = 0;\n for (let i = 0; i < tagName.length; i++) {\n hash = tagName.charCodeAt(i) + ((hash << 5) - hash);\n }\n const index = Math.abs(hash % PALETTE_SIZE);\n return 'tag-' + index;\n }\n\n // Custom Group Sort: Alpha, but UNGROUPED last\n function sortGroups(a, b) {\n const nameA = a.toUpperCase();\n const nameB = b.toUpperCase();\n if (nameA === 'UNGROUPED') return 1;\n if (nameB === 'UNGROUPED') return -1;\n return nameA.localeCompare(nameB);\n }\n\n function populateFilters() {\n // Extract unique values\n const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));\n const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));\n\n // Setup Method Checkboxes\n const sortedMethods = Array.from(allMethods).sort();\n elMethodFilters.innerHTML = sortedMethods.map(m => \\`\n <label class=\"pill-checkbox\">\n <input type=\"checkbox\" value=\"\\${m}\" checked onchange=\"updateFilters()\">\n <span>\\${m}</span>\n </label>\n \\`).join('');\n sortedMethods.forEach(m => filters.methods.add(m));\n\n // Setup Tag Checkboxes (Left Aligned, Colored)\n if (allTags.size > 0) {\n elTagFilters.innerHTML = Array.from(allTags).sort().map(t => {\n const colorClass = getTagColorClass(t);\n const styleVars = 'style=\"--tag-bg:var(--' + colorClass + '-bg); --tag-fg:var(--' + colorClass + '-fg);\"';\n return \\`\n <label class=\"pill-checkbox colored-tag\" \\${styleVars}>\n <input type=\"checkbox\" value=\"\\${t}\" onchange=\"updateFilters()\">\n <span>\\${t}</span>\n </label>\n \\`;\n }).join('');\n }\n }\n\n window.updateFilters = function() {\n filters.search = elSearch.value.toLowerCase();\n filters.methods.clear();\n elMethodFilters.querySelectorAll('input:checked').forEach(cb => filters.methods.add(cb.value));\n filters.tags.clear();\n elTagFilters.querySelectorAll('input:checked').forEach(cb => filters.tags.add(cb.value));\n render();\n }\n\n // Core Render Logic\n function render() {\n // 1. Filter Data\n const filtered = leaves.filter(leaf => {\n const m = (leaf.method || 'GET').toUpperCase();\n const t = (leaf.cfg && leaf.cfg.tags) ? leaf.cfg.tags : [];\n const path = (leaf.path || '').toLowerCase();\n const summary = (leaf.cfg && leaf.cfg.summary || '').toLowerCase();\n\n if (!filters.methods.has(m)) return false;\n if (filters.tags.size > 0 && !t.some(tag => filters.tags.has(tag))) return false;\n if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;\n\n return true;\n });\n\n if (filtered.length === 0) {\n elRouteList.innerHTML = '<div class=\"empty-message\">No endpoints match your filters.</div>';\n elOverview.innerHTML = '';\n return;\n }\n\n // 2. Group Data\n const groups = {};\n filtered.forEach(leaf => {\n const gName = (leaf.cfg && leaf.cfg.docsGroup) ? leaf.cfg.docsGroup : 'UNGROUPED';\n if (!groups[gName]) groups[gName] = [];\n groups[gName].push(leaf);\n });\n\n const sortedGroupNames = Object.keys(groups).sort(sortGroups);\n\n // 3. Render Overview Chips (Inside Sticky Header)\n // Note: We include a small label \"GROUPS:\" or similar if needed, or just chips.\n elOverview.innerHTML = '<span class=\"overview-label\">JUMP TO:</span>' + sortedGroupNames.map(gName => \\`\n <a href=\"#group-\\${gName}\" class=\"group-chip\" onclick=\"scrollToGroup(event, '\\${gName}')\">\n \\${gName}\n </a>\n \\`).join('');\n\n // 4. Render Groups & Cards\n const html = sortedGroupNames.map(gName => {\n const routes = groups[gName];\n // Sort Routes: Path ASC, then Method\n routes.sort((a, b) => {\n const pA = a.path || '';\n const pB = b.path || '';\n if (pA < pB) return -1;\n if (pA > pB) return 1;\n return (a.method || '').localeCompare(b.method || '');\n });\n\n return \\`\n <div class=\"api-group\" id=\"group-\\${gName}\">\n <div class=\"group-header\">\n <h2 class=\"group-title\">\\${gName}</h2>\n <div class=\"group-actions\">\n <button onclick=\"toggleGroup('\\${gName}', true)\">Expand All</button>\n <button onclick=\"toggleGroup('\\${gName}', false)\">Collapse All</button>\n </div>\n </div>\n <div class=\"cards-list\">\n \\${routes.map(renderCard).join('')}\n </div>\n </div>\n \\`;\n }).join('');\n\n elRouteList.innerHTML = html;\n }\n\n // Card Renderer\n function renderCard(leaf) {\n const method = (leaf.method || 'GET').toUpperCase();\n const path = leaf.path || '/';\n const cfg = leaf.cfg || {};\n const summary = cfg.summary || '';\n const description = cfg.description || '';\n const tags = cfg.tags || [];\n\n // Header Construction: Method | Path | Tags | Spacer | Chevron\n const tagBadges = tags.map(t => {\n const cClass = getTagColorClass(t);\n return \\`<span class=\"status-badge \\${cClass}\">\\${t}</span>\\`;\n }).join('');\n\n const headerHtml = \\`\n <div class=\"card-header\">\n <span class=\"method-badge m-\\${method}\">\\${method}</span>\n\n <div class=\"path-container\" onclick=\"copyText(event, '\\${path}')\" title=\"Click to copy path\">\n <span class=\"path-text\">\\${path}</span>\n <span class=\"copy-icon\">📋</span>\n </div>\n\n <div class=\"tags-container\">\n \\${tagBadges}\n </div>\n\n <div class=\"header-spacer\"></div>\n <div class=\"expand-icon\">▼</div>\n </div>\n \\`;\n\n // Body Construction (Preserved structure)\n let contentHtml = \\`<div class=\"section-block\"><div class=\"summary-text\">\n <strong>\\${escape(summary)}</strong>\n \\${description ? '<br><span style=\"font-size:12px; opacity:0.7\">' + escape(description) + '</span>' : ''}\n </div></div>\\`;\n\n if (cfg.paramsSchema || cfg.querySchema) {\n contentHtml += '<div class=\"section-block\"><div class=\"section-title\">Parameters</div>';\n if (cfg.paramsSchema) contentHtml += renderSchemaTable(cfg.paramsSchema, 'Path Parameters');\n if (cfg.querySchema) contentHtml += renderSchemaTable(cfg.querySchema, 'Query Parameters');\n contentHtml += '</div>';\n }\n\n if (cfg.bodySchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Request Body \\${cfg.hasBody ? '' : '(Optional)'}</div>\n \\${renderSchemaTable(cfg.bodySchema)}\n </div>\n \\`;\n }\n\n if (cfg.outputSchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Response Schema</div>\n \\${renderSchemaTable(cfg.outputSchema)}\n </div>\n \\`;\n }\n\n return \\`\n <article class=\"endpoint-card\" data-expanded=\"false\" onclick=\"toggleCard(this, event)\">\n \\${headerHtml}\n <div class=\"card-body\" onclick=\"event.stopPropagation()\">\n \\${contentHtml}\n </div>\n </article>\n \\`;\n }\n\n function renderSchemaTable(node, title) {\n let rows = '';\n if (node.kind === 'object' && node.properties) {\n Object.keys(node.properties).forEach(key => {\n const prop = node.properties[key];\n const reqClass = prop.optional ? 'req-false' : 'req-true';\n const reqText = prop.optional ? 'OPT' : 'REQ';\n rows += \\`\n <tr>\n <td class=\"col-name\">\\${key}</td>\n <td class=\"col-type\">\\${getTypeLabel(prop)}</td>\n <td><span class=\"req-badge \\${reqClass}\">\\${reqText}</span></td>\n <td>\\${escape(prop.description || '')}</td>\n </tr>\n \\`;\n });\n } else {\n rows = \\`<tr><td colspan=\"4\">Type: \\${getTypeLabel(node)}</td></tr>\\`;\n }\n return \\`\n \\${title ? '<div style=\"font-size:11px; font-weight:600; margin-bottom:4px; color:#64748b\">' + title + '</div>' : ''}\n <table class=\"schema-table\">\\${rows}</table>\n \\`;\n }\n\n function getTypeLabel(node) {\n if (!node) return 'any';\n if (node.kind === 'array') return \\`\\${getTypeLabel(node.element)}[]\\`;\n if (node.enumValues) return \\`enum(\\${node.enumValues.join('|')})\\`;\n return node.kind || 'any';\n }\n\n function escape(str) {\n if (!str) return '';\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n window.toggleCard = function(card, e) {\n if (e.target.closest('.path-container')) return; // Don't toggle on path click\n const isExpanded = card.getAttribute('data-expanded') === 'true';\n card.setAttribute('data-expanded', !isExpanded);\n };\n\n window.toggleGroup = function(gName, expand) {\n const group = document.getElementById('group-' + gName);\n if (!group) return;\n group.querySelectorAll('.endpoint-card').forEach(c => c.setAttribute('data-expanded', expand));\n };\n\n window.copyText = function(e, text) {\n e.stopPropagation();\n navigator.clipboard.writeText(text);\n // Could add visual feedback here\n };\n\n window.scrollToGroup = function(e, gName) {\n e.preventDefault();\n const el = document.getElementById('group-' + gName);\n if(el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });\n }\n\n function setupGlobalListeners() {\n elSearch.addEventListener('input', updateFilters);\n }\n\n init();\n})();\n`;\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n const leavesJson = JSON.stringify(leaves.map(serializeLeaf));\n const nonceAttr = options.cspNonce ? ` nonce=\"${options.cspNonce}\"` : \"\";\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Documentation</title>\n\n <style${nonceAttr}>${CSS_STYLES}</style>\n ${options.inlineCss ? `<style${nonceAttr}>${options.inlineCss}</style>` : \"\"}\n</head>\n<body>\n <div class=\"page\">\n <header class=\"header\">\n <div class=\"header-title\">\n <h1>API Reference</h1>\n </div>\n </header>\n\n <div class=\"controls-container\">\n <div class=\"filters-row\">\n\n <div class=\"left-column\">\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Search</div>\n\n <div class=\"search-box\">\n <span class=\"search-icon\">🔍</span>\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Filter endpoints...\">\n </div>\n </div>\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Methods</div>\n <div class=\"checkbox-group\" id=\"methodFilters\"></div>\n </div>\n </div>\n\n <div class=\"filter-group tag-filters-container\" id=\"tagFilterContainer\">\n <div class=\"filter-label\">Tags</div>\n <div class=\"checkbox-group\" id=\"tagFilters\"></div>\n </div>\n </div>\n\n <div class=\"overview-row\" id=\"overviewList\"></div>\n </div>\n\n <main id=\"routeList\">\n <div style=\"text-align:center; padding:40px; color:#64748b\">Loading API definition...</div>\n </main>\n </div>\n\n <script id=\"leaf-data\" type=\"application/json\">${leavesJson}</script>\n <script${nonceAttr}>${DOCS_JS}</script>\n ${options.inlineClientScript ? `<script${nonceAttr}>${options.inlineClientScript}</script>` : \"\"}\n</body>\n</html>\n `;\n}\n","// renderLeafDocsHTML.ts\nimport { AnyLeaf } from \"../core/routesV3.core\";\nimport { renderLeafDocsHTML as LeafDocsPage } from \"./LeafDocsPage\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n return LeafDocsPage(leaves, options)\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAubnB,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiTT,SAAS,mBACd,QACA,UAAyB,CAAC,GAClB;AACR,QAAM,aAAa,KAAK,UAAU,OAAO,IAAI,aAAa,CAAC;AAC3D,QAAM,YAAY,QAAQ,WAAW,WAAW,QAAQ,QAAQ,MAAM;AAEtE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQC,SAAS,IAAI,UAAU;AAAA,IAC7B,QAAQ,YAAY,SAAS,SAAS,IAAI,QAAQ,SAAS,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDA4C3B,UAAU;AAAA,WAClD,SAAS,IAAI,OAAO;AAAA,IAC3B,QAAQ,qBAAqB,UAAU,SAAS,IAAI,QAAQ,kBAAkB,cAAc,EAAE;AAAA;AAAA;AAAA;AAIlG;;;AC7yBO,SAASC,oBACd,QACA,UAAyB,CAAC,GAClB;AACR,SAAO,mBAAa,QAAQ,OAAO;AACrC;","names":["mergeSchemas","renderLeafDocsHTML","import_zod","mergeSchemas","z","renderLeafDocsHTML"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1158,6 +1158,7 @@ var DOCS_JS = `
|
|
|
1158
1158
|
`;
|
|
1159
1159
|
function renderLeafDocsHTML(leaves, options = {}) {
|
|
1160
1160
|
const leavesJson = JSON.stringify(leaves.map(serializeLeaf));
|
|
1161
|
+
const nonceAttr = options.cspNonce ? ` nonce="${options.cspNonce}"` : "";
|
|
1161
1162
|
return `
|
|
1162
1163
|
<!DOCTYPE html>
|
|
1163
1164
|
<html lang="en">
|
|
@@ -1165,8 +1166,9 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1165
1166
|
<meta charset="UTF-8">
|
|
1166
1167
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1167
1168
|
<title>API Documentation</title>
|
|
1168
|
-
|
|
1169
|
-
${
|
|
1169
|
+
|
|
1170
|
+
<style${nonceAttr}>${CSS_STYLES}</style>
|
|
1171
|
+
${options.inlineCss ? `<style${nonceAttr}>${options.inlineCss}</style>` : ""}
|
|
1170
1172
|
</head>
|
|
1171
1173
|
<body>
|
|
1172
1174
|
<div class="page">
|
|
@@ -1176,24 +1178,24 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1176
1178
|
</div>
|
|
1177
1179
|
</header>
|
|
1178
1180
|
|
|
1179
|
-
<div class="controls-container">
|
|
1181
|
+
<div class="controls-container">
|
|
1180
1182
|
<div class="filters-row">
|
|
1181
1183
|
|
|
1182
1184
|
<div class="left-column">
|
|
1183
1185
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
+
<div class="filter-group method-filters-container">
|
|
1187
|
+
<div class="filter-label">Search</div>
|
|
1186
1188
|
|
|
1187
1189
|
<div class="search-box">
|
|
1188
1190
|
<span class="search-icon">\u{1F50D}</span>
|
|
1189
1191
|
<input type="text" id="searchInput" class="search-input" placeholder="Filter endpoints...">
|
|
1190
1192
|
</div>
|
|
1191
|
-
|
|
1193
|
+
</div>
|
|
1192
1194
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1195
|
+
<div class="filter-group method-filters-container">
|
|
1196
|
+
<div class="filter-label">Methods</div>
|
|
1197
|
+
<div class="checkbox-group" id="methodFilters"></div>
|
|
1198
|
+
</div>
|
|
1197
1199
|
</div>
|
|
1198
1200
|
|
|
1199
1201
|
<div class="filter-group tag-filters-container" id="tagFilterContainer">
|
|
@@ -1211,8 +1213,8 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1211
1213
|
</div>
|
|
1212
1214
|
|
|
1213
1215
|
<script id="leaf-data" type="application/json">${leavesJson}</script>
|
|
1214
|
-
<script>${DOCS_JS}</script>
|
|
1215
|
-
${options.inlineClientScript ? `<script>${options.inlineClientScript}</script>` : ""}
|
|
1216
|
+
<script${nonceAttr}>${DOCS_JS}</script>
|
|
1217
|
+
${options.inlineClientScript ? `<script${nonceAttr}>${options.inlineClientScript}</script>` : ""}
|
|
1216
1218
|
</body>
|
|
1217
1219
|
</html>
|
|
1218
1220
|
`;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/crud/routesV3.crud.ts","../src/sockets/socket.index.ts","../src/docs/schemaIntrospection.ts","../src/docs/serializer.ts","../src/docs/LeafDocsPage.tsx","../src/docs/docs.ts"],"sourcesContent":["import { z } from 'zod';\nimport {\n AnyLeaf,\n Append,\n HttpMethod,\n Leaf,\n Merge,\n MergeArray,\n MethodCfg,\n NodeCfg,\n Prettify,\n} from './routesV3.core';\n\nconst defaultFeedQuerySchema = z.object({\n cursor: z.string().optional(),\n limit: z.coerce.number().min(1).max(100).default(20),\n});\n\nconst paginationField = defaultFeedQuerySchema.optional().default(defaultFeedQuerySchema.parse({}));\ntype PaginationField = typeof paginationField;\n\ntype ZodTypeAny = z.ZodTypeAny;\ntype ParamZod<Name extends string, S extends ZodTypeAny> = z.ZodObject<{ [K in Name]: S }>;\ntype ZodArrayAny = z.ZodArray<ZodTypeAny>;\ntype ZodObjectAny = z.ZodObject<any>;\ntype AnyZodObject = z.ZodObject<any>;\ntype MergedParamsResult<PS, Name extends string, P extends ZodTypeAny> = PS extends ZodTypeAny\n ? z.ZodIntersection<PS, ParamZod<Name, P>>\n : ParamZod<Name, P>;\n\nconst defaultFeedOutputSchema = z.object({\n items: z.array(z.unknown()),\n nextCursor: z.string().optional(),\n});\n\nfunction augmentFeedQuerySchema<Q extends ZodTypeAny | undefined>(schema: Q) {\n if (schema && !(schema instanceof z.ZodObject)) {\n console.warn('Feed queries must be a ZodObject; default pagination applied.');\n return z.object({\n pagination: paginationField,\n });\n }\n\n const base = (schema as ZodObjectAny) ?? z.object({});\n return base.extend({\n pagination: paginationField,\n });\n}\n\nfunction augmentFeedOutputSchema<O extends ZodTypeAny | undefined>(schema: O) {\n if (!schema) return defaultFeedOutputSchema;\n if (schema instanceof z.ZodArray) {\n return z.object({\n items: schema,\n nextCursor: z.string().optional(),\n });\n }\n if (schema instanceof z.ZodObject) {\n const shape = (schema as any).shape ? (schema as any).shape : (schema as any)._def?.shape?.();\n const hasItems = Boolean(shape?.items);\n if (hasItems) {\n return schema.extend({ nextCursor: z.string().optional() });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nfunction mergeSchemas<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodTypeAny;\nfunction mergeSchemas<A extends ZodTypeAny>(a: A, b: undefined): A;\nfunction mergeSchemas<B extends ZodTypeAny>(a: undefined, b: B): B;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined): ZodTypeAny | undefined;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined) {\n if (a && b) return z.intersection(a as any, b as any);\n return (a ?? b) as ZodTypeAny | undefined;\n}\n\ntype FeedQuerySchema<C extends MethodCfg> = z.ZodObject<any>;\n\ntype FeedOutputSchema<C extends MethodCfg> = C['outputSchema'] extends ZodArrayAny\n ? z.ZodObject<{\n items: C['outputSchema'];\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : C['outputSchema'] extends ZodTypeAny\n ? z.ZodObject<{\n items: z.ZodArray<C['outputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : typeof defaultFeedOutputSchema;\n\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>;\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true ? { feed: true } : { feed?: boolean };\n\ntype AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<\n infer Shape\n>\n ? z.ZodObject<Shape & { pagination: PaginationField }>\n : z.ZodObject<{ pagination: PaginationField }>;\n\ntype FeedQueryField<C extends MethodCfg> = {\n querySchema: AddPaginationToQuery<\n C['querySchema'] extends AnyZodObject ? C['querySchema'] : undefined\n >;\n};\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodTypeAny\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined };\n\ntype FeedOutputField<C extends MethodCfg> = { outputSchema: FeedOutputSchema<C> };\n\ntype NonFeedOutputField<C extends MethodCfg> = C['outputSchema'] extends ZodTypeAny\n ? { outputSchema: C['outputSchema'] }\n : { outputSchema?: undefined };\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodTypeAny\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS };\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & FeedOutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & NonFeedOutputField<C> & ParamsField<C, PS>;\n\ntype EffectiveCfg<C extends MethodCfg, PS> = Prettify<\n Merge<MethodCfg, BaseMethodCfg<C> & EffectiveFeedFields<C, PS>>\n>;\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<M, Base, Merge<I, EffectiveCfg<C, PS>>>;\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodTypeAny | undefined,\n> {\n // --- structure ---\n /**\n * Enter or define a static child segment.\n * Optionally accepts extra config and/or a builder to populate nested routes.\n * @param name Child segment literal (no leading slash).\n * @param cfg Optional node configuration to merge for descendants.\n * @param builder Callback to produce leaves for the child branch.\n */\n sub<Name extends string, J extends NodeCfg>(\n name: Name,\n cfg?: J,\n ): Branch<`${Base}/${Name}`, Acc, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, J extends NodeCfg | undefined, R extends readonly AnyLeaf[]>(\n name: Name,\n cfg: J,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], Merge<I, NonNullable<J>>, PS>) => R,\n ): Branch<Base, Append<Acc, R[number]>, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, R extends readonly AnyLeaf[]>(\n name: Name,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], I, PS>) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, PS>;\n\n // --- parameterized segment (single signature) ---\n /**\n * Introduce a `:param` segment and merge its schema into downstream leaves.\n * @param name Parameter key (without leading colon).\n * @param paramsSchema Zod schema for the parameter value.\n * @param builder Callback that produces leaves beneath the parameterized segment.\n */\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base}/:${Name}`,\n readonly [],\n I,\n MergedParamsResult<PS, Name, P>\n >,\n ) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, MergedParamsResult<PS, Name, P>>;\n\n // --- flags inheritance ---\n /**\n * Merge additional node configuration that subsequent leaves will inherit.\n * @param cfg Partial node configuration.\n */\n with<J extends NodeCfg>(cfg: J): Branch<Base, Acc, Merge<I, J>, PS>;\n\n // --- methods (return Branch to keep chaining) ---\n /**\n * Register a GET leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n get<C extends MethodCfg>(\n cfg: C,\n ): Branch<\n Base,\n Append<Acc, Prettify<BuiltLeaf<'get', Base, I, C, PS>>>,\n I,\n PS\n >;\n\n /**\n * Register a POST leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n post<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'post', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PUT leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n put<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'put', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PATCH leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n patch<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'patch', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a DELETE leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n delete<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'delete', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n // --- finalize this subtree ---\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>;\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited?: I,\n): Branch<Base, readonly [], I, undefined> {\n const rootBase = base;\n const rootInherited: NodeCfg = { ...(inherited as NodeCfg) };\n\n function makeBranch<Base2 extends string, I2 extends NodeCfg, PS2 extends ZodTypeAny | undefined>(\n base2: Base2,\n inherited2: I2,\n mergedParamsSchema?: PS2,\n ) {\n const stack: AnyLeaf[] = [];\n let currentBase: string = base2;\n let inheritedCfg: NodeCfg = { ...(inherited2 as NodeCfg) };\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2;\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n // If the method didn’t provide a paramsSchema, inject the active merged one.\n const effectiveParamsSchema = (cfg.paramsSchema ?? currentParamsSchema) as PS2 | undefined;\n const effectiveQuerySchema =\n cfg.feed === true ? augmentFeedQuerySchema(cfg.querySchema) : cfg.querySchema;\n const effectiveOutputSchema =\n cfg.feed === true ? augmentFeedOutputSchema(cfg.outputSchema) : cfg.outputSchema;\n\n const fullCfg = (\n effectiveParamsSchema\n ? {\n ...inheritedCfg,\n ...cfg,\n paramsSchema: effectiveParamsSchema,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n : {\n ...inheritedCfg,\n ...cfg,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n ) as any;\n\n const leaf = {\n method,\n path: currentBase as Base2,\n cfg: fullCfg,\n } as const;\n\n stack.push(leaf as unknown as AnyLeaf);\n\n // Return same runtime obj, but with Acc including this new leaf\n return api as unknown as Branch<Base2, Append<readonly [], typeof leaf>, I2, PS2>;\n }\n\n const api: any = {\n // compose a plain subpath (optional cfg) with optional builder\n sub<Name extends string, J extends NodeCfg | undefined = undefined>(\n name: Name,\n cfgOrBuilder?: J | ((r: any) => readonly AnyLeaf[]),\n maybeBuilder?: (r: any) => readonly AnyLeaf[],\n ) {\n let cfg: NodeCfg | undefined;\n let builder: ((r: any) => readonly AnyLeaf[]) | undefined;\n\n if (typeof cfgOrBuilder === 'function') {\n builder = cfgOrBuilder as any;\n } else {\n cfg = cfgOrBuilder as NodeCfg | undefined;\n builder = maybeBuilder;\n }\n\n const childBase = `${currentBase}/${name}` as const;\n const childInherited = { ...inheritedCfg, ...(cfg ?? {}) } as Merge<I2, NonNullable<J>>;\n\n if (builder) {\n // params schema PS2 flows through unchanged on plain sub\n const child = makeBranch(childBase, childInherited, currentParamsSchema);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n typeof childInherited,\n PS2\n >;\n } else {\n currentBase = childBase;\n inheritedCfg = childInherited;\n return api as Branch<`${Base2}/${Name}`, readonly [], typeof childInherited, PS2>;\n }\n },\n\n // the single param function: name + schema + builder\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base2}/:${Name}`,\n readonly [],\n I2,\n MergedParamsResult<PS2, Name, P>\n >,\n ) => R,\n ) {\n const childBase = `${currentBase}/:${name}` as const;\n // Wrap the value schema under the param name before merging with inherited params schema\n const paramObj: ParamZod<Name, P> = z.object({ [name]: paramsSchema } as Record<Name, P>);\n const childParams = (currentParamsSchema\n ? mergeSchemas(currentParamsSchema, paramObj)\n : paramObj) as MergedParamsResult<PS2, Name, P>;\n const child = makeBranch(childBase, inheritedCfg as I2, childParams);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n I2,\n MergedParamsResult<PS2, Name, P>\n >;\n },\n\n with<J extends NodeCfg>(cfg: J) {\n inheritedCfg = { ...inheritedCfg, ...cfg };\n return api as Branch<Base2, readonly [], Merge<I2, J>, PS2>;\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg);\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false });\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false });\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false });\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false });\n },\n\n done() {\n return stack;\n },\n };\n\n return api as Branch<Base2, readonly [], I2, PS2>;\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(rootBase, rootInherited, undefined);\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S];\n};\n","import { z, ZodType } from 'zod';\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string;\n /** Maximum number of files accepted for this field. */\n maxCount: number;\n};\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport type NodeCfg = {\n /** @deprecated. Does nothing. */\n authenticated?: boolean;\n};\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType;\n /** Zod schema describing the query string. */\n querySchema?: ZodType;\n /** Zod schema describing path params (overrides inferred params). */\n paramsSchema?: ZodType;\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType;\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[];\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean;\n\n /** Optional human-readable description for docs/debugging. */\n description?: string;\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string;\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string;\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[];\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean;\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated';\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean;\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>;\n};\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<M extends HttpMethod, P extends string, C extends MethodCfg> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M;\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P;\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>;\n};\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>;\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>;\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X];\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<A extends readonly unknown[], B extends readonly unknown[]> = [\n ...A,\n ...B,\n];\n\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never;\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S];\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>;\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never ? never : Record<ExtractParamNames<Path>, string | number>;\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(path: Path, params: ExtractParamsFromPath<Path>) {\n if (!params) return path;\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k];\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`);\n return String(v);\n });\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P];\nexport function buildCacheKey<L extends AnyLeaf>(args: {\n leaf: L;\n params?: ExtractParamsFromPath<L['path']>;\n query?: InferQuery<L>;\n}) {\n let p = args.leaf.path;\n if (args.params) {\n p = compilePath<L['path']>(p, args.params);\n }\n return [args.leaf.method, ...(p.split('/').filter(Boolean) as SplitPath<typeof p>), args.query ?? {}] as const;\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends ZodType\n ? z.infer<L['cfg']['paramsSchema']>\n : ExtractParamsFromPath<L['path']>;\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends ZodType\n ? z.infer<L['cfg']['querySchema']>\n : never;\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends ZodType\n ? z.infer<L['cfg']['bodySchema']>\n : never;\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\nexport type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends ZodType\n ? z.infer<L['cfg']['outputSchema']>\n : unknown;\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n","// TODO:\n// * use this as a transform that infers the types from Zod, to only pass the types around instead of the full schemas -> make converters that go both ways (data to schema, schema to data)\n// * consider moving to core package\n// * update server and client side to use this type instead of raw zod schemas \nimport { AnyLeaf, HttpMethod, Prettify } from './routesV3.core';\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeaf> = L extends AnyLeaf\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never;\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeaf[]> = KeyOf<Leaves[number]>;\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<Leaves extends readonly AnyLeaf[], K extends string> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/**\n * Freeze a leaf tuple into a registry with typed key lookups.\n * @param leaves Readonly tuple of leaves produced by the builder DSL.\n * @returns Registry containing the leaves array and a `byKey` lookup map.\n */\nexport function finalize<const L extends readonly AnyLeaf[]>(leaves: L) {\n type Keys = KeysOf<L>;\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> };\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey;\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:');\n (Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k];\n logger.system(`- ${k}`);\n });\n };\n\n return { all: leaves, byKey, log };\n}\n\n/** Nominal type alias for a finalized registry. */\nexport type Registry<R extends ReturnType<typeof finalize>> = R;\n\ntype FilterRoute<\n T extends readonly AnyLeaf[],\n F extends string,\n Acc extends readonly AnyLeaf[] = [],\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc;\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S;\n\ntype Routes<T extends readonly AnyLeaf[], F extends string> = FilterRoute<T, F>;\ntype ByKey<\n T extends readonly AnyLeaf[],\n Acc extends Record<string, AnyLeaf> = {},\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? ByKey<Rest, Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }>\n : Acc;\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<T extends readonly AnyLeaf[], F extends string> = {\n byKey: ByKey<Routes<T, F>>;\n all: Routes<T, F>;\n};\n","// routesV3.crud.ts\n// -----------------------------------------------------------------------------\n// Add a first-class .crud() helper to the routesV3 DSL, without touching the\n// core builder. This file:\n// 1) Declares types + module augmentation to extend Branch<...> with .crud\n// 2) Provides a runtime decorator `withCrud(...)` that installs the method\n// 3) Exports a convenience `resourceWithCrud(...)` wrapper (optional)\n// -----------------------------------------------------------------------------\n\nimport { z, ZodType } from 'zod';\nimport {\n resource as baseResource,\n type Branch as _Branch, // import type so we can augment it\n} from '../core/routesV3.builder';\nimport { type AnyLeaf, type Leaf, type NodeCfg } from '../core/routesV3.core';\n\n// -----------------------------------------------------------------------------\n// Small utilities (runtime + types)\n// -----------------------------------------------------------------------------\n/** Default cursor pagination used by list (GET collection). */\nexport const CrudDefaultPagination = z.object({\n limit: z.coerce.number().min(1).max(100).default(20),\n cursor: z.string().optional(),\n});\nconst CrudPaginationField = CrudDefaultPagination.optional().default(CrudDefaultPagination.parse({}));\ntype CrudPaginationField = typeof CrudPaginationField;\ntype AnyCrudZodObject = z.ZodObject<any>;\ntype AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape>\n ? z.ZodObject<Shape & { pagination: CrudPaginationField }>\n : z.ZodObject<{ pagination: CrudPaginationField }>;\n\n/**\n * Merge two Zod schemas at runtime; matches the typing pattern used in builder.\n * @param a Existing params schema inherited from parent branches.\n * @param b Schema introduced at the current branch.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType | undefined, B extends ZodType | undefined>(\n a: A,\n b: B,\n): A extends ZodType ? (B extends ZodType ? ReturnType<typeof z.intersection<A, B>> : A) : B {\n if (a && b) return z.intersection(a as any, b as any) as any;\n return (a ?? b) as any;\n}\n\n/** Build { [Name]: S } Zod object at the type-level. */\nexport type ParamSchema<Name extends string, S extends ZodType> = z.ZodObject<{\n [K in Name]: S;\n}>;\n\n/** Inject active params schema into a leaf cfg (to match your Leaf typing). */\ntype WithParams<I, P> = P extends ZodType ? Omit<I, 'paramsSchema'> & { paramsSchema: P } : I;\n\n/** Resolve boolean flag: default D unless explicitly false. */\ntype Flag<E, D extends boolean> = E extends false ? false : D;\n\n// -----------------------------------------------------------------------------\n// CRUD config (few knobs, with DX comments)\n// -----------------------------------------------------------------------------\n\n/** Toggle individual CRUD methods; defaults enable all (create/update require body). */\nexport type CrudEnable = {\n /** GET /collection (feed). Defaults to true. */ list?: boolean;\n /** POST /collection. Defaults to true if `create` provided. */ create?: boolean;\n /** GET /collection/:id. Defaults to true. */ read?: boolean;\n /** PATCH /collection/:id. Defaults to true if `update` provided. */ update?: boolean;\n /** DELETE /collection/:id. Defaults to true. */ remove?: boolean;\n};\n\n/** Collection GET config. */\nexport type CrudListCfg = {\n /** Query schema (defaults to cursor pagination). */\n querySchema?: ZodType;\n /** Output schema (defaults to { items: Item[], nextCursor? }). */\n outputSchema?: ZodType;\n /** Optional description string. */\n description?: string;\n};\n\n/** Collection POST config. */\nexport type CrudCreateCfg = {\n /** Body schema for creating an item. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item GET config. */\nexport type CrudReadCfg = {\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item PATCH config. */\nexport type CrudUpdateCfg = {\n /** Body schema for partial updates. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item DELETE config. */\nexport type CrudRemoveCfg = {\n /** Output schema (defaults to { ok: true }). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/**\n * CRUD config for `.crud(\"resource\", cfg, extras?)`.\n *\n * It synthesizes `:[resourceName]Id` as the param key and uses `paramSchema` for the value.\n * Prefer passing a **value schema** (e.g. `z.string().uuid()`).\n * As a convenience, a ZodObject of shape `{ [resourceName]Id: schema }` is accepted at runtime too.\n */\nexport type CrudCfg<Name extends string = string> = {\n /**\n * Zod schema for the ID *value* (e.g. `z.string().uuid()`).\n * The parameter key is always `:${name}Id`.\n * (If you pass `z.object({ [name]Id: ... })`, it's accepted at runtime.)\n */\n paramSchema: ZodType;\n\n /**\n * The single-item output shape used by read/create/update by default.\n * List defaults to `{ items: z.array(itemOutputSchema), nextCursor? }`.\n */\n itemOutputSchema: ZodType;\n\n /** Per-method toggles (all enabled by default; create/update require bodies). */\n enable?: CrudEnable;\n\n /** GET /collection configuration. */ list?: CrudListCfg;\n /** POST /collection configuration (enables create). */ create?: CrudCreateCfg;\n /** GET /collection/:id configuration. */ read?: CrudReadCfg;\n /** PATCH /collection/:id configuration (enables update). */ update?: CrudUpdateCfg;\n /** DELETE /collection/:id configuration. */ remove?: CrudRemoveCfg;\n};\n\n// -----------------------------------------------------------------------------\n// Type plumbing to expose generated leaves (so finalize().byKey sees them)\n// -----------------------------------------------------------------------------\n\ntype CrudLeavesTuple<\n Base extends string,\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n> = `${Name}Id` extends infer IdName extends string\n ? ParamSchema<IdName, C['paramSchema']> extends infer IdParamZ extends ZodType\n ? [\n // GET /collection\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['list'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n feed: true;\n querySchema: C['list'] extends CrudListCfg\n ? AddCrudPagination<\n C['list']['querySchema'] extends AnyCrudZodObject\n ? C['list']['querySchema']\n : undefined\n >\n : AddCrudPagination<undefined>;\n outputSchema: C['list'] extends CrudListCfg\n ? C['list']['outputSchema'] extends ZodType\n ? C['list']['outputSchema']\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []),\n\n // POST /collection\n ...((C['create'] extends CrudCreateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['create'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'post',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['create'] extends CrudCreateCfg\n ? C['create']['bodySchema']\n : never;\n outputSchema: C['create'] extends CrudCreateCfg\n ? C['create']['outputSchema'] extends ZodType\n ? C['create']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []\n : []),\n\n // GET /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['read'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['read'] extends CrudReadCfg\n ? C['read']['outputSchema'] extends ZodType\n ? C['read']['outputSchema']\n : C['itemOutputSchema']\n : C['itemOutputSchema'];\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n\n // PATCH /collection/:id\n ...((C['update'] extends CrudUpdateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['update'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'patch',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['update'] extends CrudUpdateCfg\n ? C['update']['bodySchema']\n : never;\n outputSchema: C['update'] extends CrudUpdateCfg\n ? C['update']['outputSchema'] extends ZodType\n ? C['update']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []\n : []),\n\n // DELETE /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['remove'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'delete',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['remove'] extends CrudRemoveCfg\n ? C['remove']['outputSchema'] extends ZodType\n ? C['remove']['outputSchema']\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n ]\n : never\n : never;\n\n/** Merge generated leaves + extras into the branch accumulator. */\ntype CrudResultAcc<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[],\n> = [...Acc, ...CrudLeavesTuple<Base, I, PS, Name, C>, ...Extras];\n\n// -----------------------------------------------------------------------------\n// Module augmentation: add the typed `.crud(...)` to Branch<...>\n// -----------------------------------------------------------------------------\n\ndeclare module './../core/routesV3.builder' {\n interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n > {\n /**\n * Generate opinionated CRUD endpoints for a collection at `/.../<name>`\n * with an item at `/.../<name>/:<name>Id`.\n *\n * Creates (subject to `enable` + presence of create/update bodies):\n * - GET /<name> (feed list)\n * - POST /<name> (create)\n * - GET /<name>/:<name>Id (read item)\n * - PATCH /<name>/:<name>Id (update item)\n * - DELETE /<name>/:<name>Id (remove item)\n *\n * The `extras` callback receives live builders at both collection and item\n * levels so you can add custom subroutes; their leaves are included in the\n * returned Branch type.\n */\n crud<\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(\n name: Name,\n cfg: C,\n extras?: (ctx: {\n /** Builder at `/.../<name>` (collection scope). */\n collection: _Branch<`${Base}/${Name}`, readonly [], I, PS>;\n /** Builder at `/.../<name>/:<name>Id` (item scope). */\n item: _Branch<\n `${Base}/${Name}/:${`${Name}Id`}`,\n readonly [],\n I,\n ReturnType<typeof mergeSchemas<PS, ParamSchema<`${Name}Id`, C['paramSchema']>>>\n >;\n }) => Extras,\n ): _Branch<Base, CrudResultAcc<Base, Acc, I, PS, Name, C, Extras>, I, PS>;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Runtime: install .crud on any Branch via decorator\n// -----------------------------------------------------------------------------\n\n/**\n * Decorate a Branch instance so `.crud(...)` is available at runtime.\n * Tip: wrap the root: `withCrud(resource('/v1'))`.\n * @param branch Branch returned by `resource(...)`.\n * @returns Same branch with `.crud(...)` installed.\n */\nexport function withCrud<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n>(branch: _Branch<Base, Acc, I, PS>): _Branch<Base, Acc, I, PS> {\n const b = branch as any;\n if (typeof b.crud === 'function') return branch;\n\n b.crud = function <\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(this: any, name: Name, cfg: C, extras?: (ctx: { collection: any; item: any }) => Extras) {\n // Build inside a sub-branch at `/.../<name>` and harvest its leaves.\n return this.sub(name, (collection: any) => {\n const idKey = `${name}Id`;\n\n // Accept a value schema (preferred); but if a z.object({[name]Id: ...}) is provided, use it.\n const s = cfg.paramSchema;\n const isObj =\n s &&\n typeof (s as any)._def === 'object' &&\n (s as any)._def.typeName === 'ZodObject' &&\n typeof (s as any).shape === 'function';\n\n const paramValueSchema =\n isObj && (s as any).shape()[idKey] ? ((s as any).shape()[idKey] as ZodType) : (s as ZodType);\n\n const itemOut = cfg.itemOutputSchema;\n const listOut =\n cfg.list?.outputSchema ??\n z.object({\n items: z.array(itemOut),\n nextCursor: z.string().optional(),\n });\n\n const want = {\n list: cfg.enable?.list !== false,\n create: cfg.enable?.create !== false && !!cfg.create?.bodySchema,\n read: cfg.enable?.read !== false,\n update: cfg.enable?.update !== false && !!cfg.update?.bodySchema,\n remove: cfg.enable?.remove !== false,\n } as const;\n\n // Collection routes\n if (want.list) {\n collection.get({\n feed: true,\n querySchema: cfg.list?.querySchema ?? CrudDefaultPagination,\n outputSchema: listOut,\n description: cfg.list?.description ?? 'List',\n });\n }\n\n if (want.create) {\n collection.post({\n bodySchema: cfg.create!.bodySchema,\n outputSchema: cfg.create?.outputSchema ?? itemOut,\n description: cfg.create?.description ?? 'Create',\n });\n }\n\n // Item routes via :<name>Id\n collection.routeParameter(idKey as any, paramValueSchema as any, (item: any) => {\n if (want.read) {\n item.get({\n outputSchema: cfg.read?.outputSchema ?? itemOut,\n description: cfg.read?.description ?? 'Read',\n });\n }\n if (want.update) {\n item.patch({\n bodySchema: cfg.update!.bodySchema,\n outputSchema: cfg.update?.outputSchema ?? itemOut,\n description: cfg.update?.description ?? 'Update',\n });\n }\n if (want.remove) {\n item.delete({\n outputSchema: cfg.remove?.outputSchema ?? z.object({ ok: z.literal(true) }),\n description: cfg.remove?.description ?? 'Delete',\n });\n }\n\n // Give extras fully-capable builders (decorate so extras can call .crud again)\n if (extras) extras({ collection: withCrud(collection), item: withCrud(item) });\n\n return item.done();\n });\n\n return collection.done();\n });\n };\n\n return branch;\n}\n\n// -----------------------------------------------------------------------------\n// Optional convenience: re-export a resource() that already has .crud installed\n// -----------------------------------------------------------------------------\n\n/**\n * Drop-in replacement for `resource(...)` that returns a Branch with `.crud()` installed.\n * You can either use this or call `withCrud(resource(...))`.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Node configuration merged into every leaf.\n * @returns Branch builder that already includes the CRUD helper.\n */\nexport function resourceWithCrud<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited: I,\n) {\n return withCrud(baseResource(base, inherited));\n}\n\n// Re-export the original in case you want both styles from this module\nexport { baseResource as resource };\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod';\n\nexport type SocketSchema<Output =unknown> = z.ZodType & {\n __out: Output;\n};\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out;\n} ? Out : z.output<Schema>;\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny;\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out;\n};\n\nexport type EventMap = Record<string, SocketEvent<any>>;\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName = 'sys:connect' | 'sys:disconnect' | 'sys:reconnect' | 'sys:connect_error' | 'sys:ping' | 'sys:pong' | 'sys:room_join' | 'sys:room_leave';\nexport function defineSocketEvents<const C extends SocketConnectionConfig, const Schemas extends Record<string, {\n message: z.ZodTypeAny;\n}>>(config: C, events: Schemas): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>;\n };\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>;\n };\n};\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events };\n}\n\n\n\nexport type Payload<T extends EventMap, K extends keyof T> = T[K] extends SocketEvent<infer Out> ? Out : never;\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny;\n leaveMetaMessage: z.ZodTypeAny;\n pingPayload: z.ZodTypeAny;\n pongPayload: z.ZodTypeAny;\n};\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>;\n leaveMetaMessage: SocketSchema<any>;\n pingPayload: SocketSchema<any>;\n pongPayload: SocketSchema<any>;\n}","// schemaIntrospection.ts\nimport * as z from \"zod\";\n\nexport type SerializableSchemaNode = {\n kind: string; // \"object\" | \"string\" | \"number\" | ...\n optional?: boolean;\n nullable?: boolean;\n description?: string;\n\n // object\n properties?: Record<string, SerializableSchemaNode>;\n // array\n element?: SerializableSchemaNode;\n // union\n union?: SerializableSchemaNode[];\n // literal\n literal?: unknown;\n // enum\n enumValues?: string[];\n};\n\ntype ZodAny = z.ZodTypeAny;\n\n/**\n * Zod 3 uses `schema._def`, Zod 4 uses `schema._zod.def`.\n */\nfunction getDef(schema: unknown): any | undefined {\n if (!schema || typeof schema !== \"object\") return undefined;\n const anySchema = schema as any;\n return anySchema._zod?.def ?? anySchema._def;\n}\n\n/**\n * Try to get a human-readable description.\n * Zod 4: use metadata/registry; fallback to internal def.description.\n * Zod 3: only internal def.description exists.\n */\nfunction getDescription(schema: ZodAny): string | undefined {\n const anyZ: any = z as any;\n\n // Zod 4 global registry metadata, if present\n const registry = anyZ.globalRegistry?.get\n ? anyZ.globalRegistry.get(schema)\n : undefined;\n if (registry && typeof registry.description === \"string\") {\n return registry.description;\n }\n\n // Legacy / internal description\n const def = getDef(schema);\n if (def && typeof def.description === \"string\") {\n return def.description;\n }\n\n return undefined;\n}\n\n/**\n * Peel off wrappers (effects, optional, nullable, default) and\n * return the inner schema + flags.\n *\n * Supports:\n * - Zod 3: ZodEffects, ZodOptional, ZodNullable, ZodDefault\n * - Zod 4: ZodOptional, ZodNullable, ZodDefault\n */\nfunction unwrap(schema: ZodAny): {\n base: ZodAny;\n optional: boolean;\n nullable: boolean;\n} {\n let s: ZodAny = schema;\n let optional = false;\n let nullable = false;\n\n // Zod 3 only (undefined in Zod 4)\n const ZodEffectsCtor: any = (z as any).ZodEffects;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // Zod 3: ZodEffects wrapper\n if (ZodEffectsCtor && s instanceof ZodEffectsCtor) {\n const def = getDef(s) || {};\n const sourceType =\n typeof (s as any).sourceType === \"function\"\n ? (s as any).sourceType()\n : def.schema;\n if (!sourceType) break;\n s = sourceType;\n continue;\n }\n\n // Zod 3 + 4: optional/nullable/default wrappers\n if (s instanceof z.ZodOptional) {\n optional = true;\n const def = getDef(s);\n s = (def && def.innerType) || s; // innerType exists in both 3 & 4\n continue;\n }\n\n if (s instanceof z.ZodNullable) {\n nullable = true;\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n if (s instanceof z.ZodDefault) {\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n break;\n }\n\n return { base: s, optional, nullable };\n}\n\nexport function introspectSchema(\n schema: ZodAny | undefined\n): SerializableSchemaNode | undefined {\n if (!schema) return undefined;\n\n const { base, optional, nullable } = unwrap(schema);\n const def = getDef(base);\n\n const node: SerializableSchemaNode = {\n kind: inferKind(base),\n optional: optional || undefined,\n nullable: nullable || undefined,\n description: getDescription(base),\n };\n\n // OBJECT\n if (base instanceof z.ZodObject) {\n // Zod 3: _def.shape() (function)\n // Zod 4: .shape getter returns an object\n const rawShape: any =\n (base as any).shape ?? (def && typeof def.shape === \"function\"\n ? def.shape()\n : def?.shape);\n\n const shape =\n typeof rawShape === \"function\" ? rawShape() : rawShape ?? {};\n\n const props: Record<string, SerializableSchemaNode> = {};\n for (const key of Object.keys(shape)) {\n const child = shape[key] as ZodAny;\n const childNode = introspectSchema(child);\n if (childNode) props[key] = childNode;\n }\n node.properties = props;\n }\n\n // ARRAY\n if (base instanceof z.ZodArray) {\n // Zod 3: def.type is inner schema\n // Zod 4: def.element is inner schema\n const inner =\n (def && (def.element as ZodAny)) ||\n (def && (def.type as ZodAny)) ||\n undefined;\n if (inner) {\n node.element = introspectSchema(inner);\n }\n }\n\n // UNION\n if (base instanceof z.ZodUnion) {\n const options: ZodAny[] = (def && def.options) || [];\n node.union = options\n .map((opt) => introspectSchema(opt))\n .filter(Boolean) as SerializableSchemaNode[];\n }\n\n // LITERAL\n if (base instanceof z.ZodLiteral) {\n if (def) {\n // Zod 4: def.values (multi-literal)\n if (Array.isArray(def.values)) {\n node.literal =\n def.values.length === 1 ? def.values[0] : def.values.slice();\n } else {\n // Zod 3: def.value\n node.literal = def.value;\n }\n }\n }\n\n // ENUM\n if (base instanceof z.ZodEnum) {\n if (def) {\n if (Array.isArray(def.values)) {\n // Zod 3\n node.enumValues = def.values.slice();\n } else if (def.entries && typeof def.entries === \"object\") {\n // Zod 4: entries is a { key: value } map\n node.enumValues = Object.values(def.entries).map((v: unknown) =>\n String(v)\n );\n }\n }\n }\n\n return node;\n}\n\nfunction inferKind(schema: ZodAny): string {\n // This path still uses instanceof; it works with Zod 4 Classic\n // (importing from \"zod\"). Anything unknown falls back to \"unknown\".\n if (schema instanceof z.ZodString) return \"string\";\n if (schema instanceof z.ZodNumber) return \"number\";\n if (schema instanceof z.ZodBoolean) return \"boolean\";\n if (schema instanceof z.ZodBigInt) return \"bigint\";\n if (schema instanceof z.ZodDate) return \"date\";\n if (schema instanceof z.ZodArray) return \"array\";\n if (schema instanceof z.ZodObject) return \"object\";\n if (schema instanceof z.ZodUnion) return \"union\";\n if (schema instanceof z.ZodLiteral) return \"literal\";\n if (schema instanceof z.ZodEnum) return \"enum\";\n if (schema instanceof z.ZodRecord) return \"record\";\n if (schema instanceof z.ZodTuple) return \"tuple\";\n if (schema instanceof z.ZodUnknown) return \"unknown\";\n if (schema instanceof z.ZodAny) return \"any\";\n\n return \"unknown\";\n}\n","// serializer.ts\nimport { AnyLeaf, MethodCfg } from \"../core/routesV3.core\";\nimport { introspectSchema, SerializableSchemaNode } from \"./schemaIntrospection.js\";\n\ntype SerializableMethodCfg = Pick<\n MethodCfg,\n | \"description\"\n | \"summary\"\n | \"docsGroup\"\n | \"tags\"\n | \"deprecated\"\n | \"stability\"\n | \"feed\"\n | \"docsMeta\"\n> & {\n hasBody: boolean;\n hasQuery: boolean;\n hasParams: boolean;\n hasOutput: boolean;\n\n // NEW: full Zod ASTs\n bodySchema?: SerializableSchemaNode;\n querySchema?: SerializableSchemaNode;\n paramsSchema?: SerializableSchemaNode;\n outputSchema?: SerializableSchemaNode;\n};\n\nexport type SerializableLeaf = {\n method: string;\n path: string;\n cfg: SerializableMethodCfg;\n};\n\nexport function serializeLeaf(leaf: AnyLeaf): SerializableLeaf {\n const cfg = leaf.cfg;\n\n const tags = Array.isArray(cfg.tags) ? cfg.tags.slice() : [];\n\n return {\n method: leaf.method, // 'get' | 'post' | ...\n path: leaf.path,\n cfg: {\n description: cfg.description,\n summary: cfg.summary,\n docsGroup: cfg.docsGroup,\n tags,\n deprecated: cfg.deprecated,\n stability: cfg.stability,\n feed: !!cfg.feed,\n docsMeta: cfg.docsMeta,\n hasBody: !!cfg.bodySchema || !!cfg.bodyFiles?.length,\n hasQuery: !!cfg.querySchema,\n hasParams: !!cfg.paramsSchema,\n hasOutput: !!cfg.outputSchema,\n\n bodySchema: introspectSchema(cfg.bodySchema),\n querySchema: introspectSchema(cfg.querySchema),\n paramsSchema: introspectSchema(cfg.paramsSchema),\n outputSchema: introspectSchema(cfg.outputSchema),\n },\n };\n}\n","import { AnyLeaf } from \"../core/routesV3.core\";\n\nimport { serializeLeaf } from \"./serializer.js\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\n/**\n * Updated CSS including pastel tag generation,\n * refined header layouts, and sticky overview integration.\n */\nconst CSS_STYLES = `:root {\n --bg-root: #020617;\n --bg-card: rgba(15, 23, 42, 0.6);\n --bg-card-hover: rgba(30, 41, 59, 0.7);\n --bg-glass: rgba(15, 23, 42, 0.90);\n --border-subtle: rgba(148, 163, 184, 0.2);\n\n --text-main: #f8fafc;\n --text-muted: #94a3b8;\n --text-accent: #c4b5fd;\n\n --accent-primary: #a855f7;\n --accent-glow: rgba(168, 85, 247, 0.25);\n\n /* Method Colors */\n --method-get: #4ade80; --method-get-bg: rgba(34, 197, 94, 0.15);\n --method-post: #60a5fa; --method-post-bg: rgba(59, 130, 246, 0.15);\n --method-put: #facc15; --method-put-bg: rgba(234, 179, 8, 0.15);\n --method-patch: #2dd4bf; --method-patch-bg: rgba(45, 212, 191, 0.15);\n --method-delete: #f87171; --method-delete-bg: rgba(248, 113, 113, 0.15);\n\n --radius-card: 12px;\n --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.4);\n\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\n /* Tag Pastel Palette (Generated via class logic) */\n --tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5; /* Red */\n --tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d; /* Amber */\n --tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac; /* Green */\n --tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9; /* Cyan */\n --tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd; /* Blue */\n --tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe; /* Purple */\n --tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4; /* Pink */\n}\n\n* { box-sizing: border-box; }\n\nhtml, body {\n height: 100%;\n margin: 0;\n padding: 0;\n background-color: var(--bg-root);\n color: var(--text-main);\n font-family: system-ui, -apple-system, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n\nbody {\n background-image:\n radial-gradient(circle at 15% 10%, rgba(88, 28, 135, 0.15), transparent 40%),\n radial-gradient(circle at 85% 80%, rgba(15, 23, 42, 1), transparent 40%);\n background-attachment: fixed;\n}\n\n.page {\n min-height: 100vh;\n padding: 20px 40px 60px;\n max-width: 1600px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* --- Header --- */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.header-title h1 {\n font-size: 28px;\n margin: 0;\n font-weight: 700;\n letter-spacing: -0.02em;\n background: linear-gradient(to right, #e2e8f0, #a855f7);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* --- Sticky Controls & Overview --- */\n.controls-container {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-glass);\n backdrop-filter: blur(16px);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n padding: 12px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);\n}\n\n/* Top Row: Filters */\n.filters-row {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n width: 100%;\n}\n\n/* New Wrapper for Search and Methods */\n.left-column {\n display: flex;\n flex-direction: column; /* Stack Search and Methods vertically */\n gap: 16px; /* Gap between search and methods */\n flex-basis: 300px; /* Fixed width for the left column */\n min-width: 250px;\n}\n\n.search-box {\n /* No change to internal search box styling, but remove flex properties */\n width: 100%; /* Make search fill the left-column width */\n position: relative;\n}\n\n/* Tag Filter Group */\n.filter-group.tag-filters-container {\n flex: 1; /* Tags take up all remaining space on the right */\n min-width: 200px;\n}\n\n/* Method Filter Group */\n.filter-group.method-filters-container {\n /* Stays inside the left-column, no need for 100% basis */\n}\n.search-input {\n width: 100%;\n background: rgba(2, 6, 23, 0.6);\n border: 1px solid var(--border-subtle);\n color: var(--text-main);\n padding: 8px 12px 8px 32px;\n border-radius: 6px;\n font-size: 13px;\n transition: all 0.2s;\n}\n.search-input:focus {\n outline: none;\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 2px var(--accent-glow);\n}\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-muted);\n pointer-events: none;\n font-size: 12px;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.filter-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--text-muted);\n font-weight: 700;\n}\n.checkbox-group {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* Bottom Row: Group Overview Chips */\n.overview-row {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--border-subtle);\n align-items: center;\n}\n.overview-label {\n font-size: 10px;\n text-transform: uppercase;\n color: var(--text-muted);\n font-weight: 700;\n margin-right: 4px;\n}\n\n/* Chips/Pills */\n.pill-checkbox { cursor: pointer; user-select: none; }\n.pill-checkbox input { display: none; }\n.pill-checkbox span {\n display: inline-block;\n padding: 3px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: rgba(30, 41, 59, 0.5);\n color: var(--text-muted);\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n.pill-checkbox input:checked + span {\n background: rgba(168, 85, 247, 0.15);\n color: var(--accent-primary);\n border-color: var(--accent-primary);\n}\n\n/* Specialized Colored Tag Pills for Filters */\n.pill-checkbox.colored-tag input:checked + span {\n background: var(--tag-bg);\n color: var(--tag-fg);\n border-color: var(--tag-fg);\n}\n.pill-checkbox.colored-tag span {\n /* Subtle hint of color even when unchecked? Optional. kept neutral for now */\n}\n\n/* Group Jump Links */\n.group-chip {\n font-size: 11px;\n text-decoration: none;\n color: var(--text-accent);\n background: rgba(168, 85, 247, 0.08);\n padding: 3px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n border: 1px solid transparent;\n}\n.group-chip:hover {\n background: rgba(168, 85, 247, 0.2);\n border-color: var(--accent-primary);\n color: #fff;\n}\n\n/* --- Main Content --- */\n.api-group { margin-bottom: 40px; scroll-margin-top: 180px; }\n.group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.group-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--text-main);\n margin: 0;\n}\n.group-actions button {\n background: transparent;\n border: 1px solid var(--border-subtle);\n color: var(--text-muted);\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n margin-left: 8px;\n transition: all 0.2s;\n}\n.group-actions button:hover {\n color: var(--text-main);\n background: rgba(255,255,255,0.05);\n}\n\n.cards-list { display: flex; flex-direction: column; gap: 12px; }\n\n/* --- Endpoint Card --- */\n.endpoint-card {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n overflow: hidden;\n transition: all 0.2s ease;\n cursor: pointer;\n backdrop-filter: blur(10px);\n}\n.endpoint-card:hover {\n background: var(--bg-card-hover);\n border-color: rgba(148, 163, 184, 0.4);\n box-shadow: var(--shadow-card);\n}\n.endpoint-card[data-expanded=\"true\"] {\n background: rgba(30, 41, 59, 0.6);\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 1px var(--accent-glow), var(--shadow-card);\n}\n\n/* Card Header */\n.card-header {\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 12px; /* Space between method, path, tags */\n flex-wrap: wrap; /* Allow wrapping on very small screens if needed, but row pref */\n}\n\n/* Item 1: Method */\n.method-badge {\n font-size: 11px;\n font-weight: 800;\n text-transform: uppercase;\n padding: 4px 8px;\n border-radius: 4px;\n min-width: 55px;\n text-align: center;\n letter-spacing: 0.05em;\n flex-shrink: 0;\n}\n.m-GET { background: var(--method-get-bg); color: var(--method-get); border: 1px solid rgba(74, 222, 128, 0.3); }\n.m-POST { background: var(--method-post-bg); color: var(--method-post); border: 1px solid rgba(96, 165, 250, 0.3); }\n.m-PUT { background: var(--method-put-bg); color: var(--method-put); border: 1px solid rgba(250, 204, 21, 0.3); }\n.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }\n.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }\n\n/* Item 2: Path (Clickable) */\n.path-container {\n font-family: var(--font-mono);\n font-size: 13px;\n color: var(--text-main);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: background 0.2s;\n}\n.path-container:hover {\n background: rgba(255,255,255,0.05);\n}\n.path-text {\n font-weight: 500;\n}\n.copy-icon {\n opacity: 0;\n color: var(--text-muted);\n font-size: 10px;\n transition: opacity 0.2s;\n}\n.path-container:hover .copy-icon { opacity: 1; }\n\n/* Item 3: Tags (Aligned right of path) */\n.tags-container {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n.status-badge {\n font-size: 9px;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid transparent;\n font-weight: 600;\n letter-spacing: 0.03em;\n}\n/* Tag Colors mapped via JS classes */\n.tag-0 { background: var(--tag-0-bg); color: var(--tag-0-fg); border-color: var(--tag-0-fg); }\n.tag-1 { background: var(--tag-1-bg); color: var(--tag-1-fg); border-color: var(--tag-1-fg); }\n.tag-2 { background: var(--tag-2-bg); color: var(--tag-2-fg); border-color: var(--tag-2-fg); }\n.tag-3 { background: var(--tag-3-bg); color: var(--tag-3-fg); border-color: var(--tag-3-fg); }\n.tag-4 { background: var(--tag-4-bg); color: var(--tag-4-fg); border-color: var(--tag-4-fg); }\n.tag-5 { background: var(--tag-5-bg); color: var(--tag-5-fg); border-color: var(--tag-5-fg); }\n.tag-6 { background: var(--tag-6-bg); color: var(--tag-6-fg); border-color: var(--tag-6-fg); }\n\n.not-implemented { border-color: var(--method-delete); color: var(--method-delete); background: var(--method-delete-bg); }\n\n/* Spacer to push expand icon to right */\n.header-spacer { flex: 1; }\n\n.expand-icon {\n color: var(--text-muted);\n transition: transform 0.3s ease;\n font-size: 10px;\n margin-left: 8px;\n}\n.endpoint-card[data-expanded=\"true\"] .expand-icon {\n transform: rotate(180deg);\n color: var(--text-main);\n}\n\n/* Card Body */\n.card-body {\n display: none;\n padding: 0 16px 16px;\n border-top: 1px solid var(--border-subtle);\n margin-top: 4px;\n cursor: default;\n}\n.endpoint-card[data-expanded=\"true\"] .card-body {\n display: block;\n animation: slideDown 0.2s ease-out;\n}\n@keyframes slideDown {\n from { opacity: 0; transform: translateY(-5px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.section-block { margin-top: 18px; }\n.section-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--text-muted);\n margin-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n padding-bottom: 4px;\n}\n.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }\n\n/* Tables */\n.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }\n.schema-table th {\n text-align: left; color: var(--text-muted); font-weight: 600;\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); background: rgba(0,0,0,0.2);\n}\n.schema-table td {\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted);\n}\n.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 20%; }\n.col-type { font-family: var(--font-mono); color: #93c5fd !important; width: 15%; }\n.req-badge { font-size: 9px; text-transform: uppercase; padding: 2px 4px; border-radius: 2px; }\n.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }\n.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }\n\n.empty-message { text-align: center; padding: 40px; color: var(--text-muted); }\n`;\n\n/**\n * Client-side logic.\n */\nconst DOCS_JS = `\n(function() {\n let leaves = [];\n try {\n leaves = JSON.parse(document.getElementById('leaf-data').textContent || '[]');\n } catch(e) { console.error('Failed to parse docs', e); }\n\n // State\n let filters = {\n search: '',\n methods: new Set(),\n tags: new Set()\n };\n\n // DOM Elements\n const elRouteList = document.getElementById('routeList');\n const elOverview = document.getElementById('overviewList');\n const elSearch = document.getElementById('searchInput');\n const elMethodFilters = document.getElementById('methodFilters');\n const elTagFilters = document.getElementById('tagFilters');\n\n // Constants\n const PALETTE_SIZE = 7; // matches .tag-0 to .tag-6 CSS\n\n // Initialization\n function init() {\n populateFilters();\n render();\n setupGlobalListeners();\n }\n\n // Tag Coloring Logic\n function getTagColorClass(tagName) {\n if (tagName === 'not-implemented') return 'not-implemented';\n // Simple string hash to select color index\n let hash = 0;\n for (let i = 0; i < tagName.length; i++) {\n hash = tagName.charCodeAt(i) + ((hash << 5) - hash);\n }\n const index = Math.abs(hash % PALETTE_SIZE);\n return 'tag-' + index;\n }\n\n // Custom Group Sort: Alpha, but UNGROUPED last\n function sortGroups(a, b) {\n const nameA = a.toUpperCase();\n const nameB = b.toUpperCase();\n if (nameA === 'UNGROUPED') return 1;\n if (nameB === 'UNGROUPED') return -1;\n return nameA.localeCompare(nameB);\n }\n\n function populateFilters() {\n // Extract unique values\n const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));\n const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));\n\n // Setup Method Checkboxes\n const sortedMethods = Array.from(allMethods).sort();\n elMethodFilters.innerHTML = sortedMethods.map(m => \\`\n <label class=\"pill-checkbox\">\n <input type=\"checkbox\" value=\"\\${m}\" checked onchange=\"updateFilters()\">\n <span>\\${m}</span>\n </label>\n \\`).join('');\n sortedMethods.forEach(m => filters.methods.add(m));\n\n // Setup Tag Checkboxes (Left Aligned, Colored)\n if (allTags.size > 0) {\n elTagFilters.innerHTML = Array.from(allTags).sort().map(t => {\n const colorClass = getTagColorClass(t);\n const styleVars = 'style=\"--tag-bg:var(--' + colorClass + '-bg); --tag-fg:var(--' + colorClass + '-fg);\"';\n return \\`\n <label class=\"pill-checkbox colored-tag\" \\${styleVars}>\n <input type=\"checkbox\" value=\"\\${t}\" onchange=\"updateFilters()\">\n <span>\\${t}</span>\n </label>\n \\`;\n }).join('');\n }\n }\n\n window.updateFilters = function() {\n filters.search = elSearch.value.toLowerCase();\n filters.methods.clear();\n elMethodFilters.querySelectorAll('input:checked').forEach(cb => filters.methods.add(cb.value));\n filters.tags.clear();\n elTagFilters.querySelectorAll('input:checked').forEach(cb => filters.tags.add(cb.value));\n render();\n }\n\n // Core Render Logic\n function render() {\n // 1. Filter Data\n const filtered = leaves.filter(leaf => {\n const m = (leaf.method || 'GET').toUpperCase();\n const t = (leaf.cfg && leaf.cfg.tags) ? leaf.cfg.tags : [];\n const path = (leaf.path || '').toLowerCase();\n const summary = (leaf.cfg && leaf.cfg.summary || '').toLowerCase();\n\n if (!filters.methods.has(m)) return false;\n if (filters.tags.size > 0 && !t.some(tag => filters.tags.has(tag))) return false;\n if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;\n\n return true;\n });\n\n if (filtered.length === 0) {\n elRouteList.innerHTML = '<div class=\"empty-message\">No endpoints match your filters.</div>';\n elOverview.innerHTML = '';\n return;\n }\n\n // 2. Group Data\n const groups = {};\n filtered.forEach(leaf => {\n const gName = (leaf.cfg && leaf.cfg.docsGroup) ? leaf.cfg.docsGroup : 'UNGROUPED';\n if (!groups[gName]) groups[gName] = [];\n groups[gName].push(leaf);\n });\n\n const sortedGroupNames = Object.keys(groups).sort(sortGroups);\n\n // 3. Render Overview Chips (Inside Sticky Header)\n // Note: We include a small label \"GROUPS:\" or similar if needed, or just chips.\n elOverview.innerHTML = '<span class=\"overview-label\">JUMP TO:</span>' + sortedGroupNames.map(gName => \\`\n <a href=\"#group-\\${gName}\" class=\"group-chip\" onclick=\"scrollToGroup(event, '\\${gName}')\">\n \\${gName}\n </a>\n \\`).join('');\n\n // 4. Render Groups & Cards\n const html = sortedGroupNames.map(gName => {\n const routes = groups[gName];\n // Sort Routes: Path ASC, then Method\n routes.sort((a, b) => {\n const pA = a.path || '';\n const pB = b.path || '';\n if (pA < pB) return -1;\n if (pA > pB) return 1;\n return (a.method || '').localeCompare(b.method || '');\n });\n\n return \\`\n <div class=\"api-group\" id=\"group-\\${gName}\">\n <div class=\"group-header\">\n <h2 class=\"group-title\">\\${gName}</h2>\n <div class=\"group-actions\">\n <button onclick=\"toggleGroup('\\${gName}', true)\">Expand All</button>\n <button onclick=\"toggleGroup('\\${gName}', false)\">Collapse All</button>\n </div>\n </div>\n <div class=\"cards-list\">\n \\${routes.map(renderCard).join('')}\n </div>\n </div>\n \\`;\n }).join('');\n\n elRouteList.innerHTML = html;\n }\n\n // Card Renderer\n function renderCard(leaf) {\n const method = (leaf.method || 'GET').toUpperCase();\n const path = leaf.path || '/';\n const cfg = leaf.cfg || {};\n const summary = cfg.summary || '';\n const description = cfg.description || '';\n const tags = cfg.tags || [];\n\n // Header Construction: Method | Path | Tags | Spacer | Chevron\n const tagBadges = tags.map(t => {\n const cClass = getTagColorClass(t);\n return \\`<span class=\"status-badge \\${cClass}\">\\${t}</span>\\`;\n }).join('');\n\n const headerHtml = \\`\n <div class=\"card-header\">\n <span class=\"method-badge m-\\${method}\">\\${method}</span>\n\n <div class=\"path-container\" onclick=\"copyText(event, '\\${path}')\" title=\"Click to copy path\">\n <span class=\"path-text\">\\${path}</span>\n <span class=\"copy-icon\">📋</span>\n </div>\n\n <div class=\"tags-container\">\n \\${tagBadges}\n </div>\n\n <div class=\"header-spacer\"></div>\n <div class=\"expand-icon\">▼</div>\n </div>\n \\`;\n\n // Body Construction (Preserved structure)\n let contentHtml = \\`<div class=\"section-block\"><div class=\"summary-text\">\n <strong>\\${escape(summary)}</strong>\n \\${description ? '<br><span style=\"font-size:12px; opacity:0.7\">' + escape(description) + '</span>' : ''}\n </div></div>\\`;\n\n if (cfg.paramsSchema || cfg.querySchema) {\n contentHtml += '<div class=\"section-block\"><div class=\"section-title\">Parameters</div>';\n if (cfg.paramsSchema) contentHtml += renderSchemaTable(cfg.paramsSchema, 'Path Parameters');\n if (cfg.querySchema) contentHtml += renderSchemaTable(cfg.querySchema, 'Query Parameters');\n contentHtml += '</div>';\n }\n\n if (cfg.bodySchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Request Body \\${cfg.hasBody ? '' : '(Optional)'}</div>\n \\${renderSchemaTable(cfg.bodySchema)}\n </div>\n \\`;\n }\n\n if (cfg.outputSchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Response Schema</div>\n \\${renderSchemaTable(cfg.outputSchema)}\n </div>\n \\`;\n }\n\n return \\`\n <article class=\"endpoint-card\" data-expanded=\"false\" onclick=\"toggleCard(this, event)\">\n \\${headerHtml}\n <div class=\"card-body\" onclick=\"event.stopPropagation()\">\n \\${contentHtml}\n </div>\n </article>\n \\`;\n }\n\n function renderSchemaTable(node, title) {\n let rows = '';\n if (node.kind === 'object' && node.properties) {\n Object.keys(node.properties).forEach(key => {\n const prop = node.properties[key];\n const reqClass = prop.optional ? 'req-false' : 'req-true';\n const reqText = prop.optional ? 'OPT' : 'REQ';\n rows += \\`\n <tr>\n <td class=\"col-name\">\\${key}</td>\n <td class=\"col-type\">\\${getTypeLabel(prop)}</td>\n <td><span class=\"req-badge \\${reqClass}\">\\${reqText}</span></td>\n <td>\\${escape(prop.description || '')}</td>\n </tr>\n \\`;\n });\n } else {\n rows = \\`<tr><td colspan=\"4\">Type: \\${getTypeLabel(node)}</td></tr>\\`;\n }\n return \\`\n \\${title ? '<div style=\"font-size:11px; font-weight:600; margin-bottom:4px; color:#64748b\">' + title + '</div>' : ''}\n <table class=\"schema-table\">\\${rows}</table>\n \\`;\n }\n\n function getTypeLabel(node) {\n if (!node) return 'any';\n if (node.kind === 'array') return \\`\\${getTypeLabel(node.element)}[]\\`;\n if (node.enumValues) return \\`enum(\\${node.enumValues.join('|')})\\`;\n return node.kind || 'any';\n }\n\n function escape(str) {\n if (!str) return '';\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n window.toggleCard = function(card, e) {\n if (e.target.closest('.path-container')) return; // Don't toggle on path click\n const isExpanded = card.getAttribute('data-expanded') === 'true';\n card.setAttribute('data-expanded', !isExpanded);\n };\n\n window.toggleGroup = function(gName, expand) {\n const group = document.getElementById('group-' + gName);\n if (!group) return;\n group.querySelectorAll('.endpoint-card').forEach(c => c.setAttribute('data-expanded', expand));\n };\n\n window.copyText = function(e, text) {\n e.stopPropagation();\n navigator.clipboard.writeText(text);\n // Could add visual feedback here\n };\n\n window.scrollToGroup = function(e, gName) {\n e.preventDefault();\n const el = document.getElementById('group-' + gName);\n if(el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });\n }\n\n function setupGlobalListeners() {\n elSearch.addEventListener('input', updateFilters);\n }\n\n init();\n})();\n`;\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n const leavesJson = JSON.stringify(leaves.map(serializeLeaf));\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Documentation</title>\n <style>${CSS_STYLES}</style>\n ${options.inlineCss ? `<style>${options.inlineCss}</style>` : \"\"}\n</head>\n<body>\n <div class=\"page\">\n <header class=\"header\">\n <div class=\"header-title\">\n <h1>API Reference</h1>\n </div>\n </header>\n\n<div class=\"controls-container\">\n <div class=\"filters-row\">\n\n <div class=\"left-column\">\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Search</div>\n\n <div class=\"search-box\">\n <span class=\"search-icon\">🔍</span>\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Filter endpoints...\">\n </div>\n </div>\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Methods</div>\n <div class=\"checkbox-group\" id=\"methodFilters\"></div>\n </div>\n </div>\n\n <div class=\"filter-group tag-filters-container\" id=\"tagFilterContainer\">\n <div class=\"filter-label\">Tags</div>\n <div class=\"checkbox-group\" id=\"tagFilters\"></div>\n </div>\n </div>\n\n <div class=\"overview-row\" id=\"overviewList\"></div>\n </div>\n\n <main id=\"routeList\">\n <div style=\"text-align:center; padding:40px; color:#64748b\">Loading API definition...</div>\n </main>\n </div>\n\n <script id=\"leaf-data\" type=\"application/json\">${leavesJson}</script>\n <script>${DOCS_JS}</script>\n ${options.inlineClientScript ? `<script>${options.inlineClientScript}</script>` : \"\"}\n</body>\n</html>\n `;\n}\n","// renderLeafDocsHTML.ts\nimport { AnyLeaf } from \"../core/routesV3.core\";\nimport { renderLeafDocsHTML as LeafDocsPage } from \"./LeafDocsPage\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n return LeafDocsPage(leaves, options)\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAubnB,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiTT,SAAS,mBACd,QACA,UAAyB,CAAC,GAClB;AACR,QAAM,aAAa,KAAK,UAAU,OAAO,IAAI,aAAa,CAAC;AAE3D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAOE,UAAU;AAAA,IACjB,QAAQ,YAAY,UAAU,QAAQ,SAAS,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDA4Cf,UAAU;AAAA,YACjD,OAAO;AAAA,IACf,QAAQ,qBAAqB,WAAW,QAAQ,kBAAkB,cAAc,EAAE;AAAA;AAAA;AAAA;AAItF;;;ACjzBO,SAASC,oBACd,QACA,UAAyB,CAAC,GAClB;AACR,SAAO,mBAAa,QAAQ,OAAO;AACrC;","names":["z","z","mergeSchemas","z","renderLeafDocsHTML"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/routesV3.builder.ts","../src/core/routesV3.core.ts","../src/core/routesV3.finalize.ts","../src/crud/routesV3.crud.ts","../src/sockets/socket.index.ts","../src/docs/schemaIntrospection.ts","../src/docs/serializer.ts","../src/docs/LeafDocsPage.tsx","../src/docs/docs.ts"],"sourcesContent":["import { z } from 'zod';\nimport {\n AnyLeaf,\n Append,\n HttpMethod,\n Leaf,\n Merge,\n MergeArray,\n MethodCfg,\n NodeCfg,\n Prettify,\n} from './routesV3.core';\n\nconst defaultFeedQuerySchema = z.object({\n cursor: z.string().optional(),\n limit: z.coerce.number().min(1).max(100).default(20),\n});\n\nconst paginationField = defaultFeedQuerySchema.optional().default(defaultFeedQuerySchema.parse({}));\ntype PaginationField = typeof paginationField;\n\ntype ZodTypeAny = z.ZodTypeAny;\ntype ParamZod<Name extends string, S extends ZodTypeAny> = z.ZodObject<{ [K in Name]: S }>;\ntype ZodArrayAny = z.ZodArray<ZodTypeAny>;\ntype ZodObjectAny = z.ZodObject<any>;\ntype AnyZodObject = z.ZodObject<any>;\ntype MergedParamsResult<PS, Name extends string, P extends ZodTypeAny> = PS extends ZodTypeAny\n ? z.ZodIntersection<PS, ParamZod<Name, P>>\n : ParamZod<Name, P>;\n\nconst defaultFeedOutputSchema = z.object({\n items: z.array(z.unknown()),\n nextCursor: z.string().optional(),\n});\n\nfunction augmentFeedQuerySchema<Q extends ZodTypeAny | undefined>(schema: Q) {\n if (schema && !(schema instanceof z.ZodObject)) {\n console.warn('Feed queries must be a ZodObject; default pagination applied.');\n return z.object({\n pagination: paginationField,\n });\n }\n\n const base = (schema as ZodObjectAny) ?? z.object({});\n return base.extend({\n pagination: paginationField,\n });\n}\n\nfunction augmentFeedOutputSchema<O extends ZodTypeAny | undefined>(schema: O) {\n if (!schema) return defaultFeedOutputSchema;\n if (schema instanceof z.ZodArray) {\n return z.object({\n items: schema,\n nextCursor: z.string().optional(),\n });\n }\n if (schema instanceof z.ZodObject) {\n const shape = (schema as any).shape ? (schema as any).shape : (schema as any)._def?.shape?.();\n const hasItems = Boolean(shape?.items);\n if (hasItems) {\n return schema.extend({ nextCursor: z.string().optional() });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n }\n return z.object({\n items: z.array(schema as ZodTypeAny),\n nextCursor: z.string().optional(),\n });\n}\n\n/**\n * Runtime helper that mirrors the typed merge used by the builder.\n * @param a Previously merged params schema inherited from parent segments.\n * @param b Newly introduced params schema.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nfunction mergeSchemas<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodTypeAny;\nfunction mergeSchemas<A extends ZodTypeAny>(a: A, b: undefined): A;\nfunction mergeSchemas<B extends ZodTypeAny>(a: undefined, b: B): B;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined): ZodTypeAny | undefined;\nfunction mergeSchemas(a: ZodTypeAny | undefined, b: ZodTypeAny | undefined) {\n if (a && b) return z.intersection(a as any, b as any);\n return (a ?? b) as ZodTypeAny | undefined;\n}\n\ntype FeedQuerySchema<C extends MethodCfg> = z.ZodObject<any>;\n\ntype FeedOutputSchema<C extends MethodCfg> = C['outputSchema'] extends ZodArrayAny\n ? z.ZodObject<{\n items: C['outputSchema'];\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : C['outputSchema'] extends ZodTypeAny\n ? z.ZodObject<{\n items: z.ZodArray<C['outputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : typeof defaultFeedOutputSchema;\n\ntype BaseMethodCfg<C extends MethodCfg> = Merge<\n Omit<MethodCfg, 'querySchema' | 'outputSchema' | 'feed'>,\n Omit<C, 'querySchema' | 'outputSchema' | 'feed'>\n>;\n\ntype FeedField<C extends MethodCfg> = C['feed'] extends true ? { feed: true } : { feed?: boolean };\n\ntype AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<\n infer Shape\n>\n ? z.ZodObject<Shape & { pagination: PaginationField }>\n : z.ZodObject<{ pagination: PaginationField }>;\n\ntype FeedQueryField<C extends MethodCfg> = {\n querySchema: AddPaginationToQuery<\n C['querySchema'] extends AnyZodObject ? C['querySchema'] : undefined\n >;\n};\n\ntype NonFeedQueryField<C extends MethodCfg> = C['querySchema'] extends ZodTypeAny\n ? { querySchema: C['querySchema'] }\n : { querySchema?: undefined };\n\ntype FeedOutputField<C extends MethodCfg> = { outputSchema: FeedOutputSchema<C> };\n\ntype NonFeedOutputField<C extends MethodCfg> = C['outputSchema'] extends ZodTypeAny\n ? { outputSchema: C['outputSchema'] }\n : { outputSchema?: undefined };\n\ntype ParamsField<C extends MethodCfg, PS> = C['paramsSchema'] extends ZodTypeAny\n ? { paramsSchema: C['paramsSchema'] }\n : { paramsSchema: PS };\n\ntype EffectiveFeedFields<C extends MethodCfg, PS> = C['feed'] extends true\n ? FeedField<C> & FeedQueryField<C> & FeedOutputField<C> & ParamsField<C, PS>\n : FeedField<C> & NonFeedQueryField<C> & NonFeedOutputField<C> & ParamsField<C, PS>;\n\ntype EffectiveCfg<C extends MethodCfg, PS> = Prettify<\n Merge<MethodCfg, BaseMethodCfg<C> & EffectiveFeedFields<C, PS>>\n>;\n\ntype BuiltLeaf<\n M extends HttpMethod,\n Base extends string,\n I extends NodeCfg,\n C extends MethodCfg,\n PS,\n> = Leaf<M, Base, Merge<I, EffectiveCfg<C, PS>>>;\n\n/** Builder surface used by `resource(...)` to accumulate leaves. */\nexport interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodTypeAny | undefined,\n> {\n // --- structure ---\n /**\n * Enter or define a static child segment.\n * Optionally accepts extra config and/or a builder to populate nested routes.\n * @param name Child segment literal (no leading slash).\n * @param cfg Optional node configuration to merge for descendants.\n * @param builder Callback to produce leaves for the child branch.\n */\n sub<Name extends string, J extends NodeCfg>(\n name: Name,\n cfg?: J,\n ): Branch<`${Base}/${Name}`, Acc, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, J extends NodeCfg | undefined, R extends readonly AnyLeaf[]>(\n name: Name,\n cfg: J,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], Merge<I, NonNullable<J>>, PS>) => R,\n ): Branch<Base, Append<Acc, R[number]>, Merge<I, NonNullable<J>>, PS>;\n\n sub<Name extends string, R extends readonly AnyLeaf[]>(\n name: Name,\n builder: (r: Branch<`${Base}/${Name}`, readonly [], I, PS>) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, PS>;\n\n // --- parameterized segment (single signature) ---\n /**\n * Introduce a `:param` segment and merge its schema into downstream leaves.\n * @param name Parameter key (without leading colon).\n * @param paramsSchema Zod schema for the parameter value.\n * @param builder Callback that produces leaves beneath the parameterized segment.\n */\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base}/:${Name}`,\n readonly [],\n I,\n MergedParamsResult<PS, Name, P>\n >,\n ) => R,\n ): Branch<Base, MergeArray<Acc, R>, I, MergedParamsResult<PS, Name, P>>;\n\n // --- flags inheritance ---\n /**\n * Merge additional node configuration that subsequent leaves will inherit.\n * @param cfg Partial node configuration.\n */\n with<J extends NodeCfg>(cfg: J): Branch<Base, Acc, Merge<I, J>, PS>;\n\n // --- methods (return Branch to keep chaining) ---\n /**\n * Register a GET leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n get<C extends MethodCfg>(\n cfg: C,\n ): Branch<\n Base,\n Append<Acc, Prettify<BuiltLeaf<'get', Base, I, C, PS>>>,\n I,\n PS\n >;\n\n /**\n * Register a POST leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n post<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'post', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PUT leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n put<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'put', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a PATCH leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n patch<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'patch', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n /**\n * Register a DELETE leaf at the current path.\n * @param cfg Method configuration (schemas, flags, descriptions, etc).\n */\n delete<C extends Omit<MethodCfg, 'feed'>>(\n cfg: C,\n ): Branch<\n Base,\n Append<\n Acc,\n Prettify<BuiltLeaf<'delete', Base, I, Merge<C, { feed: false }>, PS>>\n >,\n I,\n PS\n >;\n\n // --- finalize this subtree ---\n /**\n * Finish the branch and return the collected leaves.\n * @returns Readonly tuple of accumulated leaves.\n */\n done(): Readonly<Acc>;\n}\n\n/**\n * Start building a resource at the given base path.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Optional node configuration applied to all descendants.\n * @returns Root `Branch` instance used to compose the route tree.\n */\nexport function resource<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited?: I,\n): Branch<Base, readonly [], I, undefined> {\n const rootBase = base;\n const rootInherited: NodeCfg = { ...(inherited as NodeCfg) };\n\n function makeBranch<Base2 extends string, I2 extends NodeCfg, PS2 extends ZodTypeAny | undefined>(\n base2: Base2,\n inherited2: I2,\n mergedParamsSchema?: PS2,\n ) {\n const stack: AnyLeaf[] = [];\n let currentBase: string = base2;\n let inheritedCfg: NodeCfg = { ...(inherited2 as NodeCfg) };\n let currentParamsSchema: PS2 = mergedParamsSchema as PS2;\n\n function add<M extends HttpMethod, C extends MethodCfg>(method: M, cfg: C) {\n // If the method didn’t provide a paramsSchema, inject the active merged one.\n const effectiveParamsSchema = (cfg.paramsSchema ?? currentParamsSchema) as PS2 | undefined;\n const effectiveQuerySchema =\n cfg.feed === true ? augmentFeedQuerySchema(cfg.querySchema) : cfg.querySchema;\n const effectiveOutputSchema =\n cfg.feed === true ? augmentFeedOutputSchema(cfg.outputSchema) : cfg.outputSchema;\n\n const fullCfg = (\n effectiveParamsSchema\n ? {\n ...inheritedCfg,\n ...cfg,\n paramsSchema: effectiveParamsSchema,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n : {\n ...inheritedCfg,\n ...cfg,\n ...(effectiveQuerySchema ? { querySchema: effectiveQuerySchema } : {}),\n ...(effectiveOutputSchema ? { outputSchema: effectiveOutputSchema } : {}),\n }\n ) as any;\n\n const leaf = {\n method,\n path: currentBase as Base2,\n cfg: fullCfg,\n } as const;\n\n stack.push(leaf as unknown as AnyLeaf);\n\n // Return same runtime obj, but with Acc including this new leaf\n return api as unknown as Branch<Base2, Append<readonly [], typeof leaf>, I2, PS2>;\n }\n\n const api: any = {\n // compose a plain subpath (optional cfg) with optional builder\n sub<Name extends string, J extends NodeCfg | undefined = undefined>(\n name: Name,\n cfgOrBuilder?: J | ((r: any) => readonly AnyLeaf[]),\n maybeBuilder?: (r: any) => readonly AnyLeaf[],\n ) {\n let cfg: NodeCfg | undefined;\n let builder: ((r: any) => readonly AnyLeaf[]) | undefined;\n\n if (typeof cfgOrBuilder === 'function') {\n builder = cfgOrBuilder as any;\n } else {\n cfg = cfgOrBuilder as NodeCfg | undefined;\n builder = maybeBuilder;\n }\n\n const childBase = `${currentBase}/${name}` as const;\n const childInherited = { ...inheritedCfg, ...(cfg ?? {}) } as Merge<I2, NonNullable<J>>;\n\n if (builder) {\n // params schema PS2 flows through unchanged on plain sub\n const child = makeBranch(childBase, childInherited, currentParamsSchema);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n typeof childInherited,\n PS2\n >;\n } else {\n currentBase = childBase;\n inheritedCfg = childInherited;\n return api as Branch<`${Base2}/${Name}`, readonly [], typeof childInherited, PS2>;\n }\n },\n\n // the single param function: name + schema + builder\n routeParameter<Name extends string, P extends ZodTypeAny, R extends readonly AnyLeaf[]>(\n name: Name,\n paramsSchema: P,\n builder: (\n r: Branch<\n `${Base2}/:${Name}`,\n readonly [],\n I2,\n MergedParamsResult<PS2, Name, P>\n >,\n ) => R,\n ) {\n const childBase = `${currentBase}/:${name}` as const;\n // Wrap the value schema under the param name before merging with inherited params schema\n const paramObj: ParamZod<Name, P> = z.object({ [name]: paramsSchema } as Record<Name, P>);\n const childParams = (currentParamsSchema\n ? mergeSchemas(currentParamsSchema, paramObj)\n : paramObj) as MergedParamsResult<PS2, Name, P>;\n const child = makeBranch(childBase, inheritedCfg as I2, childParams);\n const leaves = builder(child) as readonly AnyLeaf[];\n for (const l of leaves) stack.push(l);\n return api as Branch<\n Base2,\n Append<readonly [], (typeof leaves)[number]>,\n I2,\n MergedParamsResult<PS2, Name, P>\n >;\n },\n\n with<J extends NodeCfg>(cfg: J) {\n inheritedCfg = { ...inheritedCfg, ...cfg };\n return api as Branch<Base2, readonly [], Merge<I2, J>, PS2>;\n },\n\n // methods (inject current params schema if missing)\n get<C extends MethodCfg>(cfg: C) {\n return add('get', cfg);\n },\n post<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('post', { ...cfg, feed: false });\n },\n put<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('put', { ...cfg, feed: false });\n },\n patch<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('patch', { ...cfg, feed: false });\n },\n delete<C extends Omit<MethodCfg, 'feed'>>(cfg: C) {\n return add('delete', { ...cfg, feed: false });\n },\n\n done() {\n return stack;\n },\n };\n\n return api as Branch<Base2, readonly [], I2, PS2>;\n }\n\n // Root branch starts with no params schema (PS = undefined)\n return makeBranch(rootBase, rootInherited, undefined);\n}\n\n/**\n * Merge two readonly tuples (preserves literal tuple information).\n * @param arr1 First tuple.\n * @param arr2 Second tuple.\n * @returns New tuple containing values from both inputs.\n */\nexport const mergeArrays = <T extends readonly any[], S extends readonly any[]>(\n arr1: T,\n arr2: S,\n) => {\n return [...arr1, ...arr2] as [...T, ...S];\n};\n","import { z, ZodType } from 'zod';\n\n/** Supported HTTP verbs for the routes DSL. */\nexport type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\n/** Declarative description of a multipart upload field. */\nexport type FileField = {\n /** Form field name used for uploads. */\n name: string;\n /** Maximum number of files accepted for this field. */\n maxCount: number;\n};\n\n/** Configuration that applies to an entire branch of the route tree. */\nexport type NodeCfg = {\n /** @deprecated. Does nothing. */\n authenticated?: boolean;\n};\n\n/** Per-method configuration merged with inherited node config. */\nexport type MethodCfg = {\n /** Zod schema describing the request body. */\n bodySchema?: ZodType;\n /** Zod schema describing the query string. */\n querySchema?: ZodType;\n /** Zod schema describing path params (overrides inferred params). */\n paramsSchema?: ZodType;\n /** Zod schema describing the response payload. */\n outputSchema?: ZodType;\n /** Multipart upload definitions for the route. */\n bodyFiles?: FileField[];\n /** Marks the route as an infinite feed (enables cursor helpers). */\n feed?: boolean;\n\n /** Optional human-readable description for docs/debugging. */\n description?: string;\n /**\n * Short one-line summary for docs list views.\n * Shown in navigation / tables instead of the full description.\n */\n summary?: string;\n /**\n * Group name used for navigation sections in docs UIs.\n * e.g. \"Users\", \"Billing\", \"Auth\".\n */\n docsGroup?: string;\n /**\n * Tags that can be used to filter / facet in interactive docs.\n * e.g. [\"users\", \"admin\", \"internal\"].\n */\n tags?: string[];\n /**\n * Mark the route as deprecated in docs.\n * Renderers can badge this and/or hide by default.\n */\n deprecated?: boolean;\n /**\n * Optional stability information for the route.\n * Renderers can surface this prominently.\n */\n stability?: 'experimental' | 'beta' | 'stable' | 'deprecated';\n /**\n * Hide this route from public docs while keeping it usable in code.\n */\n docsHidden?: boolean;\n /**\n * Arbitrary extra metadata for docs renderers.\n * Can be used for auth requirements, rate limits, feature flags, etc.\n */\n docsMeta?: Record<string, unknown>;\n};\n\n/** Immutable representation of a single HTTP route in the tree. */\nexport type Leaf<M extends HttpMethod, P extends string, C extends MethodCfg> = {\n /** Lowercase HTTP method (get/post/...). */\n readonly method: M;\n /** Concrete path for the route (e.g. `/v1/users/:userId`). */\n readonly path: P;\n /** Readonly snapshot of route configuration. */\n readonly cfg: Readonly<C>;\n};\n\n/** Convenience union covering all generated leaves. */\nexport type AnyLeaf = Leaf<HttpMethod, string, MethodCfg>;\n\n/** Merge two object types while keeping nice IntelliSense output. */\nexport type Merge<A, B> = Prettify<Omit<A, keyof B> & B>;\n\n/** Append a new element to a readonly tuple type. */\nexport type Append<T extends readonly unknown[], X> = [...T, X];\n\n/** Concatenate two readonly tuple types. */\nexport type MergeArray<A extends readonly unknown[], B extends readonly unknown[]> = [\n ...A,\n ...B,\n];\n\n// helpers (optional)\ntype SegmentParams<S extends string> = S extends `:${infer P}` ? P : never;\ntype Split<S extends string> = S extends ''\n ? []\n : S extends `${infer A}/${infer B}`\n ? [A, ...Split<B>]\n : [S];\ntype ExtractParamNames<Path extends string> = SegmentParams<Split<Path>[number]>;\n\n/** Derive a params object type from a literal route string. */\nexport type ExtractParamsFromPath<Path extends string> =\n ExtractParamNames<Path> extends never ? never : Record<ExtractParamNames<Path>, string | number>;\n\n/**\n * Interpolate `:params` in a path using the given values.\n * @param path Literal route string containing `:param` segments.\n * @param params Object of parameter values to interpolate.\n * @returns Path string with parameters substituted.\n */\nexport function compilePath<Path extends string>(path: Path, params: ExtractParamsFromPath<Path>) {\n if (!params) return path;\n return path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {\n const v = (params as any)[k];\n if (v === undefined || v === null) throw new Error(`Missing param :${k}`);\n return String(v);\n });\n}\n\n/**\n * Build a deterministic cache key for the given leaf.\n * The key matches the shape consumed by React Query helpers.\n * @param args.leaf Leaf describing the endpoint.\n * @param args.params Optional params used to build the path.\n * @param args.query Optional query payload.\n * @returns Tuple suitable for React Query cache keys.\n */\ntype SplitPath<P extends string> = P extends ''\n ? []\n : P extends `${infer A}/${infer B}`\n ? [A, ...SplitPath<B>]\n : [P];\nexport function buildCacheKey<L extends AnyLeaf>(args: {\n leaf: L;\n params?: ExtractParamsFromPath<L['path']>;\n query?: InferQuery<L>;\n}) {\n let p = args.leaf.path;\n if (args.params) {\n p = compilePath<L['path']>(p, args.params);\n }\n return [args.leaf.method, ...(p.split('/').filter(Boolean) as SplitPath<typeof p>), args.query ?? {}] as const;\n}\n\n/** Infer params either from the explicit params schema or from the path literal. */\nexport type InferParams<L extends AnyLeaf> = L['cfg']['paramsSchema'] extends ZodType\n ? z.infer<L['cfg']['paramsSchema']>\n : ExtractParamsFromPath<L['path']>;\n\n/** Infer query shape from a Zod schema when present. */\nexport type InferQuery<L extends AnyLeaf> = L['cfg']['querySchema'] extends ZodType\n ? z.infer<L['cfg']['querySchema']>\n : never;\n\n/** Infer request body shape from a Zod schema when present. */\nexport type InferBody<L extends AnyLeaf> = L['cfg']['bodySchema'] extends ZodType\n ? z.infer<L['cfg']['bodySchema']>\n : never;\n\n/** Infer handler output shape from a Zod schema. Defaults to unknown. */\nexport type InferOutput<L extends AnyLeaf> = L['cfg']['outputSchema'] extends ZodType\n ? z.infer<L['cfg']['outputSchema']>\n : unknown;\n\n/** Render a type as if it were a simple object literal. */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n","// TODO:\n// * use this as a transform that infers the types from Zod, to only pass the types around instead of the full schemas -> make converters that go both ways (data to schema, schema to data)\n// * consider moving to core package\n// * update server and client side to use this type instead of raw zod schemas \nimport { AnyLeaf, HttpMethod, Prettify } from './routesV3.core';\n\n/** Build the key type for a leaf — distributive so method/path stay paired. */\nexport type KeyOf<L extends AnyLeaf> = L extends AnyLeaf\n ? `${Uppercase<L['method']>} ${L['path']}`\n : never;\n\n// From a tuple of leaves, get the union of keys (pairwise, no cartesian blow-up)\ntype KeysOf<Leaves extends readonly AnyLeaf[]> = KeyOf<Leaves[number]>;\n\n// Parse method & path out of a key\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n// Given Leaves and a Key, pick the exact leaf that matches method+path\ntype LeafForKey<Leaves extends readonly AnyLeaf[], K extends string> = Extract<\n Leaves[number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/**\n * Freeze a leaf tuple into a registry with typed key lookups.\n * @param leaves Readonly tuple of leaves produced by the builder DSL.\n * @returns Registry containing the leaves array and a `byKey` lookup map.\n */\nexport function finalize<const L extends readonly AnyLeaf[]>(leaves: L) {\n type Keys = KeysOf<L>;\n type MapByKey = { [K in Keys]: Prettify<LeafForKey<L, K>> };\n\n const byKey = Object.fromEntries(\n leaves.map((l) => [`${l.method.toUpperCase()} ${l.path}`, l] as const),\n ) as unknown as MapByKey;\n\n const log = (logger: { system: (...args: unknown[]) => void }) => {\n logger.system('Finalized routes:');\n (Object.keys(byKey) as Keys[]).forEach((k) => {\n const leaf = byKey[k];\n logger.system(`- ${k}`);\n });\n };\n\n return { all: leaves, byKey, log };\n}\n\n/** Nominal type alias for a finalized registry. */\nexport type Registry<R extends ReturnType<typeof finalize>> = R;\n\ntype FilterRoute<\n T extends readonly AnyLeaf[],\n F extends string,\n Acc extends readonly AnyLeaf[] = [],\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? First extends { path: `${string}${F}${string}` }\n ? FilterRoute<Rest, F, [...Acc, First]>\n : FilterRoute<Rest, F, Acc>\n : Acc;\n\ntype UpperCase<S extends string> = S extends `${infer First}${infer Rest}`\n ? `${Uppercase<First>}${UpperCase<Rest>}`\n : S;\n\ntype Routes<T extends readonly AnyLeaf[], F extends string> = FilterRoute<T, F>;\ntype ByKey<\n T extends readonly AnyLeaf[],\n Acc extends Record<string, AnyLeaf> = {},\n> = T extends readonly [infer First extends AnyLeaf, ...infer Rest extends AnyLeaf[]]\n ? ByKey<Rest, Acc & { [K in `${UpperCase<First['method']>} ${First['path']}`]: First }>\n : Acc;\n\n/**\n * Convenience helper for extracting a subset of routes by path fragment.\n * @param T Tuple of leaves produced by the DSL.\n * @param F String fragment to match against the route path.\n */\nexport type SubsetRoutes<T extends readonly AnyLeaf[], F extends string> = {\n byKey: ByKey<Routes<T, F>>;\n all: Routes<T, F>;\n};\n","// routesV3.crud.ts\n// -----------------------------------------------------------------------------\n// Add a first-class .crud() helper to the routesV3 DSL, without touching the\n// core builder. This file:\n// 1) Declares types + module augmentation to extend Branch<...> with .crud\n// 2) Provides a runtime decorator `withCrud(...)` that installs the method\n// 3) Exports a convenience `resourceWithCrud(...)` wrapper (optional)\n// -----------------------------------------------------------------------------\n\nimport { z, ZodType } from 'zod';\nimport {\n resource as baseResource,\n type Branch as _Branch, // import type so we can augment it\n} from '../core/routesV3.builder';\nimport { type AnyLeaf, type Leaf, type NodeCfg } from '../core/routesV3.core';\n\n// -----------------------------------------------------------------------------\n// Small utilities (runtime + types)\n// -----------------------------------------------------------------------------\n/** Default cursor pagination used by list (GET collection). */\nexport const CrudDefaultPagination = z.object({\n limit: z.coerce.number().min(1).max(100).default(20),\n cursor: z.string().optional(),\n});\nconst CrudPaginationField = CrudDefaultPagination.optional().default(CrudDefaultPagination.parse({}));\ntype CrudPaginationField = typeof CrudPaginationField;\ntype AnyCrudZodObject = z.ZodObject<any>;\ntype AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape>\n ? z.ZodObject<Shape & { pagination: CrudPaginationField }>\n : z.ZodObject<{ pagination: CrudPaginationField }>;\n\n/**\n * Merge two Zod schemas at runtime; matches the typing pattern used in builder.\n * @param a Existing params schema inherited from parent branches.\n * @param b Schema introduced at the current branch.\n * @returns Intersection of schemas when both exist, otherwise whichever is defined.\n */\nexport function mergeSchemas<A extends ZodType | undefined, B extends ZodType | undefined>(\n a: A,\n b: B,\n): A extends ZodType ? (B extends ZodType ? ReturnType<typeof z.intersection<A, B>> : A) : B {\n if (a && b) return z.intersection(a as any, b as any) as any;\n return (a ?? b) as any;\n}\n\n/** Build { [Name]: S } Zod object at the type-level. */\nexport type ParamSchema<Name extends string, S extends ZodType> = z.ZodObject<{\n [K in Name]: S;\n}>;\n\n/** Inject active params schema into a leaf cfg (to match your Leaf typing). */\ntype WithParams<I, P> = P extends ZodType ? Omit<I, 'paramsSchema'> & { paramsSchema: P } : I;\n\n/** Resolve boolean flag: default D unless explicitly false. */\ntype Flag<E, D extends boolean> = E extends false ? false : D;\n\n// -----------------------------------------------------------------------------\n// CRUD config (few knobs, with DX comments)\n// -----------------------------------------------------------------------------\n\n/** Toggle individual CRUD methods; defaults enable all (create/update require body). */\nexport type CrudEnable = {\n /** GET /collection (feed). Defaults to true. */ list?: boolean;\n /** POST /collection. Defaults to true if `create` provided. */ create?: boolean;\n /** GET /collection/:id. Defaults to true. */ read?: boolean;\n /** PATCH /collection/:id. Defaults to true if `update` provided. */ update?: boolean;\n /** DELETE /collection/:id. Defaults to true. */ remove?: boolean;\n};\n\n/** Collection GET config. */\nexport type CrudListCfg = {\n /** Query schema (defaults to cursor pagination). */\n querySchema?: ZodType;\n /** Output schema (defaults to { items: Item[], nextCursor? }). */\n outputSchema?: ZodType;\n /** Optional description string. */\n description?: string;\n};\n\n/** Collection POST config. */\nexport type CrudCreateCfg = {\n /** Body schema for creating an item. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item GET config. */\nexport type CrudReadCfg = {\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item PATCH config. */\nexport type CrudUpdateCfg = {\n /** Body schema for partial updates. */\n bodySchema: ZodType;\n /** Output schema (defaults to itemOutputSchema). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/** Item DELETE config. */\nexport type CrudRemoveCfg = {\n /** Output schema (defaults to { ok: true }). */\n outputSchema?: ZodType;\n /** Optional description. */\n description?: string;\n};\n\n/**\n * CRUD config for `.crud(\"resource\", cfg, extras?)`.\n *\n * It synthesizes `:[resourceName]Id` as the param key and uses `paramSchema` for the value.\n * Prefer passing a **value schema** (e.g. `z.string().uuid()`).\n * As a convenience, a ZodObject of shape `{ [resourceName]Id: schema }` is accepted at runtime too.\n */\nexport type CrudCfg<Name extends string = string> = {\n /**\n * Zod schema for the ID *value* (e.g. `z.string().uuid()`).\n * The parameter key is always `:${name}Id`.\n * (If you pass `z.object({ [name]Id: ... })`, it's accepted at runtime.)\n */\n paramSchema: ZodType;\n\n /**\n * The single-item output shape used by read/create/update by default.\n * List defaults to `{ items: z.array(itemOutputSchema), nextCursor? }`.\n */\n itemOutputSchema: ZodType;\n\n /** Per-method toggles (all enabled by default; create/update require bodies). */\n enable?: CrudEnable;\n\n /** GET /collection configuration. */ list?: CrudListCfg;\n /** POST /collection configuration (enables create). */ create?: CrudCreateCfg;\n /** GET /collection/:id configuration. */ read?: CrudReadCfg;\n /** PATCH /collection/:id configuration (enables update). */ update?: CrudUpdateCfg;\n /** DELETE /collection/:id configuration. */ remove?: CrudRemoveCfg;\n};\n\n// -----------------------------------------------------------------------------\n// Type plumbing to expose generated leaves (so finalize().byKey sees them)\n// -----------------------------------------------------------------------------\n\ntype CrudLeavesTuple<\n Base extends string,\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n> = `${Name}Id` extends infer IdName extends string\n ? ParamSchema<IdName, C['paramSchema']> extends infer IdParamZ extends ZodType\n ? [\n // GET /collection\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['list'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n feed: true;\n querySchema: C['list'] extends CrudListCfg\n ? AddCrudPagination<\n C['list']['querySchema'] extends AnyCrudZodObject\n ? C['list']['querySchema']\n : undefined\n >\n : AddCrudPagination<undefined>;\n outputSchema: C['list'] extends CrudListCfg\n ? C['list']['outputSchema'] extends ZodType\n ? C['list']['outputSchema']\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>\n : z.ZodObject<{\n items: z.ZodArray<C['itemOutputSchema']>;\n nextCursor: z.ZodOptional<z.ZodString>;\n }>;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []),\n\n // POST /collection\n ...((C['create'] extends CrudCreateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['create'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'post',\n `${Base}/${Name}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['create'] extends CrudCreateCfg\n ? C['create']['bodySchema']\n : never;\n outputSchema: C['create'] extends CrudCreateCfg\n ? C['create']['outputSchema'] extends ZodType\n ? C['create']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n PS\n >\n >,\n ]\n : []\n : []),\n\n // GET /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['read'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'get',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['read'] extends CrudReadCfg\n ? C['read']['outputSchema'] extends ZodType\n ? C['read']['outputSchema']\n : C['itemOutputSchema']\n : C['itemOutputSchema'];\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n\n // PATCH /collection/:id\n ...((C['update'] extends CrudUpdateCfg ? true : false) extends true\n ? Flag<\n C['enable'] extends CrudEnable ? C['enable']['update'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'patch',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n bodySchema: C['update'] extends CrudUpdateCfg\n ? C['update']['bodySchema']\n : never;\n outputSchema: C['update'] extends CrudUpdateCfg\n ? C['update']['outputSchema'] extends ZodType\n ? C['update']['outputSchema']\n : C['itemOutputSchema']\n : never;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []\n : []),\n\n // DELETE /collection/:id\n ...(Flag<\n C['enable'] extends CrudEnable ? C['enable']['remove'] : undefined,\n true\n > extends true\n ? [\n Leaf<\n 'delete',\n `${Base}/${Name}/:${IdName}`,\n WithParams<\n Omit<I, never> & {\n outputSchema: C['remove'] extends CrudRemoveCfg\n ? C['remove']['outputSchema'] extends ZodType\n ? C['remove']['outputSchema']\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>\n : z.ZodObject<{ ok: z.ZodLiteral<true> }>;\n description?: string;\n },\n ReturnType<typeof mergeSchemas<PS, IdParamZ>>\n >\n >,\n ]\n : []),\n ]\n : never\n : never;\n\n/** Merge generated leaves + extras into the branch accumulator. */\ntype CrudResultAcc<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[],\n> = [...Acc, ...CrudLeavesTuple<Base, I, PS, Name, C>, ...Extras];\n\n// -----------------------------------------------------------------------------\n// Module augmentation: add the typed `.crud(...)` to Branch<...>\n// -----------------------------------------------------------------------------\n\ndeclare module './../core/routesV3.builder' {\n interface Branch<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n > {\n /**\n * Generate opinionated CRUD endpoints for a collection at `/.../<name>`\n * with an item at `/.../<name>/:<name>Id`.\n *\n * Creates (subject to `enable` + presence of create/update bodies):\n * - GET /<name> (feed list)\n * - POST /<name> (create)\n * - GET /<name>/:<name>Id (read item)\n * - PATCH /<name>/:<name>Id (update item)\n * - DELETE /<name>/:<name>Id (remove item)\n *\n * The `extras` callback receives live builders at both collection and item\n * levels so you can add custom subroutes; their leaves are included in the\n * returned Branch type.\n */\n crud<\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(\n name: Name,\n cfg: C,\n extras?: (ctx: {\n /** Builder at `/.../<name>` (collection scope). */\n collection: _Branch<`${Base}/${Name}`, readonly [], I, PS>;\n /** Builder at `/.../<name>/:<name>Id` (item scope). */\n item: _Branch<\n `${Base}/${Name}/:${`${Name}Id`}`,\n readonly [],\n I,\n ReturnType<typeof mergeSchemas<PS, ParamSchema<`${Name}Id`, C['paramSchema']>>>\n >;\n }) => Extras,\n ): _Branch<Base, CrudResultAcc<Base, Acc, I, PS, Name, C, Extras>, I, PS>;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Runtime: install .crud on any Branch via decorator\n// -----------------------------------------------------------------------------\n\n/**\n * Decorate a Branch instance so `.crud(...)` is available at runtime.\n * Tip: wrap the root: `withCrud(resource('/v1'))`.\n * @param branch Branch returned by `resource(...)`.\n * @returns Same branch with `.crud(...)` installed.\n */\nexport function withCrud<\n Base extends string,\n Acc extends readonly AnyLeaf[],\n I extends NodeCfg,\n PS extends ZodType | undefined,\n>(branch: _Branch<Base, Acc, I, PS>): _Branch<Base, Acc, I, PS> {\n const b = branch as any;\n if (typeof b.crud === 'function') return branch;\n\n b.crud = function <\n Name extends string,\n C extends CrudCfg<Name>,\n Extras extends readonly AnyLeaf[] = readonly [],\n >(this: any, name: Name, cfg: C, extras?: (ctx: { collection: any; item: any }) => Extras) {\n // Build inside a sub-branch at `/.../<name>` and harvest its leaves.\n return this.sub(name, (collection: any) => {\n const idKey = `${name}Id`;\n\n // Accept a value schema (preferred); but if a z.object({[name]Id: ...}) is provided, use it.\n const s = cfg.paramSchema;\n const isObj =\n s &&\n typeof (s as any)._def === 'object' &&\n (s as any)._def.typeName === 'ZodObject' &&\n typeof (s as any).shape === 'function';\n\n const paramValueSchema =\n isObj && (s as any).shape()[idKey] ? ((s as any).shape()[idKey] as ZodType) : (s as ZodType);\n\n const itemOut = cfg.itemOutputSchema;\n const listOut =\n cfg.list?.outputSchema ??\n z.object({\n items: z.array(itemOut),\n nextCursor: z.string().optional(),\n });\n\n const want = {\n list: cfg.enable?.list !== false,\n create: cfg.enable?.create !== false && !!cfg.create?.bodySchema,\n read: cfg.enable?.read !== false,\n update: cfg.enable?.update !== false && !!cfg.update?.bodySchema,\n remove: cfg.enable?.remove !== false,\n } as const;\n\n // Collection routes\n if (want.list) {\n collection.get({\n feed: true,\n querySchema: cfg.list?.querySchema ?? CrudDefaultPagination,\n outputSchema: listOut,\n description: cfg.list?.description ?? 'List',\n });\n }\n\n if (want.create) {\n collection.post({\n bodySchema: cfg.create!.bodySchema,\n outputSchema: cfg.create?.outputSchema ?? itemOut,\n description: cfg.create?.description ?? 'Create',\n });\n }\n\n // Item routes via :<name>Id\n collection.routeParameter(idKey as any, paramValueSchema as any, (item: any) => {\n if (want.read) {\n item.get({\n outputSchema: cfg.read?.outputSchema ?? itemOut,\n description: cfg.read?.description ?? 'Read',\n });\n }\n if (want.update) {\n item.patch({\n bodySchema: cfg.update!.bodySchema,\n outputSchema: cfg.update?.outputSchema ?? itemOut,\n description: cfg.update?.description ?? 'Update',\n });\n }\n if (want.remove) {\n item.delete({\n outputSchema: cfg.remove?.outputSchema ?? z.object({ ok: z.literal(true) }),\n description: cfg.remove?.description ?? 'Delete',\n });\n }\n\n // Give extras fully-capable builders (decorate so extras can call .crud again)\n if (extras) extras({ collection: withCrud(collection), item: withCrud(item) });\n\n return item.done();\n });\n\n return collection.done();\n });\n };\n\n return branch;\n}\n\n// -----------------------------------------------------------------------------\n// Optional convenience: re-export a resource() that already has .crud installed\n// -----------------------------------------------------------------------------\n\n/**\n * Drop-in replacement for `resource(...)` that returns a Branch with `.crud()` installed.\n * You can either use this or call `withCrud(resource(...))`.\n * @param base Root path for the resource (e.g. `/v1`).\n * @param inherited Node configuration merged into every leaf.\n * @returns Branch builder that already includes the CRUD helper.\n */\nexport function resourceWithCrud<Base extends string, I extends NodeCfg = {}>(\n base: Base,\n inherited: I,\n) {\n return withCrud(baseResource(base, inherited));\n}\n\n// Re-export the original in case you want both styles from this module\nexport { baseResource as resource };\n","// socket.index.ts (shared client/server)\n\nimport { z } from 'zod';\n\nexport type SocketSchema<Output =unknown> = z.ZodType & {\n __out: Output;\n};\n\nexport type SocketSchemaOutput<Schema extends z.ZodTypeAny> = Schema extends {\n __out: infer Out;\n} ? Out : z.output<Schema>;\n\nexport type SocketEvent<Out = unknown> = {\n message: z.ZodTypeAny;\n /** phantom field, only for typing; not meant to be used at runtime */\n __out: Out;\n};\n\nexport type EventMap = Record<string, SocketEvent<any>>;\n\n/**\n * System event names – shared so server and client\n * don't drift on naming.\n */\nexport type SysEventName = 'sys:connect' | 'sys:disconnect' | 'sys:reconnect' | 'sys:connect_error' | 'sys:ping' | 'sys:pong' | 'sys:room_join' | 'sys:room_leave';\nexport function defineSocketEvents<const C extends SocketConnectionConfig, const Schemas extends Record<string, {\n message: z.ZodTypeAny;\n}>>(config: C, events: Schemas): {\n config: {\n [K in keyof C]: SocketSchema<z.output<C[K]>>;\n };\n events: {\n [K in keyof Schemas]: SocketEvent<z.output<Schemas[K]['message']>>;\n };\n};\n\nexport function defineSocketEvents(config: any, events: any) {\n return { config, events };\n}\n\n\n\nexport type Payload<T extends EventMap, K extends keyof T> = T[K] extends SocketEvent<infer Out> ? Out : never;\nexport type SocketConnectionConfig = {\n joinMetaMessage: z.ZodTypeAny;\n leaveMetaMessage: z.ZodTypeAny;\n pingPayload: z.ZodTypeAny;\n pongPayload: z.ZodTypeAny;\n};\n\nexport type SocketConnectionConfigOutput = {\n joinMetaMessage: SocketSchema<any>;\n leaveMetaMessage: SocketSchema<any>;\n pingPayload: SocketSchema<any>;\n pongPayload: SocketSchema<any>;\n}","// schemaIntrospection.ts\nimport * as z from \"zod\";\n\nexport type SerializableSchemaNode = {\n kind: string; // \"object\" | \"string\" | \"number\" | ...\n optional?: boolean;\n nullable?: boolean;\n description?: string;\n\n // object\n properties?: Record<string, SerializableSchemaNode>;\n // array\n element?: SerializableSchemaNode;\n // union\n union?: SerializableSchemaNode[];\n // literal\n literal?: unknown;\n // enum\n enumValues?: string[];\n};\n\ntype ZodAny = z.ZodTypeAny;\n\n/**\n * Zod 3 uses `schema._def`, Zod 4 uses `schema._zod.def`.\n */\nfunction getDef(schema: unknown): any | undefined {\n if (!schema || typeof schema !== \"object\") return undefined;\n const anySchema = schema as any;\n return anySchema._zod?.def ?? anySchema._def;\n}\n\n/**\n * Try to get a human-readable description.\n * Zod 4: use metadata/registry; fallback to internal def.description.\n * Zod 3: only internal def.description exists.\n */\nfunction getDescription(schema: ZodAny): string | undefined {\n const anyZ: any = z as any;\n\n // Zod 4 global registry metadata, if present\n const registry = anyZ.globalRegistry?.get\n ? anyZ.globalRegistry.get(schema)\n : undefined;\n if (registry && typeof registry.description === \"string\") {\n return registry.description;\n }\n\n // Legacy / internal description\n const def = getDef(schema);\n if (def && typeof def.description === \"string\") {\n return def.description;\n }\n\n return undefined;\n}\n\n/**\n * Peel off wrappers (effects, optional, nullable, default) and\n * return the inner schema + flags.\n *\n * Supports:\n * - Zod 3: ZodEffects, ZodOptional, ZodNullable, ZodDefault\n * - Zod 4: ZodOptional, ZodNullable, ZodDefault\n */\nfunction unwrap(schema: ZodAny): {\n base: ZodAny;\n optional: boolean;\n nullable: boolean;\n} {\n let s: ZodAny = schema;\n let optional = false;\n let nullable = false;\n\n // Zod 3 only (undefined in Zod 4)\n const ZodEffectsCtor: any = (z as any).ZodEffects;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // Zod 3: ZodEffects wrapper\n if (ZodEffectsCtor && s instanceof ZodEffectsCtor) {\n const def = getDef(s) || {};\n const sourceType =\n typeof (s as any).sourceType === \"function\"\n ? (s as any).sourceType()\n : def.schema;\n if (!sourceType) break;\n s = sourceType;\n continue;\n }\n\n // Zod 3 + 4: optional/nullable/default wrappers\n if (s instanceof z.ZodOptional) {\n optional = true;\n const def = getDef(s);\n s = (def && def.innerType) || s; // innerType exists in both 3 & 4\n continue;\n }\n\n if (s instanceof z.ZodNullable) {\n nullable = true;\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n if (s instanceof z.ZodDefault) {\n const def = getDef(s);\n s = (def && def.innerType) || s;\n continue;\n }\n\n break;\n }\n\n return { base: s, optional, nullable };\n}\n\nexport function introspectSchema(\n schema: ZodAny | undefined\n): SerializableSchemaNode | undefined {\n if (!schema) return undefined;\n\n const { base, optional, nullable } = unwrap(schema);\n const def = getDef(base);\n\n const node: SerializableSchemaNode = {\n kind: inferKind(base),\n optional: optional || undefined,\n nullable: nullable || undefined,\n description: getDescription(base),\n };\n\n // OBJECT\n if (base instanceof z.ZodObject) {\n // Zod 3: _def.shape() (function)\n // Zod 4: .shape getter returns an object\n const rawShape: any =\n (base as any).shape ?? (def && typeof def.shape === \"function\"\n ? def.shape()\n : def?.shape);\n\n const shape =\n typeof rawShape === \"function\" ? rawShape() : rawShape ?? {};\n\n const props: Record<string, SerializableSchemaNode> = {};\n for (const key of Object.keys(shape)) {\n const child = shape[key] as ZodAny;\n const childNode = introspectSchema(child);\n if (childNode) props[key] = childNode;\n }\n node.properties = props;\n }\n\n // ARRAY\n if (base instanceof z.ZodArray) {\n // Zod 3: def.type is inner schema\n // Zod 4: def.element is inner schema\n const inner =\n (def && (def.element as ZodAny)) ||\n (def && (def.type as ZodAny)) ||\n undefined;\n if (inner) {\n node.element = introspectSchema(inner);\n }\n }\n\n // UNION\n if (base instanceof z.ZodUnion) {\n const options: ZodAny[] = (def && def.options) || [];\n node.union = options\n .map((opt) => introspectSchema(opt))\n .filter(Boolean) as SerializableSchemaNode[];\n }\n\n // LITERAL\n if (base instanceof z.ZodLiteral) {\n if (def) {\n // Zod 4: def.values (multi-literal)\n if (Array.isArray(def.values)) {\n node.literal =\n def.values.length === 1 ? def.values[0] : def.values.slice();\n } else {\n // Zod 3: def.value\n node.literal = def.value;\n }\n }\n }\n\n // ENUM\n if (base instanceof z.ZodEnum) {\n if (def) {\n if (Array.isArray(def.values)) {\n // Zod 3\n node.enumValues = def.values.slice();\n } else if (def.entries && typeof def.entries === \"object\") {\n // Zod 4: entries is a { key: value } map\n node.enumValues = Object.values(def.entries).map((v: unknown) =>\n String(v)\n );\n }\n }\n }\n\n return node;\n}\n\nfunction inferKind(schema: ZodAny): string {\n // This path still uses instanceof; it works with Zod 4 Classic\n // (importing from \"zod\"). Anything unknown falls back to \"unknown\".\n if (schema instanceof z.ZodString) return \"string\";\n if (schema instanceof z.ZodNumber) return \"number\";\n if (schema instanceof z.ZodBoolean) return \"boolean\";\n if (schema instanceof z.ZodBigInt) return \"bigint\";\n if (schema instanceof z.ZodDate) return \"date\";\n if (schema instanceof z.ZodArray) return \"array\";\n if (schema instanceof z.ZodObject) return \"object\";\n if (schema instanceof z.ZodUnion) return \"union\";\n if (schema instanceof z.ZodLiteral) return \"literal\";\n if (schema instanceof z.ZodEnum) return \"enum\";\n if (schema instanceof z.ZodRecord) return \"record\";\n if (schema instanceof z.ZodTuple) return \"tuple\";\n if (schema instanceof z.ZodUnknown) return \"unknown\";\n if (schema instanceof z.ZodAny) return \"any\";\n\n return \"unknown\";\n}\n","// serializer.ts\nimport { AnyLeaf, MethodCfg } from \"../core/routesV3.core\";\nimport { introspectSchema, SerializableSchemaNode } from \"./schemaIntrospection\";\n\ntype SerializableMethodCfg = Pick<\n MethodCfg,\n | \"description\"\n | \"summary\"\n | \"docsGroup\"\n | \"tags\"\n | \"deprecated\"\n | \"stability\"\n | \"feed\"\n | \"docsMeta\"\n> & {\n hasBody: boolean;\n hasQuery: boolean;\n hasParams: boolean;\n hasOutput: boolean;\n\n // NEW: full Zod ASTs\n bodySchema?: SerializableSchemaNode;\n querySchema?: SerializableSchemaNode;\n paramsSchema?: SerializableSchemaNode;\n outputSchema?: SerializableSchemaNode;\n};\n\nexport type SerializableLeaf = {\n method: string;\n path: string;\n cfg: SerializableMethodCfg;\n};\n\nexport function serializeLeaf(leaf: AnyLeaf): SerializableLeaf {\n const cfg = leaf.cfg;\n\n const tags = Array.isArray(cfg.tags) ? cfg.tags.slice() : [];\n\n return {\n method: leaf.method, // 'get' | 'post' | ...\n path: leaf.path,\n cfg: {\n description: cfg.description,\n summary: cfg.summary,\n docsGroup: cfg.docsGroup,\n tags,\n deprecated: cfg.deprecated,\n stability: cfg.stability,\n feed: !!cfg.feed,\n docsMeta: cfg.docsMeta,\n hasBody: !!cfg.bodySchema || !!cfg.bodyFiles?.length,\n hasQuery: !!cfg.querySchema,\n hasParams: !!cfg.paramsSchema,\n hasOutput: !!cfg.outputSchema,\n\n bodySchema: introspectSchema(cfg.bodySchema),\n querySchema: introspectSchema(cfg.querySchema),\n paramsSchema: introspectSchema(cfg.paramsSchema),\n outputSchema: introspectSchema(cfg.outputSchema),\n },\n };\n}\n","import { AnyLeaf } from \"../core/routesV3.core\";\n\nimport { serializeLeaf } from \"./serializer\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n/**\n * Updated CSS including pastel tag generation,\n * refined header layouts, and sticky overview integration.\n */\nconst CSS_STYLES = `:root {\n --bg-root: #020617;\n --bg-card: rgba(15, 23, 42, 0.6);\n --bg-card-hover: rgba(30, 41, 59, 0.7);\n --bg-glass: rgba(15, 23, 42, 0.90);\n --border-subtle: rgba(148, 163, 184, 0.2);\n\n --text-main: #f8fafc;\n --text-muted: #94a3b8;\n --text-accent: #c4b5fd;\n\n --accent-primary: #a855f7;\n --accent-glow: rgba(168, 85, 247, 0.25);\n\n /* Method Colors */\n --method-get: #4ade80; --method-get-bg: rgba(34, 197, 94, 0.15);\n --method-post: #60a5fa; --method-post-bg: rgba(59, 130, 246, 0.15);\n --method-put: #facc15; --method-put-bg: rgba(234, 179, 8, 0.15);\n --method-patch: #2dd4bf; --method-patch-bg: rgba(45, 212, 191, 0.15);\n --method-delete: #f87171; --method-delete-bg: rgba(248, 113, 113, 0.15);\n\n --radius-card: 12px;\n --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.4);\n\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\n /* Tag Pastel Palette (Generated via class logic) */\n --tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5; /* Red */\n --tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d; /* Amber */\n --tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac; /* Green */\n --tag-3-bg: rgba(165, 243, 252, 0.2); --tag-3-fg: #67e8f9; /* Cyan */\n --tag-4-bg: rgba(191, 219, 254, 0.2); --tag-4-fg: #93c5fd; /* Blue */\n --tag-5-bg: rgba(233, 213, 255, 0.2); --tag-5-fg: #d8b4fe; /* Purple */\n --tag-6-bg: rgba(251, 207, 232, 0.2); --tag-6-fg: #f9a8d4; /* Pink */\n}\n\n* { box-sizing: border-box; }\n\nhtml, body {\n height: 100%;\n margin: 0;\n padding: 0;\n background-color: var(--bg-root);\n color: var(--text-main);\n font-family: system-ui, -apple-system, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n\nbody {\n background-image:\n radial-gradient(circle at 15% 10%, rgba(88, 28, 135, 0.15), transparent 40%),\n radial-gradient(circle at 85% 80%, rgba(15, 23, 42, 1), transparent 40%);\n background-attachment: fixed;\n}\n\n.page {\n min-height: 100vh;\n padding: 20px 40px 60px;\n max-width: 1600px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* --- Header --- */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.header-title h1 {\n font-size: 28px;\n margin: 0;\n font-weight: 700;\n letter-spacing: -0.02em;\n background: linear-gradient(to right, #e2e8f0, #a855f7);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* --- Sticky Controls & Overview --- */\n.controls-container {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--bg-glass);\n backdrop-filter: blur(16px);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n padding: 12px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);\n}\n\n/* Top Row: Filters */\n.filters-row {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n width: 100%;\n}\n\n/* New Wrapper for Search and Methods */\n.left-column {\n display: flex;\n flex-direction: column; /* Stack Search and Methods vertically */\n gap: 16px; /* Gap between search and methods */\n flex-basis: 300px; /* Fixed width for the left column */\n min-width: 250px;\n}\n\n.search-box {\n /* No change to internal search box styling, but remove flex properties */\n width: 100%; /* Make search fill the left-column width */\n position: relative;\n}\n\n/* Tag Filter Group */\n.filter-group.tag-filters-container {\n flex: 1; /* Tags take up all remaining space on the right */\n min-width: 200px;\n}\n\n/* Method Filter Group */\n.filter-group.method-filters-container {\n /* Stays inside the left-column, no need for 100% basis */\n}\n.search-input {\n width: 100%;\n background: rgba(2, 6, 23, 0.6);\n border: 1px solid var(--border-subtle);\n color: var(--text-main);\n padding: 8px 12px 8px 32px;\n border-radius: 6px;\n font-size: 13px;\n transition: all 0.2s;\n}\n.search-input:focus {\n outline: none;\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 2px var(--accent-glow);\n}\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-muted);\n pointer-events: none;\n font-size: 12px;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.filter-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--text-muted);\n font-weight: 700;\n}\n.checkbox-group {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* Bottom Row: Group Overview Chips */\n.overview-row {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--border-subtle);\n align-items: center;\n}\n.overview-label {\n font-size: 10px;\n text-transform: uppercase;\n color: var(--text-muted);\n font-weight: 700;\n margin-right: 4px;\n}\n\n/* Chips/Pills */\n.pill-checkbox { cursor: pointer; user-select: none; }\n.pill-checkbox input { display: none; }\n.pill-checkbox span {\n display: inline-block;\n padding: 3px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: rgba(30, 41, 59, 0.5);\n color: var(--text-muted);\n border: 1px solid transparent;\n transition: all 0.15s;\n}\n.pill-checkbox input:checked + span {\n background: rgba(168, 85, 247, 0.15);\n color: var(--accent-primary);\n border-color: var(--accent-primary);\n}\n\n/* Specialized Colored Tag Pills for Filters */\n.pill-checkbox.colored-tag input:checked + span {\n background: var(--tag-bg);\n color: var(--tag-fg);\n border-color: var(--tag-fg);\n}\n.pill-checkbox.colored-tag span {\n /* Subtle hint of color even when unchecked? Optional. kept neutral for now */\n}\n\n/* Group Jump Links */\n.group-chip {\n font-size: 11px;\n text-decoration: none;\n color: var(--text-accent);\n background: rgba(168, 85, 247, 0.08);\n padding: 3px 8px;\n border-radius: 4px;\n transition: all 0.2s;\n border: 1px solid transparent;\n}\n.group-chip:hover {\n background: rgba(168, 85, 247, 0.2);\n border-color: var(--accent-primary);\n color: #fff;\n}\n\n/* --- Main Content --- */\n.api-group { margin-bottom: 40px; scroll-margin-top: 180px; }\n.group-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n}\n.group-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--text-main);\n margin: 0;\n}\n.group-actions button {\n background: transparent;\n border: 1px solid var(--border-subtle);\n color: var(--text-muted);\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n margin-left: 8px;\n transition: all 0.2s;\n}\n.group-actions button:hover {\n color: var(--text-main);\n background: rgba(255,255,255,0.05);\n}\n\n.cards-list { display: flex; flex-direction: column; gap: 12px; }\n\n/* --- Endpoint Card --- */\n.endpoint-card {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-card);\n overflow: hidden;\n transition: all 0.2s ease;\n cursor: pointer;\n backdrop-filter: blur(10px);\n}\n.endpoint-card:hover {\n background: var(--bg-card-hover);\n border-color: rgba(148, 163, 184, 0.4);\n box-shadow: var(--shadow-card);\n}\n.endpoint-card[data-expanded=\"true\"] {\n background: rgba(30, 41, 59, 0.6);\n border-color: var(--accent-primary);\n box-shadow: 0 0 0 1px var(--accent-glow), var(--shadow-card);\n}\n\n/* Card Header */\n.card-header {\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 12px; /* Space between method, path, tags */\n flex-wrap: wrap; /* Allow wrapping on very small screens if needed, but row pref */\n}\n\n/* Item 1: Method */\n.method-badge {\n font-size: 11px;\n font-weight: 800;\n text-transform: uppercase;\n padding: 4px 8px;\n border-radius: 4px;\n min-width: 55px;\n text-align: center;\n letter-spacing: 0.05em;\n flex-shrink: 0;\n}\n.m-GET { background: var(--method-get-bg); color: var(--method-get); border: 1px solid rgba(74, 222, 128, 0.3); }\n.m-POST { background: var(--method-post-bg); color: var(--method-post); border: 1px solid rgba(96, 165, 250, 0.3); }\n.m-PUT { background: var(--method-put-bg); color: var(--method-put); border: 1px solid rgba(250, 204, 21, 0.3); }\n.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }\n.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }\n\n/* Item 2: Path (Clickable) */\n.path-container {\n font-family: var(--font-mono);\n font-size: 13px;\n color: var(--text-main);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: background 0.2s;\n}\n.path-container:hover {\n background: rgba(255,255,255,0.05);\n}\n.path-text {\n font-weight: 500;\n}\n.copy-icon {\n opacity: 0;\n color: var(--text-muted);\n font-size: 10px;\n transition: opacity 0.2s;\n}\n.path-container:hover .copy-icon { opacity: 1; }\n\n/* Item 3: Tags (Aligned right of path) */\n.tags-container {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n.status-badge {\n font-size: 9px;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: 3px;\n border: 1px solid transparent;\n font-weight: 600;\n letter-spacing: 0.03em;\n}\n/* Tag Colors mapped via JS classes */\n.tag-0 { background: var(--tag-0-bg); color: var(--tag-0-fg); border-color: var(--tag-0-fg); }\n.tag-1 { background: var(--tag-1-bg); color: var(--tag-1-fg); border-color: var(--tag-1-fg); }\n.tag-2 { background: var(--tag-2-bg); color: var(--tag-2-fg); border-color: var(--tag-2-fg); }\n.tag-3 { background: var(--tag-3-bg); color: var(--tag-3-fg); border-color: var(--tag-3-fg); }\n.tag-4 { background: var(--tag-4-bg); color: var(--tag-4-fg); border-color: var(--tag-4-fg); }\n.tag-5 { background: var(--tag-5-bg); color: var(--tag-5-fg); border-color: var(--tag-5-fg); }\n.tag-6 { background: var(--tag-6-bg); color: var(--tag-6-fg); border-color: var(--tag-6-fg); }\n\n.not-implemented { border-color: var(--method-delete); color: var(--method-delete); background: var(--method-delete-bg); }\n\n/* Spacer to push expand icon to right */\n.header-spacer { flex: 1; }\n\n.expand-icon {\n color: var(--text-muted);\n transition: transform 0.3s ease;\n font-size: 10px;\n margin-left: 8px;\n}\n.endpoint-card[data-expanded=\"true\"] .expand-icon {\n transform: rotate(180deg);\n color: var(--text-main);\n}\n\n/* Card Body */\n.card-body {\n display: none;\n padding: 0 16px 16px;\n border-top: 1px solid var(--border-subtle);\n margin-top: 4px;\n cursor: default;\n}\n.endpoint-card[data-expanded=\"true\"] .card-body {\n display: block;\n animation: slideDown 0.2s ease-out;\n}\n@keyframes slideDown {\n from { opacity: 0; transform: translateY(-5px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.section-block { margin-top: 18px; }\n.section-title {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n color: var(--text-muted);\n margin-bottom: 8px;\n border-bottom: 1px solid var(--border-subtle);\n padding-bottom: 4px;\n}\n.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }\n\n/* Tables */\n.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }\n.schema-table th {\n text-align: left; color: var(--text-muted); font-weight: 600;\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); background: rgba(0,0,0,0.2);\n}\n.schema-table td {\n padding: 6px 8px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; color: var(--text-muted);\n}\n.col-name { font-family: var(--font-mono); color: var(--text-accent) !important; width: 20%; }\n.col-type { font-family: var(--font-mono); color: #93c5fd !important; width: 15%; }\n.req-badge { font-size: 9px; text-transform: uppercase; padding: 2px 4px; border-radius: 2px; }\n.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }\n.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }\n\n.empty-message { text-align: center; padding: 40px; color: var(--text-muted); }\n`;\n\n/**\n * Client-side logic.\n */\nconst DOCS_JS = `\n(function() {\n let leaves = [];\n try {\n leaves = JSON.parse(document.getElementById('leaf-data').textContent || '[]');\n } catch(e) { console.error('Failed to parse docs', e); }\n\n // State\n let filters = {\n search: '',\n methods: new Set(),\n tags: new Set()\n };\n\n // DOM Elements\n const elRouteList = document.getElementById('routeList');\n const elOverview = document.getElementById('overviewList');\n const elSearch = document.getElementById('searchInput');\n const elMethodFilters = document.getElementById('methodFilters');\n const elTagFilters = document.getElementById('tagFilters');\n\n // Constants\n const PALETTE_SIZE = 7; // matches .tag-0 to .tag-6 CSS\n\n // Initialization\n function init() {\n populateFilters();\n render();\n setupGlobalListeners();\n }\n\n // Tag Coloring Logic\n function getTagColorClass(tagName) {\n if (tagName === 'not-implemented') return 'not-implemented';\n // Simple string hash to select color index\n let hash = 0;\n for (let i = 0; i < tagName.length; i++) {\n hash = tagName.charCodeAt(i) + ((hash << 5) - hash);\n }\n const index = Math.abs(hash % PALETTE_SIZE);\n return 'tag-' + index;\n }\n\n // Custom Group Sort: Alpha, but UNGROUPED last\n function sortGroups(a, b) {\n const nameA = a.toUpperCase();\n const nameB = b.toUpperCase();\n if (nameA === 'UNGROUPED') return 1;\n if (nameB === 'UNGROUPED') return -1;\n return nameA.localeCompare(nameB);\n }\n\n function populateFilters() {\n // Extract unique values\n const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));\n const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));\n\n // Setup Method Checkboxes\n const sortedMethods = Array.from(allMethods).sort();\n elMethodFilters.innerHTML = sortedMethods.map(m => \\`\n <label class=\"pill-checkbox\">\n <input type=\"checkbox\" value=\"\\${m}\" checked onchange=\"updateFilters()\">\n <span>\\${m}</span>\n </label>\n \\`).join('');\n sortedMethods.forEach(m => filters.methods.add(m));\n\n // Setup Tag Checkboxes (Left Aligned, Colored)\n if (allTags.size > 0) {\n elTagFilters.innerHTML = Array.from(allTags).sort().map(t => {\n const colorClass = getTagColorClass(t);\n const styleVars = 'style=\"--tag-bg:var(--' + colorClass + '-bg); --tag-fg:var(--' + colorClass + '-fg);\"';\n return \\`\n <label class=\"pill-checkbox colored-tag\" \\${styleVars}>\n <input type=\"checkbox\" value=\"\\${t}\" onchange=\"updateFilters()\">\n <span>\\${t}</span>\n </label>\n \\`;\n }).join('');\n }\n }\n\n window.updateFilters = function() {\n filters.search = elSearch.value.toLowerCase();\n filters.methods.clear();\n elMethodFilters.querySelectorAll('input:checked').forEach(cb => filters.methods.add(cb.value));\n filters.tags.clear();\n elTagFilters.querySelectorAll('input:checked').forEach(cb => filters.tags.add(cb.value));\n render();\n }\n\n // Core Render Logic\n function render() {\n // 1. Filter Data\n const filtered = leaves.filter(leaf => {\n const m = (leaf.method || 'GET').toUpperCase();\n const t = (leaf.cfg && leaf.cfg.tags) ? leaf.cfg.tags : [];\n const path = (leaf.path || '').toLowerCase();\n const summary = (leaf.cfg && leaf.cfg.summary || '').toLowerCase();\n\n if (!filters.methods.has(m)) return false;\n if (filters.tags.size > 0 && !t.some(tag => filters.tags.has(tag))) return false;\n if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;\n\n return true;\n });\n\n if (filtered.length === 0) {\n elRouteList.innerHTML = '<div class=\"empty-message\">No endpoints match your filters.</div>';\n elOverview.innerHTML = '';\n return;\n }\n\n // 2. Group Data\n const groups = {};\n filtered.forEach(leaf => {\n const gName = (leaf.cfg && leaf.cfg.docsGroup) ? leaf.cfg.docsGroup : 'UNGROUPED';\n if (!groups[gName]) groups[gName] = [];\n groups[gName].push(leaf);\n });\n\n const sortedGroupNames = Object.keys(groups).sort(sortGroups);\n\n // 3. Render Overview Chips (Inside Sticky Header)\n // Note: We include a small label \"GROUPS:\" or similar if needed, or just chips.\n elOverview.innerHTML = '<span class=\"overview-label\">JUMP TO:</span>' + sortedGroupNames.map(gName => \\`\n <a href=\"#group-\\${gName}\" class=\"group-chip\" onclick=\"scrollToGroup(event, '\\${gName}')\">\n \\${gName}\n </a>\n \\`).join('');\n\n // 4. Render Groups & Cards\n const html = sortedGroupNames.map(gName => {\n const routes = groups[gName];\n // Sort Routes: Path ASC, then Method\n routes.sort((a, b) => {\n const pA = a.path || '';\n const pB = b.path || '';\n if (pA < pB) return -1;\n if (pA > pB) return 1;\n return (a.method || '').localeCompare(b.method || '');\n });\n\n return \\`\n <div class=\"api-group\" id=\"group-\\${gName}\">\n <div class=\"group-header\">\n <h2 class=\"group-title\">\\${gName}</h2>\n <div class=\"group-actions\">\n <button onclick=\"toggleGroup('\\${gName}', true)\">Expand All</button>\n <button onclick=\"toggleGroup('\\${gName}', false)\">Collapse All</button>\n </div>\n </div>\n <div class=\"cards-list\">\n \\${routes.map(renderCard).join('')}\n </div>\n </div>\n \\`;\n }).join('');\n\n elRouteList.innerHTML = html;\n }\n\n // Card Renderer\n function renderCard(leaf) {\n const method = (leaf.method || 'GET').toUpperCase();\n const path = leaf.path || '/';\n const cfg = leaf.cfg || {};\n const summary = cfg.summary || '';\n const description = cfg.description || '';\n const tags = cfg.tags || [];\n\n // Header Construction: Method | Path | Tags | Spacer | Chevron\n const tagBadges = tags.map(t => {\n const cClass = getTagColorClass(t);\n return \\`<span class=\"status-badge \\${cClass}\">\\${t}</span>\\`;\n }).join('');\n\n const headerHtml = \\`\n <div class=\"card-header\">\n <span class=\"method-badge m-\\${method}\">\\${method}</span>\n\n <div class=\"path-container\" onclick=\"copyText(event, '\\${path}')\" title=\"Click to copy path\">\n <span class=\"path-text\">\\${path}</span>\n <span class=\"copy-icon\">📋</span>\n </div>\n\n <div class=\"tags-container\">\n \\${tagBadges}\n </div>\n\n <div class=\"header-spacer\"></div>\n <div class=\"expand-icon\">▼</div>\n </div>\n \\`;\n\n // Body Construction (Preserved structure)\n let contentHtml = \\`<div class=\"section-block\"><div class=\"summary-text\">\n <strong>\\${escape(summary)}</strong>\n \\${description ? '<br><span style=\"font-size:12px; opacity:0.7\">' + escape(description) + '</span>' : ''}\n </div></div>\\`;\n\n if (cfg.paramsSchema || cfg.querySchema) {\n contentHtml += '<div class=\"section-block\"><div class=\"section-title\">Parameters</div>';\n if (cfg.paramsSchema) contentHtml += renderSchemaTable(cfg.paramsSchema, 'Path Parameters');\n if (cfg.querySchema) contentHtml += renderSchemaTable(cfg.querySchema, 'Query Parameters');\n contentHtml += '</div>';\n }\n\n if (cfg.bodySchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Request Body \\${cfg.hasBody ? '' : '(Optional)'}</div>\n \\${renderSchemaTable(cfg.bodySchema)}\n </div>\n \\`;\n }\n\n if (cfg.outputSchema) {\n contentHtml += \\`\n <div class=\"section-block\">\n <div class=\"section-title\">Response Schema</div>\n \\${renderSchemaTable(cfg.outputSchema)}\n </div>\n \\`;\n }\n\n return \\`\n <article class=\"endpoint-card\" data-expanded=\"false\" onclick=\"toggleCard(this, event)\">\n \\${headerHtml}\n <div class=\"card-body\" onclick=\"event.stopPropagation()\">\n \\${contentHtml}\n </div>\n </article>\n \\`;\n }\n\n function renderSchemaTable(node, title) {\n let rows = '';\n if (node.kind === 'object' && node.properties) {\n Object.keys(node.properties).forEach(key => {\n const prop = node.properties[key];\n const reqClass = prop.optional ? 'req-false' : 'req-true';\n const reqText = prop.optional ? 'OPT' : 'REQ';\n rows += \\`\n <tr>\n <td class=\"col-name\">\\${key}</td>\n <td class=\"col-type\">\\${getTypeLabel(prop)}</td>\n <td><span class=\"req-badge \\${reqClass}\">\\${reqText}</span></td>\n <td>\\${escape(prop.description || '')}</td>\n </tr>\n \\`;\n });\n } else {\n rows = \\`<tr><td colspan=\"4\">Type: \\${getTypeLabel(node)}</td></tr>\\`;\n }\n return \\`\n \\${title ? '<div style=\"font-size:11px; font-weight:600; margin-bottom:4px; color:#64748b\">' + title + '</div>' : ''}\n <table class=\"schema-table\">\\${rows}</table>\n \\`;\n }\n\n function getTypeLabel(node) {\n if (!node) return 'any';\n if (node.kind === 'array') return \\`\\${getTypeLabel(node.element)}[]\\`;\n if (node.enumValues) return \\`enum(\\${node.enumValues.join('|')})\\`;\n return node.kind || 'any';\n }\n\n function escape(str) {\n if (!str) return '';\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n window.toggleCard = function(card, e) {\n if (e.target.closest('.path-container')) return; // Don't toggle on path click\n const isExpanded = card.getAttribute('data-expanded') === 'true';\n card.setAttribute('data-expanded', !isExpanded);\n };\n\n window.toggleGroup = function(gName, expand) {\n const group = document.getElementById('group-' + gName);\n if (!group) return;\n group.querySelectorAll('.endpoint-card').forEach(c => c.setAttribute('data-expanded', expand));\n };\n\n window.copyText = function(e, text) {\n e.stopPropagation();\n navigator.clipboard.writeText(text);\n // Could add visual feedback here\n };\n\n window.scrollToGroup = function(e, gName) {\n e.preventDefault();\n const el = document.getElementById('group-' + gName);\n if(el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });\n }\n\n function setupGlobalListeners() {\n elSearch.addEventListener('input', updateFilters);\n }\n\n init();\n})();\n`;\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n const leavesJson = JSON.stringify(leaves.map(serializeLeaf));\n const nonceAttr = options.cspNonce ? ` nonce=\"${options.cspNonce}\"` : \"\";\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Documentation</title>\n\n <style${nonceAttr}>${CSS_STYLES}</style>\n ${options.inlineCss ? `<style${nonceAttr}>${options.inlineCss}</style>` : \"\"}\n</head>\n<body>\n <div class=\"page\">\n <header class=\"header\">\n <div class=\"header-title\">\n <h1>API Reference</h1>\n </div>\n </header>\n\n <div class=\"controls-container\">\n <div class=\"filters-row\">\n\n <div class=\"left-column\">\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Search</div>\n\n <div class=\"search-box\">\n <span class=\"search-icon\">🔍</span>\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Filter endpoints...\">\n </div>\n </div>\n\n <div class=\"filter-group method-filters-container\">\n <div class=\"filter-label\">Methods</div>\n <div class=\"checkbox-group\" id=\"methodFilters\"></div>\n </div>\n </div>\n\n <div class=\"filter-group tag-filters-container\" id=\"tagFilterContainer\">\n <div class=\"filter-label\">Tags</div>\n <div class=\"checkbox-group\" id=\"tagFilters\"></div>\n </div>\n </div>\n\n <div class=\"overview-row\" id=\"overviewList\"></div>\n </div>\n\n <main id=\"routeList\">\n <div style=\"text-align:center; padding:40px; color:#64748b\">Loading API definition...</div>\n </main>\n </div>\n\n <script id=\"leaf-data\" type=\"application/json\">${leavesJson}</script>\n <script${nonceAttr}>${DOCS_JS}</script>\n ${options.inlineClientScript ? `<script${nonceAttr}>${options.inlineClientScript}</script>` : \"\"}\n</body>\n</html>\n `;\n}\n","// renderLeafDocsHTML.ts\nimport { AnyLeaf } from \"../core/routesV3.core\";\nimport { renderLeafDocsHTML as LeafDocsPage } from \"./LeafDocsPage\";\n\ninterface RenderOptions {\n cspNonce?: string;\n inlineCss?: string;\n inlineClientScript?: string;\n}\n\nexport function renderLeafDocsHTML(\n leaves: AnyLeaf[],\n options: RenderOptions = {}\n): string {\n return LeafDocsPage(leaves, options)\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAubnB,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiTT,SAAS,mBACd,QACA,UAAyB,CAAC,GAClB;AACR,QAAM,aAAa,KAAK,UAAU,OAAO,IAAI,aAAa,CAAC;AAC3D,QAAM,YAAY,QAAQ,WAAW,WAAW,QAAQ,QAAQ,MAAM;AAEtE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQC,SAAS,IAAI,UAAU;AAAA,IAC7B,QAAQ,YAAY,SAAS,SAAS,IAAI,QAAQ,SAAS,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDA4C3B,UAAU;AAAA,WAClD,SAAS,IAAI,OAAO;AAAA,IAC3B,QAAQ,qBAAqB,UAAU,SAAS,IAAI,QAAQ,kBAAkB,cAAc,EAAE;AAAA;AAAA;AAAA;AAIlG;;;AC7yBO,SAASC,oBACd,QACA,UAAyB,CAAC,GAClB;AACR,SAAO,mBAAa,QAAQ,OAAO;AACrC;","names":["z","z","mergeSchemas","z","renderLeafDocsHTML"]}
|