@lpdjs/firestore-repo-service 2.2.1 → 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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/servers/admin/router.ts","../../../src/shared/date-config.ts","../../../src/servers/admin/index-url.ts","../../../src/servers/crud/handlers.ts","../../../src/servers/crud/openapi.ts","../../../src/servers/crud/index.ts"],"names":["compilePath","path","paramNames","src","c","_match","name","extractPath","req","raw","idx","MiniRouter","_req","res","err","middleware","handler","method","pattern","matchedRoute","params","route","m","i","enrichedReq","finalHandler","index","next","mw","currentMode","getDateHandling","isTimestampLike","v","coerceToDate","value","Timestamp","d","maybeNormalize","RANGE_OPS","ARRAY_OPS","toIndexOrder","dir","collectionIdFromPath","segments","buildIndexUrl","projectId","collectionId","isGroup","filters","sort","fields","seen","f","buildExemptionUrl","lastDir","buildCompositeUrl","databaseId","resource","payload","pbString","pbInt","pbMessage","encodeIndexField","urlDbId","encoded","toBase64","extractIndexUrl","message","pbVarint","n","out","pbTag","fieldNumber","wireType","bytes","bin","b64","field","extractProjectId","ref","r","candidates","isMissingIndexError","fe","toQueryError","ctx","isIndex","indexUrl","colId","sendJson","data","status","sendSuccess","meta","sendError","error","sendQueryError","fallbackMessage","verbose","qe","_idChars","generateFirestoreId","id","wrapDateSchemas","schema","def","typeName","z","shape","wrapped","k","inner","dflt","pickSchemaFields","systemKeys","picked","source","topLevel","validateData","partial","targetSchema","partialSchema","e","parseFilters","query","filterableFields","allowedFields","opMap","key","rawVal","val","match","op","opKey","parsedVal","parseValue","num","serializeCursor","snapshot","deserializeCursor","entry","cursor","docId","colRef","createCrudHandlers","registry","basePath","getRepoEntry","repoName","extractPathArgs","doc","pathKey","fullPath","args","fetchDocById","getterName","getter","handleList","ctxFilters","ctxSort","pageSize","direction","orderBy","orderDir","selectStr","select","s","includes","inc","queryOptions","cursorObj","result","responseData","handleQuery","body","w","validIncludes","allowed","invalid","group","handleGet","handleCreate","validation","customError","created","missingKeys","parentIds","handleUpdate","existingDoc","pathArgs","updated","handleDelete","handleOptions","zodToJsonSchema","schemaRef","errorResponse","description","successResponse","dataSchema","listResponse","itemSchema","paginationParams","filterParams","ops","queryBodySchema","buildPathsForEntry","base","modelSchemaName","createSchemaName","updateSchemaName","paths","tag","collectionPath","documentPath","idParam","capitalize","singularize","docOps","generateOpenAPISpec","options","title","version","servers","auth","schemas","allPaths","tags","modelName","createName","updateName","buildShape","fieldList","top","createShape","updateShape","entryPaths","securitySchemes","security","scalarDocsHtml","specUrl","getLinkBase","staticBasePath","project","region","target","service","host","readRawBody","createCrudServer","repos","parseBody","extraMiddleware","httpsOptions","cfg","resolvedSchema","mutableFields","createFields","fc","roles","role","parentKeys","pk","handlers","openapi","openapiOpts","_specCache","getSpec","authType","router","_res","realm","expected","specPath","docsPath","spec","html"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Minimal zero-dependency HTTP router for Firebase Functions.\n * Compatible with any Express-like (req, res) handler.\n *\n * Supports:\n * - Named path parameters (e.g. \"/repos/:name/:id\")\n * - GET, POST, DELETE methods\n * - Global middleware (before each route)\n * - 404 / error fallbacks\n *\n * @example\n * ```typescript\n * import { MiniRouter } from \"@lpdjs/firestore-repo-service/servers/admin\";\n *\n * // Create router\n * const router = new MiniRouter();\n *\n * // Add global middleware (executed before every route)\n * router.use(async (req, res, next) => {\n * console.log(`${req.method} ${req.url}`);\n * await next();\n * });\n *\n * // Auth middleware\n * router.use((req, res, next) => {\n * if (!req.headers?.authorization) {\n * res.status(401).send(\"Unauthorized\");\n * return;\n * }\n * next();\n * });\n *\n * // Define routes with path parameters\n * router.get(\"/users\", async (req, res) => {\n * res.json({ users: await getAllUsers() });\n * });\n *\n * router.get(\"/users/:id\", async (req, res) => {\n * const user = await getUser(req.params.id); // Access path params\n * if (!user) {\n * res.status(404).send(\"User not found\");\n * return;\n * }\n * res.json(user);\n * });\n *\n * router.post(\"/users\", async (req, res) => {\n * const user = await createUser(req.body);\n * res.status(201).json(user);\n * });\n *\n * router.delete(\"/users/:id\", async (req, res) => {\n * await deleteUser(req.params.id);\n * res.status(204).end();\n * });\n *\n * // Custom 404 handler\n * router.onNotFound((req, res) => {\n * res.status(404).json({ error: \"Route not found\", path: req.url });\n * });\n *\n * // Custom error handler\n * router.onError((err, req, res) => {\n * console.error(\"Error:\", err);\n * res.status(500).json({ error: \"Internal server error\" });\n * });\n *\n * // Use with Firebase Functions\n * export const api = onRequest(async (req, res) => {\n * await router.handle(req, res);\n * });\n * ```\n */\n\nexport type AnyReq = {\n method?: string;\n url?: string;\n /** Express originalUrl — preserved before any router stripping, contains the full path including the Firebase Functions prefix */\n originalUrl?: string;\n path?: string;\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n query?: Record<string, string | string[] | undefined>;\n};\n\nexport type AnyRes = {\n status: (code: number) => AnyRes;\n set: (key: string, value: string) => AnyRes;\n send: (body: string) => void;\n json: (body: unknown) => void;\n end: () => void;\n};\n\nexport type RouteParams = Record<string, string>;\n\nexport type RouteHandler = (\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n) => void | Promise<void>;\n\nexport type Middleware = (\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n next: () => void | Promise<void>,\n) => void | Promise<void>;\n\n// ---------------------------------------------------------------------------\n// Route matching\n// ---------------------------------------------------------------------------\n\ninterface CompiledRoute {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n}\n\nfunction compilePath(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n const src = path\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (c) => (c === \":\" ? c : `\\\\${c}`))\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_match, name: string) => {\n paramNames.push(name);\n return \"([^/]+)\";\n });\n\n return { pattern: new RegExp(`^${src}$`), paramNames };\n}\n\nfunction extractPath(req: AnyReq): string {\n const raw = req.path ?? req.url ?? \"/\";\n const idx = raw.indexOf(\"?\");\n return idx === -1 ? raw : raw.slice(0, idx);\n}\n\n// ---------------------------------------------------------------------------\n// Router class\n// ---------------------------------------------------------------------------\n\nexport class MiniRouter {\n private routes: CompiledRoute[] = [];\n private middlewares: Middleware[] = [];\n private notFoundHandler: RouteHandler = (_req, res) => {\n res.status(404).send(\"Not Found\");\n };\n private errorHandler: (err: unknown, req: AnyReq, res: AnyRes) => void = (\n err,\n _req,\n res,\n ) => {\n console.error(\"[MiniRouter]\", err);\n res.status(500).send(\"Internal Server Error\");\n };\n\n // ── Route registration ────────────────────────────────────────────────────\n\n use(middleware: Middleware): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n get(path: string, handler: RouteHandler): this {\n return this.addRoute(\"GET\", path, handler);\n }\n\n post(path: string, handler: RouteHandler): this {\n return this.addRoute(\"POST\", path, handler);\n }\n\n put(path: string, handler: RouteHandler): this {\n return this.addRoute(\"PUT\", path, handler);\n }\n\n patch(path: string, handler: RouteHandler): this {\n return this.addRoute(\"PATCH\", path, handler);\n }\n\n delete(path: string, handler: RouteHandler): this {\n return this.addRoute(\"DELETE\", path, handler);\n }\n\n onNotFound(handler: RouteHandler): this {\n this.notFoundHandler = handler;\n return this;\n }\n\n onError(handler: (err: unknown, req: AnyReq, res: AnyRes) => void): this {\n this.errorHandler = handler;\n return this;\n }\n\n private addRoute(method: string, path: string, handler: RouteHandler): this {\n const { pattern, paramNames } = compilePath(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n });\n return this;\n }\n\n // ── Dispatch ──────────────────────────────────────────────────────────────\n\n async handle(req: AnyReq, res: AnyRes): Promise<void> {\n const method = (req.method ?? \"GET\").toUpperCase();\n const path = extractPath(req);\n\n // Find matching route\n let matchedRoute: CompiledRoute | null = null;\n let params: RouteParams = {};\n\n for (const route of this.routes) {\n if (route.method !== method) continue;\n const m = path.match(route.pattern);\n if (m) {\n matchedRoute = route;\n params = {};\n route.paramNames.forEach((name, i) => {\n params[name] = decodeURIComponent(m[i + 1] ?? \"\");\n });\n break;\n }\n }\n\n const enrichedReq = Object.assign(req, { params });\n\n // Run middleware chain → then handler\n const handler = matchedRoute ? matchedRoute.handler : this.notFoundHandler;\n\n try {\n await this.runMiddlewareChain(enrichedReq, res, handler);\n } catch (err) {\n this.errorHandler(err, req, res);\n }\n }\n\n private async runMiddlewareChain(\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n finalHandler: RouteHandler,\n ): Promise<void> {\n let index = 0;\n\n const next = async (): Promise<void> => {\n if (index < this.middlewares.length) {\n const mw = this.middlewares[index++]!;\n await mw(req, res, next);\n } else {\n await finalHandler(req, res);\n }\n };\n\n await next();\n }\n}\n","import { Timestamp } from \"firebase-admin/firestore\";\n\nexport type DateHandlingMode = \"preserve\" | \"normalize\";\n\nlet currentMode: DateHandlingMode = \"preserve\";\n\nexport function setDateHandling(mode: DateHandlingMode): void {\n currentMode = mode;\n}\n\nexport function getDateHandling(): DateHandlingMode {\n return currentMode;\n}\n\nfunction isTimestampLike(\n v: unknown,\n): v is { _seconds: number; _nanoseconds: number } {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as { _seconds?: unknown })._seconds === \"number\" &&\n typeof (v as { _nanoseconds?: unknown })._nanoseconds === \"number\"\n );\n}\n\nexport function coerceToDate(value: unknown): Date | null {\n if (value === null || value === undefined) return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (value instanceof Timestamp) return value.toDate();\n if (isTimestampLike(value)) {\n return new Date(\n value._seconds * 1000 + Math.floor(value._nanoseconds / 1e6),\n );\n }\n if (typeof value === \"string\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n if (typeof value === \"number\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n if (typeof v !== \"object\" || v === null) return false;\n const proto = Object.getPrototypeOf(v);\n return proto === Object.prototype || proto === null;\n}\n\nexport function normalizeTimestamps<T>(value: T): T {\n if (value instanceof Timestamp) return value.toDate() as unknown as T;\n if (Array.isArray(value)) {\n return value.map((v) => normalizeTimestamps(v)) as unknown as T;\n }\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n out[k] = normalizeTimestamps(v);\n }\n return out as unknown as T;\n }\n return value;\n}\n\nexport function maybeNormalize<T>(value: T): T {\n return currentMode === \"normalize\" ? normalizeTimestamps(value) : value;\n}\n","/**\n * Generates a Firebase Console URL to create a composite index OR a\n * single-field index exemption.\n *\n * When Firestore throws FAILED_PRECONDITION (code 9) for missing indexes,\n * the error message sometimes includes a creation link (regular collections)\n * but often does NOT include it (collection groups). This utility builds the\n * link from the query context so the admin UI can always present it.\n *\n * Two URL shapes are produced depending on the query:\n *\n * - **Composite index** (≥ 2 indexed fields, or any COLLECTION-scope multi-field\n * query). Encoded as a JSON `create_composite=` parameter under\n * `/v1/r/project/{p}/firestore/indexes`.\n *\n * - **Single-field exemption** (exactly one indexed field on a COLLECTION_GROUP\n * query — Firestore disables single-field collection-group indexes by\n * default and requires an explicit exemption). Encoded as a base64\n * protobuf `create_exemption=` parameter under\n * `/project/{p}/firestore/databases/-default-/indexes/automatic`.\n */\n\nimport type {\n FilterState,\n QueryError,\n SortState,\n WhereOp,\n} from \"./components/types\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\ninterface IndexField {\n fieldPath: string;\n order?: \"ASCENDING\" | \"DESCENDING\";\n arrayConfig?: \"CONTAINS\";\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nconst RANGE_OPS = new Set<WhereOp>([\"<\", \"<=\", \">\", \">=\", \"!=\"]);\nconst ARRAY_OPS = new Set<WhereOp>([\"array-contains\", \"array-contains-any\"]);\n\nfunction toIndexOrder(dir?: \"asc\" | \"desc\"): \"ASCENDING\" | \"DESCENDING\" {\n return dir === \"desc\" ? \"DESCENDING\" : \"ASCENDING\";\n}\n\n/**\n * Extract the collection ID (last path segment) from a Firestore path.\n * e.g. \"posts/{postId}/comments\" → \"comments\"\n */\nexport function collectionIdFromPath(path: string): string {\n const segments = path.split(\"/\").filter(Boolean);\n return segments[segments.length - 1] ?? path;\n}\n\n// ── Main ─────────────────────────────────────────────────────────────────────\n\n/**\n * Build a Firebase Console URL that pre-fills the composite-index creation form.\n *\n * @param projectId - GCP project ID (e.g. \"firestore-repo-services\")\n * @param collectionId - Firestore collection ID (e.g. \"posts\", \"comments\")\n * @param isGroup - Whether this is a collection group query\n * @param filters - Active filter states from the admin UI\n * @param sort - Active sort state (optional)\n * @returns - Full HTTPS URL to the Firebase Console index wizard\n */\nexport function buildIndexUrl(\n projectId: string,\n collectionId: string,\n isGroup: boolean,\n filters: FilterState[],\n sort?: SortState,\n): string {\n const fields: IndexField[] = [];\n const seen = new Set<string>();\n\n // 1. Equality filters first (order doesn't matter for equality)\n for (const f of filters) {\n if (f.op === \"==\" || f.op === \"in\" || f.op === \"not-in\") {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n fields.push({ fieldPath: f.field, order: \"ASCENDING\" });\n }\n }\n\n // 2. Array operators\n for (const f of filters) {\n if (ARRAY_OPS.has(f.op)) {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n fields.push({ fieldPath: f.field, arrayConfig: \"CONTAINS\" });\n }\n }\n\n // 3. Range / inequality filters\n for (const f of filters) {\n if (RANGE_OPS.has(f.op)) {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n // Use the sort direction if the range field matches the orderBy field\n const dir =\n sort?.field === f.field ? toIndexOrder(sort.dir) : \"ASCENDING\";\n fields.push({ fieldPath: f.field, order: dir });\n }\n }\n\n // 4. OrderBy fields not already covered by filters\n if (sort && !seen.has(sort.field)) {\n fields.push({ fieldPath: sort.field, order: toIndexOrder(sort.dir) });\n }\n\n // ── Single-field collection-group exemption ──────────────────────────────\n // Firestore disables single-field collection-group indexes by default; you\n // must create an explicit \"field exemption\". The Firebase Console wizard\n // for that uses a totally different URL (`create_exemption=`) with its own\n // protobuf shape — see buildExemptionUrl below.\n if (fields.length === 1 && isGroup) {\n return buildExemptionUrl(projectId, collectionId, fields[0]!);\n }\n\n // ── Composite index ──────────────────────────────────────────────────────\n // Firebase Console encodes composite indexes as a base64 protobuf payload\n // under `create_composite=`. Every composite index implicitly ends with\n // `__name__` as a tie-breaker, so we always append it (matches what the\n // Console itself produces).\n const lastDir =\n sort && fields.some((f) => f.fieldPath === sort.field)\n ? toIndexOrder(sort.dir)\n : \"ASCENDING\";\n fields.push({ fieldPath: \"__name__\", order: lastDir });\n\n return buildCompositeUrl(projectId, collectionId, isGroup, fields);\n}\n\n/**\n * Build a Firebase Console URL that pre-fills the composite index creation\n * form for either a regular collection or a collection-group query.\n *\n * The URL uses a base64-encoded protobuf payload (the same shape the Console\n * itself produces when you click \"Add index\" in the UI). Schema:\n *\n * message CompositeIndex {\n * string resource_path = 1; // projects/.../collectionGroups/{cg}/indexes/_\n * int32 query_scope = 2; // 1 = COLLECTION, 2 = COLLECTION_GROUP\n * repeated IndexField fields = 3;\n * }\n */\nexport function buildCompositeUrl(\n projectId: string,\n collectionId: string,\n isGroup: boolean,\n fields: IndexField[],\n databaseId: string = \"(default)\",\n): string {\n const resource = `projects/${projectId}/databases/${databaseId}/collectionGroups/${collectionId}/indexes/_`;\n\n const payload: number[] = [\n ...pbString(1, resource),\n ...pbInt(2, isGroup ? 2 : 1),\n ];\n for (const f of fields) {\n payload.push(...pbMessage(3, encodeIndexField(f)));\n }\n\n const urlDbId = databaseId === \"(default)\" ? \"-default-\" : databaseId;\n const encoded = encodeURIComponent(toBase64(payload));\n return `https://console.firebase.google.com/project/${projectId}/firestore/databases/${urlDbId}/indexes?create_composite=${encoded}`;\n}\n\n/**\n * Try to extract an index-creation URL from a Firestore error message.\n * Returns `undefined` if no URL is found.\n */\nexport function extractIndexUrl(message: string): string | undefined {\n const match = message.match(\n /https:\\/\\/console\\.firebase\\.google\\.com[^\\s)\"]*/,\n );\n return match?.[0];\n}\n\n// ── Single-field exemption URL (collection-group) ────────────────────────────\n\n/**\n * Minimal protobuf encoder for the field-exemption payload used by the\n * Firebase Console URL `?create_exemption=…`.\n *\n * Wire format (subset):\n * tag = (field_number << 3) | wire_type\n * wire types: 0 = varint, 2 = length-delimited\n *\n * Schema we encode (reverse-engineered from real Firebase Console URLs):\n * message FieldExemption {\n * string resource_path = 1; // projects/.../fields/{field}\n * int32 query_scope = 2; // 1 = COLLECTION, 2 = COLLECTION_GROUP\n * IndexConfig index = 3;\n * }\n * message IndexConfig {\n * string field_path = 1;\n * int32 order = 2; // 1 = ASCENDING, 2 = DESCENDING\n * int32 array_config = 3; // 1 = CONTAINS (mutually exclusive with order)\n * }\n */\nfunction pbVarint(n: number): number[] {\n const out: number[] = [];\n let v = n >>> 0;\n while (v >= 0x80) {\n out.push((v & 0x7f) | 0x80);\n v >>>= 7;\n }\n out.push(v & 0x7f);\n return out;\n}\n\nfunction pbTag(fieldNumber: number, wireType: 0 | 2): number {\n return (fieldNumber << 3) | wireType;\n}\n\nfunction pbString(fieldNumber: number, value: string): number[] {\n const bytes = Array.from(new TextEncoder().encode(value));\n return [pbTag(fieldNumber, 2), ...pbVarint(bytes.length), ...bytes];\n}\n\nfunction pbInt(fieldNumber: number, value: number): number[] {\n return [pbTag(fieldNumber, 0), ...pbVarint(value)];\n}\n\nfunction pbMessage(fieldNumber: number, payload: number[]): number[] {\n return [pbTag(fieldNumber, 2), ...pbVarint(payload.length), ...payload];\n}\n\n/** Encode an IndexField submessage: { field_path:1, order:2 OR array_config:3 } */\nfunction encodeIndexField(f: IndexField): number[] {\n const out: number[] = [...pbString(1, f.fieldPath)];\n if (f.arrayConfig === \"CONTAINS\") {\n out.push(...pbInt(3, 1));\n } else {\n out.push(...pbInt(2, f.order === \"DESCENDING\" ? 2 : 1));\n }\n return out;\n}\n\nfunction toBase64(bytes: number[]): string {\n // Standard base64 (no padding) — matches what Firebase Console produces.\n // Use Buffer when available (Node), otherwise fall back to btoa.\n const bin = String.fromCharCode(...bytes);\n let b64: string;\n if (typeof Buffer !== \"undefined\") {\n b64 = Buffer.from(bytes).toString(\"base64\");\n } else if (typeof btoa !== \"undefined\") {\n b64 = btoa(bin);\n } else {\n throw new Error(\"No base64 encoder available\");\n }\n return b64.replace(/=+$/, \"\");\n}\n\n/**\n * Build a Firebase Console URL that pre-fills the single-field index\n * exemption form for a collection-group query.\n */\nexport function buildExemptionUrl(\n projectId: string,\n collectionId: string,\n field: IndexField,\n databaseId: string = \"(default)\",\n): string {\n const resource = `projects/${projectId}/databases/${databaseId}/collectionGroups/${collectionId}/fields/${field.fieldPath}`;\n\n const payload: number[] = [\n ...pbString(1, resource),\n ...pbInt(2, 2), // COLLECTION_GROUP\n ...pbMessage(3, encodeIndexField(field)),\n ];\n\n // Database ID for the URL path: \"(default)\" → \"-default-\",\n // any other ID is used as-is (Firebase Console uses the bare ID).\n const urlDbId = databaseId === \"(default)\" ? \"-default-\" : databaseId;\n\n // create_exemption is base64 (URL-safe characters only — `+` and `/` are\n // valid in query string values per RFC, and Firebase accepts them, but we\n // URL-encode just to be safe).\n const encoded = encodeURIComponent(toBase64(payload));\n return `https://console.firebase.google.com/project/${projectId}/firestore/databases/${urlDbId}/indexes/automatic?create_exemption=${encoded}`;\n}\n\n// ── Project ID extraction ────────────────────────────────────────────────────\n\n/**\n * Robustly extract the GCP project ID from a Firestore reference.\n * Falls back through several known locations across firebase-admin versions\n * and finally to standard environment variables.\n */\nexport function extractProjectId(ref: unknown): string | undefined {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const r = ref as any;\n const candidates: unknown[] = [\n r?.firestore?.projectId,\n r?.firestore?.app?.options?.projectId,\n r?.firestore?._settings?.projectId,\n r?.firestore?.databaseId?.projectId,\n r?._firestore?.projectId,\n ];\n for (const c of candidates) {\n if (typeof c === \"string\" && c.length > 0) return c;\n }\n const env =\n process.env[\"GCLOUD_PROJECT\"] ||\n process.env[\"GOOGLE_CLOUD_PROJECT\"] ||\n process.env[\"FIREBASE_PROJECT_ID\"];\n return env || undefined;\n}\n\n// ── Centralized error → QueryError conversion ────────────────────────────────\n\n/**\n * Context required to build a fallback index URL when Firestore does not\n * include one in its error (typical for collection-group queries).\n */\nexport interface QueryErrorContext {\n /** Repository ref (CollectionReference / Query) used to extract project id */\n ref: unknown;\n /** Firestore collection path of the repo (e.g. \"posts/{postId}/comments\") */\n path: string;\n /** Whether the repo is a collection-group */\n isGroup: boolean;\n /** Active where filters at the time of the failed query */\n filters: FilterState[];\n /** Active orderBy state at the time of the failed query (if any) */\n sort?: SortState;\n}\n\n/**\n * Detect whether an unknown error thrown by Firestore is a missing-index\n * (`FAILED_PRECONDITION` / code 9) error.\n */\nexport function isMissingIndexError(err: unknown): boolean {\n const fe = err as { code?: number; message?: string } | null | undefined;\n if (!fe) return false;\n if (fe.code === 9) return true;\n return typeof fe.message === \"string\"\n ? fe.message.includes(\"requires an index\")\n : false;\n}\n\n/**\n * Convert a Firestore error into a typed `QueryError` with a guaranteed\n * `indexUrl` for missing-index cases (extracted from the message when\n * present, otherwise rebuilt from the query context — necessary for\n * collection-group queries where the SDK omits the link).\n *\n * Returns `null` when `err` is falsy.\n */\nexport function toQueryError(\n err: unknown,\n ctx: QueryErrorContext,\n): QueryError {\n const fe = (err ?? {}) as { code?: number; message?: string };\n const isIndex = isMissingIndexError(err);\n\n let indexUrl: string | undefined;\n if (isIndex) {\n indexUrl = fe.message ? extractIndexUrl(fe.message) : undefined;\n if (!indexUrl) {\n const projectId = extractProjectId(ctx.ref);\n if (projectId) {\n const colId = collectionIdFromPath(ctx.path);\n indexUrl = buildIndexUrl(\n projectId,\n colId,\n ctx.isGroup,\n ctx.filters,\n ctx.sort,\n );\n }\n }\n }\n\n return {\n type: isIndex ? \"index\" : \"error\",\n message: isIndex\n ? \"This query requires a composite index that does not exist yet.\"\n : (fe.message ?? \"Query failed\"),\n indexUrl,\n };\n}\n","/**\n * HTTP route handlers for the CRUD API server.\n *\n * Routes:\n * GET /:repoName → list documents (paginated)\n * GET /:repoName/:id → get single document\n * POST /:repoName → create document\n * PUT /:repoName/:id → update document (full)\n * PATCH /:repoName/:id → update document (partial)\n * DELETE /:repoName/:id → delete document\n */\n\nimport { z } from \"zod\";\nimport {\n coerceToDate,\n getDateHandling,\n maybeNormalize,\n} from \"../../shared/date-config\";\nimport {\n isMissingIndexError,\n toQueryError,\n type QueryErrorContext,\n} from \"../admin/index-url\";\nimport type {\n ApiResponse,\n CrudRepoEntry,\n CrudRepoRegistry,\n ListResponseData,\n QueryRequestBody,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\nfunction sendJson<T>(res: any, data: ApiResponse<T>, status = 200): void {\n const payload = maybeNormalize(data);\n res\n .status(status)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(payload));\n}\n\nfunction sendSuccess<T>(\n res: any,\n data: T,\n meta?: ApiResponse[\"meta\"],\n status = 200,\n): void {\n sendJson(res, { success: true, data, meta }, status);\n}\n\nfunction sendError(res: any, error: string, status = 400): void {\n sendJson(res, { success: false, error }, status);\n}\n\n/**\n * Send a structured error response. When the underlying Firestore error is a\n * missing-index (`FAILED_PRECONDITION` / code 9), include `errorType: \"index\"`\n * and an `indexUrl` pointing to the Firebase Console index-creation wizard —\n * crucial for collection-group queries where the SDK omits the link.\n */\nfunction sendQueryError(\n res: any,\n err: unknown,\n ctx: QueryErrorContext,\n fallbackMessage: string,\n verbose: boolean,\n): void {\n const qe = toQueryError(err, ctx);\n const isIndex = qe.type === \"index\";\n const status = isIndex ? 424 : 500;\n const message = isIndex\n ? qe.message\n : verbose && err instanceof Error\n ? err.message\n : fallbackMessage;\n const payload: ApiResponse = { success: false, error: message };\n if (isIndex) {\n payload.errorType = \"index\";\n if (qe.indexUrl) payload.indexUrl = qe.indexUrl;\n }\n sendJson(res, payload, status);\n}\n\n// ---------------------------------------------------------------------------\n// ID generation\n// ---------------------------------------------------------------------------\n\nconst _idChars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n/** Generate a random 20-char alphanumeric ID matching Firestore's native format. */\nfunction generateFirestoreId(): string {\n let id = \"\";\n for (let i = 0; i < 20; i++) {\n id += _idChars.charAt(Math.floor(Math.random() * _idChars.length));\n }\n return id;\n}\n\n// ---------------------------------------------------------------------------\n// Zod schema helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively wrap z.date() with z.preprocess(coerceToDate) so that ISO strings,\n * Firestore Timestamps and {_seconds,_nanoseconds} payloads are accepted as input.\n * Only invoked when global date handling mode is \"normalize\".\n */\nfunction wrapDateSchemas(schema: z.ZodType): z.ZodType {\n const def = (schema as any)._def ?? (schema as any).def;\n if (!def) return schema;\n const typeName = def.typeName ?? def.type;\n\n if (typeName === \"ZodDate\" || typeName === \"date\") {\n return z.preprocess((v) => coerceToDate(v) ?? v, schema as z.ZodDate);\n }\n if (typeName === \"ZodObject\" || typeName === \"object\") {\n const shape = (schema as z.ZodObject<any>).shape;\n const wrapped: Record<string, z.ZodType> = {};\n for (const [k, v] of Object.entries(shape)) {\n wrapped[k] = wrapDateSchemas(v as z.ZodType);\n }\n return z.object(wrapped);\n }\n if (typeName === \"ZodArray\" || typeName === \"array\") {\n const inner = def.element ?? def.type;\n if (inner) return z.array(wrapDateSchemas(inner));\n }\n if (typeName === \"ZodOptional\" || typeName === \"optional\") {\n const inner = def.innerType;\n if (inner) return wrapDateSchemas(inner).optional();\n }\n if (typeName === \"ZodNullable\" || typeName === \"nullable\") {\n const inner = def.innerType;\n if (inner) return wrapDateSchemas(inner).nullable();\n }\n if (typeName === \"ZodDefault\" || typeName === \"default\") {\n const inner = def.innerType;\n const dflt = def.defaultValue;\n if (inner) {\n const wrapped = wrapDateSchemas(inner);\n return typeof dflt === \"function\"\n ? wrapped.default(dflt())\n : wrapped.default(dflt);\n }\n }\n return schema;\n}\n\n/**\n * Pick only specified fields from a Zod schema, always excluding system-managed keys.\n *\n * - `fields` undefined or empty → all schema fields minus systemKeys\n * - `fields` with values → only those fields, minus systemKeys\n */\nfunction pickSchemaFields(\n schema: z.ZodObject<any>,\n fields: string[] | undefined,\n systemKeys: string[] = [],\n): z.ZodObject<any> {\n const shape = schema.shape;\n const picked: Record<string, z.ZodType> = {};\n\n const source = fields && fields.length > 0 ? fields : Object.keys(shape);\n\n for (const field of source) {\n if (systemKeys.includes(field)) continue;\n const topLevel = field.split(\".\")[0];\n if (topLevel && shape[topLevel]) {\n picked[topLevel] = shape[topLevel]!;\n }\n }\n\n return z.object(picked);\n}\n\n/**\n * Validate data against schema and return parsed result or error.\n */\nfunction validateData(\n schema: z.ZodObject<any>,\n data: unknown,\n fields: string[] | undefined,\n partial = false,\n systemKeys: string[] = [],\n):\n | { success: true; data: Record<string, unknown> }\n | { success: false; error: string } {\n try {\n const targetSchema = pickSchemaFields(schema, fields, systemKeys);\n const partialSchema = partial ? targetSchema.partial() : targetSchema;\n const finalSchema =\n getDateHandling() === \"normalize\"\n ? (wrapDateSchemas(partialSchema) as z.ZodObject<any>)\n : partialSchema;\n const parsed = finalSchema.parse(data);\n return { success: true, data: parsed as Record<string, unknown> };\n } catch (err) {\n if (err instanceof z.ZodError) {\n const messages = err.issues.map(\n (e) => `${e.path.join(\".\")}: ${e.message}`,\n );\n return {\n success: false,\n error: `Validation failed: ${messages.join(\", \")}`,\n };\n }\n return { success: false, error: \"Validation failed\" };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Filter parsing\n// ---------------------------------------------------------------------------\n\ntype WhereOp =\n | \"==\"\n | \"!=\"\n | \"<\"\n | \"<=\"\n | \">\"\n | \">=\"\n | \"in\"\n | \"not-in\"\n | \"array-contains\"\n | \"array-contains-any\";\n\ninterface ParsedFilter {\n field: string;\n op: WhereOp;\n value: unknown;\n}\n\n/**\n * Parse query params into filter conditions.\n * Supports:\n * - field=value → field == value\n * - field__op=value → field op value (e.g., status__ne=draft → status != draft)\n * - field__in=a,b,c → field in [a, b, c]\n */\nfunction parseFilters(\n query: Record<string, string | string[] | undefined>,\n filterableFields: string[] | undefined,\n): ParsedFilter[] {\n const filters: ParsedFilter[] = [];\n const allowedFields = filterableFields ? new Set(filterableFields) : null;\n\n const opMap: Record<string, WhereOp> = {\n eq: \"==\",\n ne: \"!=\",\n lt: \"<\",\n lte: \"<=\",\n gt: \">\",\n gte: \">=\",\n in: \"in\",\n nin: \"not-in\",\n contains: \"array-contains\",\n containsAny: \"array-contains-any\",\n };\n\n for (const [key, rawVal] of Object.entries(query)) {\n if (rawVal === undefined) continue;\n\n // Skip pagination/meta params\n if (\n [\"cursor\", \"limit\", \"pageSize\", \"orderBy\", \"orderDir\", \"select\"].includes(\n key,\n )\n )\n continue;\n\n const val = Array.isArray(rawVal) ? rawVal[0] : rawVal;\n if (val === undefined || val === \"\") continue;\n\n // Parse field__op format\n const match = key.match(/^(\\w+)__(\\w+)$/);\n let field: string;\n let op: WhereOp = \"==\";\n\n if (match && match[1] && match[2]) {\n field = match[1];\n const opKey = match[2];\n if (opMap[opKey]) {\n op = opMap[opKey];\n } else {\n continue; // Unknown operator, skip\n }\n } else if (!match) {\n field = key;\n } else {\n continue; // Invalid match\n }\n\n // Check if field is filterable\n if (allowedFields && !allowedFields.has(field)) continue;\n\n // Parse value\n let parsedVal: unknown = val;\n\n // Handle \"in\" and \"not-in\" operators (comma-separated)\n if (op === \"in\" || op === \"not-in\" || op === \"array-contains-any\") {\n parsedVal = val.split(\",\").map((v) => parseValue(v.trim()));\n } else {\n parsedVal = parseValue(val);\n }\n\n filters.push({ field, op, value: parsedVal });\n }\n\n return filters;\n}\n\n/**\n * Parse a string value into appropriate type.\n */\nfunction parseValue(val: string): unknown {\n // Boolean\n if (val === \"true\") return true;\n if (val === \"false\") return false;\n if (val === \"null\") return null;\n\n // Number\n const num = Number(val);\n if (!isNaN(num) && val !== \"\") return num;\n\n // String\n return val;\n}\n\n// ---------------------------------------------------------------------------\n// Cursor serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a Firestore DocumentSnapshot to a JSON-safe cursor object.\n */\nfunction serializeCursor(\n snapshot: import(\"firebase-admin/firestore\").DocumentSnapshot | undefined,\n): Record<string, unknown> | null {\n if (!snapshot) return null;\n return { docId: snapshot.id };\n}\n\n/**\n * Deserialize a cursor object back to a DocumentSnapshot.\n * Fetches the document from Firestore to get the actual snapshot.\n */\nasync function deserializeCursor(\n entry: CrudRepoEntry,\n cursor: unknown,\n): Promise<import(\"firebase-admin/firestore\").DocumentSnapshot | undefined> {\n if (!cursor || typeof cursor !== \"object\") return undefined;\n const docId = (cursor as Record<string, unknown>).docId;\n if (typeof docId !== \"string\") return undefined;\n\n try {\n // Get the collection reference from the repo\n const colRef = entry.repo\n .ref as import(\"firebase-admin/firestore\").CollectionReference;\n if (typeof colRef.doc !== \"function\") return undefined;\n const snapshot = await colRef.doc(docId).get();\n return snapshot.exists ? snapshot : undefined;\n } catch {\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Handlers factory\n// ---------------------------------------------------------------------------\n\nexport function createCrudHandlers(\n registry: CrudRepoRegistry,\n basePath: string,\n verbose: boolean,\n) {\n // ── Helper to get repo entry ────────────────────────────────────────────\n function getRepoEntry(\n repoName: string | undefined,\n res: any,\n ): CrudRepoEntry | null {\n if (!repoName || !registry[repoName]) {\n sendError(res, `Repository \"${repoName}\" not found`, 404);\n return null;\n }\n return registry[repoName];\n }\n\n /**\n * Extract Firestore document path args from a document's stored path.\n * e.g. \"posts/abc/comments/xyz\" → [\"abc\", \"xyz\"] (the doc-ID segments).\n */\n function extractPathArgs(\n doc: Record<string, unknown>,\n pathKey?: string,\n ): string[] | undefined {\n if (!pathKey) return undefined;\n const fullPath = doc[pathKey];\n if (typeof fullPath !== \"string\" || !fullPath) return undefined;\n const segments = fullPath.split(\"/\").filter(Boolean);\n const args: string[] = [];\n for (let i = 1; i < segments.length; i += 2) {\n args.push(segments[i]!);\n }\n return args.length > 0 ? args : undefined;\n }\n\n /**\n * Fetch a single document by its documentKey, with fallback to query\n * for collection-group repos where direct documentRef may fail.\n */\n async function fetchDocById(\n entry: CrudRepoEntry,\n docId: string,\n ): Promise<Record<string, unknown> | null> {\n const getterName = `by${entry.documentKey.charAt(0).toUpperCase()}${entry.documentKey.slice(1)}`;\n const getter = (entry.repo.get as any)[getterName];\n\n if (typeof getter === \"function\") {\n try {\n const doc = (await getter(docId)) as Record<string, unknown> | null;\n if (doc) return doc;\n } catch {\n // Direct ref may fail for subcollections — fall through to query\n }\n }\n\n const results = await entry.repo.query.by({\n where: [[entry.documentKey, \"==\", docId]],\n limit: 1,\n });\n return (results[0] as Record<string, unknown>) ?? null;\n }\n\n // ── LIST: GET /:repoName ────────────────────────────────────────────────\n async function handleList(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n // Captured for error handling (so the catch can build an index URL)\n let ctxFilters: { field: string; op: any; value: string }[] = [];\n let ctxSort: { field: string; dir: \"asc\" | \"desc\" } | undefined;\n\n try {\n const query = req.query ?? {};\n const pageSize = Math.min(\n Number(query.pageSize) || entry.pageSize,\n 100, // Max page size\n );\n const cursor = query.cursor as string | undefined;\n const direction =\n (query.direction as string)?.toLowerCase() === \"prev\" ? \"prev\" : \"next\";\n const orderBy = query.orderBy as string | undefined;\n const orderDir =\n (query.orderDir as string)?.toLowerCase() === \"desc\" ? \"desc\" : \"asc\";\n const selectStr = query.select as string | undefined;\n const select = selectStr\n ? selectStr.split(\",\").map((s) => s.trim())\n : undefined;\n\n // Parse includes (relation population)\n let includes:\n | (string | { relation: string; select?: string[] })[]\n | undefined;\n if (entry.allowedIncludes && query.includes) {\n const rawIncludes =\n typeof query.includes === \"string\"\n ? query.includes.split(\",\").map((s: string) => s.trim())\n : Array.isArray(query.includes)\n ? query.includes\n : [];\n includes = rawIncludes.filter(\n (inc: string) =>\n typeof inc === \"string\" && entry.allowedIncludes!.includes(inc),\n );\n if (includes?.length === 0) includes = undefined;\n }\n\n // Parse filters\n const filters = parseFilters(query, entry.filterableFields);\n ctxFilters = filters.map((f) => ({\n field: f.field,\n op: f.op,\n value: String(f.value ?? \"\"),\n }));\n if (orderBy) ctxSort = { field: orderBy, dir: orderDir };\n\n // Build query options\n const queryOptions: any = {\n pageSize,\n direction,\n };\n\n if (cursor) {\n try {\n const cursorObj =\n typeof cursor === \"string\" ? JSON.parse(cursor) : cursor;\n queryOptions.cursor = await deserializeCursor(entry, cursorObj);\n } catch {\n // Invalid cursor, ignore\n }\n }\n\n if (orderBy) {\n queryOptions.orderBy = [{ field: orderBy, direction: orderDir }];\n }\n\n if (filters.length > 0) {\n queryOptions.where = filters.map((f) => [f.field, f.op, f.value]);\n }\n\n if (select) {\n queryOptions.select = select as any;\n }\n\n if (includes) {\n queryOptions.include = includes as any;\n }\n\n // Execute query\n const result = await entry.repo.query.paginate(queryOptions);\n\n const responseData: ListResponseData = {\n items: result.data,\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n nextCursor: serializeCursor(result.nextCursor),\n prevCursor: serializeCursor(result.prevCursor),\n };\n\n sendSuccess(res, responseData, {\n pageSize,\n hasMore: result.hasNextPage,\n });\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: ctxFilters,\n sort: ctxSort,\n },\n \"Failed to fetch documents\",\n verbose,\n );\n }\n }\n\n // ── QUERY: POST /:repoName/query ────────────────────────────────────────\n // Advanced query endpoint supporting OR conditions, array filters, etc.\n async function handleQuery(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n // Captured for error handling (so the catch can build an index URL)\n let ctxFilters: { field: string; op: any; value: string }[] = [];\n let ctxSort: { field: string; dir: \"asc\" | \"desc\" } | undefined;\n\n try {\n const body: QueryRequestBody = req.body ?? {};\n const pageSize = Math.min(body.pageSize || entry.pageSize, 100);\n const direction = body.direction === \"prev\" ? \"prev\" : \"next\";\n\n // Capture context for index URL fallback\n if (body.where) {\n ctxFilters = body.where.map((w) => ({\n field: String(w[0]),\n op: w[1] as any,\n value: String(w[2] ?? \"\"),\n }));\n }\n if (body.orderBy && body.orderBy[0]) {\n ctxSort = {\n field: body.orderBy[0].field,\n dir: body.orderBy[0].direction === \"desc\" ? \"desc\" : \"asc\",\n };\n }\n\n // Build query options\n const queryOptions: any = {\n pageSize,\n direction,\n };\n\n // Cursor\n if (body.cursor) {\n try {\n const cursorObj =\n typeof body.cursor === \"string\"\n ? JSON.parse(body.cursor)\n : body.cursor;\n queryOptions.cursor = await deserializeCursor(entry, cursorObj);\n } catch {\n // Invalid cursor, ignore\n }\n }\n\n // Includes (relation population)\n if (entry.allowedIncludes && body.includes && body.includes.length > 0) {\n const validIncludes = body.includes.filter((inc) => {\n if (typeof inc === \"string\") {\n return entry.allowedIncludes!.includes(inc);\n }\n if (\n typeof inc === \"object\" &&\n inc !== null &&\n \"relation\" in inc &&\n typeof inc.relation === \"string\"\n ) {\n return entry.allowedIncludes!.includes(inc.relation);\n }\n return false;\n });\n if (validIncludes.length > 0) {\n queryOptions.include = validIncludes as any;\n }\n }\n\n // Where conditions (AND)\n if (body.where && body.where.length > 0) {\n // Validate filterable fields if configured\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n const invalid = body.where.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n queryOptions.where = body.where;\n }\n\n // OR where conditions (simple)\n if (body.orWhere && body.orWhere.length > 0) {\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n const invalid = body.orWhere.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n queryOptions.orWhere = body.orWhere;\n }\n\n // OR where groups (advanced)\n if (body.orWhereGroups && body.orWhereGroups.length > 0) {\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n for (const group of body.orWhereGroups) {\n const invalid = group.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n }\n queryOptions.orWhereGroups = body.orWhereGroups;\n }\n\n // Order by\n if (body.orderBy && body.orderBy.length > 0) {\n queryOptions.orderBy = body.orderBy;\n }\n\n // Select\n if (body.select && body.select.length > 0) {\n queryOptions.select = body.select;\n }\n\n // Execute query\n const result = await entry.repo.query.paginate(queryOptions);\n\n const responseData: ListResponseData = {\n items: result.data,\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n nextCursor: serializeCursor(result.nextCursor),\n prevCursor: serializeCursor(result.prevCursor),\n };\n\n sendSuccess(res, responseData, {\n pageSize,\n hasMore: result.hasNextPage,\n });\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: ctxFilters,\n sort: ctxSort,\n },\n \"Failed to query documents\",\n verbose,\n );\n }\n }\n\n // ── GET: GET /:repoName/:id ─────────────────────────────────────────────\n async function handleGet(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n const doc = await fetchDocById(entry, id);\n\n if (!doc) {\n sendError(res, \"Document not found\", 404);\n return;\n }\n\n sendSuccess(res, doc);\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: [{ field: entry.documentKey, op: \"==\", value: id }],\n },\n \"Failed to fetch document\",\n verbose,\n );\n }\n }\n\n // ── CREATE: POST /:repoName ─────────────────────────────────────────────\n async function handleCreate(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n try {\n const body = req.body ?? {};\n\n // Validate against schema\n const validation = validateData(\n entry.schema,\n body,\n entry.createFields,\n false,\n entry.systemKeys,\n );\n if (!validation.success) {\n sendError(res, validation.error, 400);\n return;\n }\n\n // Custom validation\n if (entry.validate) {\n const customError = await entry.validate(validation.data, \"create\");\n if (customError) {\n sendError(res, customError, 400);\n return;\n }\n }\n\n // Create document\n let created: any;\n if (entry.isGroup && entry.parentKeys && entry.parentKeys.length > 0) {\n // Collection-group repos cannot use create(); use set() with parent path args.\n const data: Record<string, any> = { ...validation.data };\n // set() doesn't auto-set createdKey, so inject it here\n if (entry.createdKey) {\n data[entry.createdKey] = new Date();\n }\n const missingKeys = entry.parentKeys.filter((k) => !data[k]);\n if (missingKeys.length > 0) {\n sendError(\n res,\n `Missing parent key(s) for subcollection create: ${missingKeys.join(\", \")}`,\n 400,\n );\n return;\n }\n const parentIds = entry.parentKeys.map((k) => data[k] as string);\n const docId =\n data[entry.documentKey] || generateFirestoreId();\n created = await entry.repo.set(...parentIds, docId, data);\n } else {\n created = await entry.repo.create(validation.data as any);\n }\n\n sendSuccess(res, created, undefined, 201);\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to create document\";\n sendError(res, message, 500);\n }\n }\n\n // ── UPDATE: PUT/PATCH /:repoName/:id ────────────────────────────────────\n async function handleUpdate(\n req: any,\n res: any,\n partial: boolean,\n ): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n const body = req.body ?? {};\n\n // Validate against schema\n const validation = validateData(\n entry.schema,\n body,\n entry.mutableFields,\n partial,\n entry.systemKeys,\n );\n if (!validation.success) {\n sendError(res, validation.error, 400);\n return;\n }\n\n // Custom validation\n if (entry.validate) {\n const customError = await entry.validate(validation.data, \"update\");\n if (customError) {\n sendError(res, customError, 400);\n return;\n }\n }\n\n // Update document — fetch first to get path args for subcollections\n const existingDoc = await fetchDocById(entry, id);\n const pathArgs =\n (existingDoc && extractPathArgs(existingDoc, entry.pathKey)) ?? [id];\n const updated = await entry.repo.update(\n ...pathArgs,\n validation.data as any,\n );\n\n sendSuccess(res, updated);\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to update document\";\n sendError(res, message, 500);\n }\n }\n\n // ── DELETE: DELETE /:repoName/:id ───────────────────────────────────────\n async function handleDelete(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n if (!entry.allowDelete) {\n sendError(res, \"Delete not allowed for this repository\", 403);\n return;\n }\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n // Fetch first to get path args for subcollections\n const doc = await fetchDocById(entry, id);\n const pathArgs =\n (doc && extractPathArgs(doc, entry.pathKey)) ?? [id];\n await entry.repo.delete(...pathArgs);\n sendSuccess(res, { deleted: true });\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to delete document\";\n sendError(res, message, 500);\n }\n }\n\n // ── OPTIONS: for CORS preflight ─────────────────────────────────────────\n function handleOptions(req: any, res: any): void {\n res\n .status(204)\n .set(\n \"Access-Control-Allow-Methods\",\n \"GET, POST, PUT, PATCH, DELETE, OPTIONS\",\n )\n .set(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n .set(\"Access-Control-Max-Age\", \"86400\")\n .send(\"\");\n }\n\n return {\n handleList,\n handleQuery,\n handleGet,\n handleCreate,\n handleUpdate,\n handleDelete,\n handleOptions,\n };\n}\n","/**\n * OpenAPI 3.1 specification generator for the CRUD server.\n *\n * Introspects each `CrudRepoEntry` and uses Zod 4's native `z.toJSONSchema()`\n * to produce a fully typed OpenAPI document ready for Scalar UI or codegen.\n *\n * @module servers/crud/openapi\n */\n\nimport { z } from \"zod\";\nimport type { CrudRepoEntry, CrudRepoRegistry } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Minimal subset of an OpenAPI 3.1 document we produce. */\nexport interface OpenAPIDocument {\n openapi: \"3.1.0\";\n info: OpenAPIInfo;\n servers?: { url: string; description?: string }[];\n paths: Record<string, Record<string, OpenAPIOperation>>;\n components: {\n schemas: Record<string, Record<string, unknown>>;\n securitySchemes?: Record<string, Record<string, unknown>>;\n };\n security?: Record<string, string[]>[];\n tags?: { name: string; description?: string }[];\n}\n\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\nexport interface OpenAPISpecOptions {\n /** Document title (default: \"CRUD API\") */\n title?: string;\n /** API version (default: \"1.0.0\") */\n version?: string;\n /** Description shown in Scalar UI / Swagger */\n description?: string;\n /** Server URLs */\n servers?: { url: string; description?: string }[];\n /** Whether the API requires auth — adds securitySchemes */\n auth?: \"basic\" | \"bearer\" | false;\n}\n\ninterface OpenAPIOperation {\n operationId: string;\n summary: string;\n tags: string[];\n parameters?: Record<string, unknown>[];\n requestBody?: Record<string, unknown>;\n responses: Record<string, Record<string, unknown>>;\n security?: Record<string, string[]>[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Zod schema to a JSON Schema object suitable for OpenAPI 3.1.\n *\n * Uses `unrepresentable: \"any\"` so that types Zod can't natively serialize\n * (e.g. `z.date()`, `z.bigint()`, custom classes — common with Firestore\n * Timestamps stored as `Date`) don't throw and collapse the whole schema to\n * a bare `{ type: \"object\" }`. We additionally override a few well-known\n * cases to a sensible OpenAPI representation.\n */\nfunction zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {\n try {\n return z.toJSONSchema(schema, {\n target: \"openapi-3.1\",\n unrepresentable: \"any\",\n override: (ctx) => {\n const def: any = (ctx.zodSchema as any)?._zod?.def;\n if (!def) return;\n if (def.type === \"date\") {\n (ctx.jsonSchema as any).type = \"string\";\n (ctx.jsonSchema as any).format = \"date-time\";\n } else if (def.type === \"bigint\") {\n (ctx.jsonSchema as any).type = \"string\";\n (ctx.jsonSchema as any).format = \"int64\";\n }\n },\n }) as Record<string, unknown>;\n } catch (err) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n \"[generateOpenAPISpec] Failed to convert Zod schema to JSON Schema; falling back to {type:object}.\",\n err,\n );\n }\n return { type: \"object\" };\n }\n}\n\n/** Wraps a JSON Schema in a `#/components/schemas/<name>` $ref. */\nfunction schemaRef(name: string): Record<string, unknown> {\n return { $ref: `#/components/schemas/${name}` };\n}\n\n/** Standard error response schema. */\nfunction errorResponse(description: string): Record<string, unknown> {\n return {\n description,\n content: {\n \"application/json\": {\n schema: schemaRef(\"ErrorResponse\"),\n },\n },\n };\n}\n\n/** Standard success response wrapping data. */\nfunction successResponse(\n description: string,\n dataSchema: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n description,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [true] },\n data: dataSchema,\n },\n required: [\"success\", \"data\"],\n },\n },\n },\n };\n}\n\n/** Build list response with pagination metadata. */\nfunction listResponse(\n itemSchema: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n description: \"Paginated list of documents\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [true] },\n data: {\n type: \"object\",\n properties: {\n items: { type: \"array\", items: itemSchema },\n nextCursor: {\n oneOf: [{ type: \"object\" }, { type: \"null\" }],\n },\n prevCursor: {\n oneOf: [{ type: \"object\" }, { type: \"null\" }],\n },\n hasNextPage: { type: \"boolean\" },\n hasPrevPage: { type: \"boolean\" },\n },\n required: [\"items\", \"hasNextPage\", \"hasPrevPage\"],\n },\n meta: {\n type: \"object\",\n properties: {\n pageSize: { type: \"integer\" },\n hasMore: { type: \"boolean\" },\n cursor: {\n oneOf: [{ type: \"string\" }, { type: \"null\" }],\n },\n },\n },\n },\n required: [\"success\", \"data\"],\n },\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pagination / filter query parameters (shared across list endpoints)\n// ---------------------------------------------------------------------------\n\nfunction paginationParams(entry: CrudRepoEntry): Record<string, unknown>[] {\n return [\n {\n name: \"pageSize\",\n in: \"query\",\n schema: { type: \"integer\", default: entry.pageSize, maximum: 100 },\n description: \"Number of items per page\",\n },\n {\n name: \"cursor\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Base64 pagination cursor\",\n },\n {\n name: \"orderBy\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Field name to order by\",\n },\n {\n name: \"orderDir\",\n in: \"query\",\n schema: { type: \"string\", enum: [\"asc\", \"desc\"] },\n description: \"Order direction\",\n },\n {\n name: \"select\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of fields to return\",\n },\n ];\n}\n\nfunction filterParams(entry: CrudRepoEntry): Record<string, unknown>[] {\n const fields = entry.filterableFields ?? Object.keys(entry.schema.shape);\n const ops = [\"eq\", \"ne\", \"lt\", \"lte\", \"gt\", \"gte\", \"in\", \"nin\", \"contains\"];\n\n const params: Record<string, unknown>[] = [];\n for (const field of fields) {\n // Direct equality filter: ?field=value\n params.push({\n name: field,\n in: \"query\",\n schema: { type: \"string\" },\n description: `Filter by ${field} (equality)`,\n });\n // Operator filters: ?field__op=value\n for (const op of ops) {\n params.push({\n name: `${field}__${op}`,\n in: \"query\",\n schema: { type: \"string\" },\n description: `Filter ${field} with operator ${op}`,\n });\n }\n }\n return params;\n}\n\n// ---------------------------------------------------------------------------\n// Query body schema (POST /query)\n// ---------------------------------------------------------------------------\n\nfunction queryBodySchema(): Record<string, unknown> {\n return {\n type: \"object\",\n properties: {\n where: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n description: \"AND conditions: [field, operator, value][]\",\n },\n orWhere: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n description: \"Simple OR conditions (each independently OR'd)\",\n },\n orWhereGroups: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n },\n description: \"Advanced OR groups (AND within, OR across groups)\",\n },\n orderBy: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n field: { type: \"string\" },\n direction: { type: \"string\", enum: [\"asc\", \"desc\"] },\n },\n required: [\"field\"],\n },\n },\n select: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Fields to select (projection)\",\n },\n pageSize: {\n type: \"integer\",\n maximum: 100,\n description: \"Number of items per page\",\n },\n cursor: {\n oneOf: [{ type: \"string\" }, { type: \"object\" }],\n description: \"Pagination cursor\",\n },\n direction: {\n type: \"string\",\n enum: [\"next\", \"prev\"],\n description: \"Pagination direction\",\n },\n includes: {\n type: \"array\",\n items: {\n oneOf: [\n { type: \"string\" },\n {\n type: \"object\",\n properties: {\n relation: { type: \"string\" },\n select: { type: \"array\", items: { type: \"string\" } },\n },\n required: [\"relation\"],\n },\n ],\n },\n description: \"Relations to include (populate)\",\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Path generation per repo entry\n// ---------------------------------------------------------------------------\n\nfunction buildPathsForEntry(\n entry: CrudRepoEntry,\n base: string,\n modelSchemaName: string,\n createSchemaName: string | null,\n updateSchemaName: string | null,\n): Record<string, Record<string, OpenAPIOperation>> {\n const paths: Record<string, Record<string, OpenAPIOperation>> = {};\n const tag = entry.name;\n const collectionPath = `${base}/${entry.name}`;\n const documentPath = `${collectionPath}/{${entry.documentKey}}`;\n\n const idParam = {\n name: entry.documentKey,\n in: \"path\",\n required: true,\n schema: { type: \"string\" },\n description: `Unique document identifier`,\n };\n\n // ── GET /:repo → list ──────────────────────────────────────────────\n paths[collectionPath] = {\n get: {\n operationId: `list${capitalize(entry.name)}`,\n summary: `List ${entry.name} (paginated)`,\n tags: [tag],\n parameters: [...paginationParams(entry), ...filterParams(entry)],\n responses: {\n \"200\": listResponse(schemaRef(modelSchemaName)),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n // ── POST /:repo → create ────────────────────────────────────────\n post: {\n operationId: `create${capitalize(entry.name)}`,\n summary: `Create a ${singularize(entry.name)}`,\n tags: [tag],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(createSchemaName ?? modelSchemaName),\n },\n },\n },\n responses: {\n \"201\": successResponse(\"Document created\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n };\n\n // ── POST /:repo/query → advanced query ────────────────────────────\n paths[`${collectionPath}/query`] = {\n post: {\n operationId: `query${capitalize(entry.name)}`,\n summary: `Query ${entry.name} with advanced filters`,\n tags: [tag],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(\"QueryRequestBody\"),\n },\n },\n },\n responses: {\n \"200\": listResponse(schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Invalid query\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n };\n\n // ── Single-document paths ────────────────────────────────────────\n const docOps: Record<string, OpenAPIOperation> = {};\n\n // GET /:repo/:id\n docOps.get = {\n operationId: `get${capitalize(singularize(entry.name))}`,\n summary: `Get a single ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n responses: {\n \"200\": successResponse(\"Document found\", schemaRef(modelSchemaName)),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // PUT /:repo/:id (full update)\n docOps.put = {\n operationId: `update${capitalize(singularize(entry.name))}`,\n summary: `Update a ${singularize(entry.name)} (full replace)`,\n tags: [tag],\n parameters: [idParam],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(updateSchemaName ?? modelSchemaName),\n },\n },\n },\n responses: {\n \"200\": successResponse(\"Document updated\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // PATCH /:repo/:id (partial update)\n docOps.patch = {\n operationId: `patch${capitalize(singularize(entry.name))}`,\n summary: `Partially update a ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: {\n allOf: [schemaRef(updateSchemaName ?? modelSchemaName)],\n description: \"All fields are optional for partial updates\",\n },\n },\n },\n },\n responses: {\n \"200\": successResponse(\"Document patched\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // DELETE /:repo/:id (only if allowDelete)\n if (entry.allowDelete) {\n docOps.delete = {\n operationId: `delete${capitalize(singularize(entry.name))}`,\n summary: `Delete a ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n responses: {\n \"200\": successResponse(\"Document deleted\", {\n type: \"object\",\n properties: { id: { type: \"string\" } },\n }),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n }\n\n paths[documentPath] = docOps;\n\n return paths;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a full OpenAPI 3.1 specification from a `CrudRepoRegistry`.\n *\n * Uses Zod 4's native `z.toJSONSchema()` to convert each repo's schema\n * into a JSON Schema component, then assembles paths for the standard\n * CRUD endpoints.\n *\n * @example\n * ```ts\n * import { generateOpenAPISpec } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * const spec = generateOpenAPISpec(registry, \"/api\", {\n * title: \"My API\",\n * version: \"1.0.0\",\n * servers: [{ url: \"https://my-api.example.com\" }],\n * auth: \"bearer\",\n * });\n *\n * // Write to file\n * fs.writeFileSync(\"openapi.json\", JSON.stringify(spec, null, 2));\n * ```\n */\nexport function generateOpenAPISpec(\n registry: CrudRepoRegistry,\n basePath: string,\n options: OpenAPISpecOptions = {},\n): OpenAPIDocument {\n const {\n title = \"CRUD API\",\n version = \"1.0.0\",\n description,\n servers,\n auth = false,\n } = options;\n\n const base = basePath === \"/\" ? \"\" : basePath.replace(/\\/$/, \"\");\n\n // ── Components: schemas ───────────────────────────────────────────\n const schemas: Record<string, Record<string, unknown>> = {};\n const allPaths: Record<string, Record<string, OpenAPIOperation>> = {};\n const tags: { name: string; description?: string }[] = [];\n\n // Shared schemas\n schemas[\"ErrorResponse\"] = {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [false] },\n error: { type: \"string\" },\n },\n required: [\"success\", \"error\"],\n };\n\n schemas[\"QueryRequestBody\"] = queryBodySchema();\n\n // Per-repo schemas & paths\n for (const [name, entry] of Object.entries(registry)) {\n const modelName = capitalize(singularize(name));\n const createName = `${modelName}Create`;\n const updateName = `${modelName}Update`;\n\n // Full model schema\n schemas[modelName] = zodToJsonSchema(entry.schema);\n\n // Helper: build a filtered shape (respects systemKeys + field list)\n const buildShape = (\n fieldList: string[] | undefined,\n ): Record<string, z.ZodType> => {\n const source =\n fieldList && fieldList.length > 0\n ? fieldList\n : Object.keys(entry.schema.shape);\n const shape: Record<string, z.ZodType> = {};\n for (const f of source) {\n const top = f.split(\".\")[0];\n if (top && entry.schema.shape[top] && !entry.systemKeys.includes(top)) {\n shape[top] = entry.schema.shape[top];\n }\n }\n return shape;\n };\n\n // Create schema\n let createSchemaName: string | null = null;\n const createShape = buildShape(entry.createFields);\n if (Object.keys(createShape).length > 0) {\n schemas[createName] = zodToJsonSchema(z.object(createShape));\n createSchemaName = createName;\n }\n\n // Update schema\n let updateSchemaName: string | null = null;\n const updateShape = buildShape(entry.mutableFields);\n if (Object.keys(updateShape).length > 0) {\n schemas[updateName] = zodToJsonSchema(z.object(updateShape));\n updateSchemaName = updateName;\n }\n\n // Build paths\n const entryPaths = buildPathsForEntry(\n entry,\n base,\n modelName,\n createSchemaName,\n updateSchemaName,\n );\n Object.assign(allPaths, entryPaths);\n\n // Tag\n tags.push({\n name,\n description: `Operations on ${name} (collection: ${entry.path})`,\n });\n }\n\n // ── Security ──────────────────────────────────────────────────────\n const securitySchemes: Record<string, Record<string, unknown>> = {};\n let security: Record<string, string[]>[] | undefined;\n\n if (auth === \"basic\") {\n securitySchemes[\"basicAuth\"] = {\n type: \"http\",\n scheme: \"basic\",\n };\n security = [{ basicAuth: [] }];\n } else if (auth === \"bearer\") {\n securitySchemes[\"bearerAuth\"] = {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n };\n security = [{ bearerAuth: [] }];\n }\n\n // ── Assemble ──────────────────────────────────────────────────────\n const doc: OpenAPIDocument = {\n openapi: \"3.1.0\",\n info: {\n title,\n version,\n ...(description ? { description } : {}),\n },\n ...(servers && servers.length > 0 ? { servers } : {}),\n paths: allPaths,\n components: {\n schemas,\n ...(Object.keys(securitySchemes).length > 0 ? { securitySchemes } : {}),\n },\n ...(security ? { security } : {}),\n tags,\n };\n\n return doc;\n}\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n/** Naive singularize: strip trailing 's' for display. */\nfunction singularize(s: string): string {\n if (s.endsWith(\"ies\")) return s.slice(0, -3) + \"y\";\n if (s.endsWith(\"ses\") || s.endsWith(\"xes\") || s.endsWith(\"zes\"))\n return s.slice(0, -2);\n if (s.endsWith(\"s\") && !s.endsWith(\"ss\")) return s.slice(0, -1);\n return s;\n}\n","/**\n * @module servers/crud\n *\n * Creates a REST API server for CRUD operations on Firestore repositories.\n *\n * Features:\n * - RESTful endpoints for List, Get, Create, Update, Delete\n * - Request validation using Zod schemas\n * - Cursor-based pagination\n * - Query filtering with operators (eq, ne, lt, gt, in, etc.)\n * - Field selection\n * - CORS support\n * - Configurable auth (Basic Auth or custom middleware)\n *\n * @example\n * ```ts\n * import * as functions from \"firebase-functions\";\n * import { z } from \"zod\";\n * import { createCrudServer } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * const postSchema = z.object({\n * title: z.string().min(1),\n * content: z.string(),\n * status: z.enum([\"draft\", \"published\"]),\n * authorId: z.string(),\n * });\n *\n * export const api = functions.https.onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * repos: {\n * posts: {\n * repo: repos.posts,\n * schema: postSchema,\n * path: \"posts\",\n * fieldsConfig: {\n * status: [\"filterable\"],\n * authorId: [\"filterable\"],\n * },\n * allowDelete: true,\n * },\n * },\n * })\n * );\n * ```\n *\n * ## API Endpoints\n *\n * | Method | Path | Description |\n * |--------|-------------------|--------------------------|\n * | GET | /:repo | List documents (paginated) |\n * | GET | /:repo/:id | Get single document |\n * | POST | /:repo | Create document |\n * | PUT | /:repo/:id | Update document (full) |\n * | PATCH | /:repo/:id | Update document (partial)|\n * | DELETE | /:repo/:id | Delete document |\n *\n * ## Query Parameters (GET list)\n *\n * | Param | Description |\n * |------------|------------------------------------------|\n * | pageSize | Number of items per page (max 100) |\n * | cursor | Base64 pagination cursor |\n * | orderBy | Field to order by |\n * | orderDir | Order direction (asc/desc) |\n * | select | Comma-separated fields to return |\n * | field | Filter by field (field=value) |\n * | field__op | Filter with operator (field__gt=10) |\n *\n * ## Filter Operators\n *\n * | Suffix | Firestore Op | Example |\n * |-------------|-------------------|-----------------------|\n * | (none) | == | status=active |\n * | __eq | == | status__eq=active |\n * | __ne | != | status__ne=draft |\n * | __lt | < | age__lt=18 |\n * | __lte | <= | age__lte=18 |\n * | __gt | > | age__gt=18 |\n * | __gte | >= | age__gte=18 |\n * | __in | in | status__in=a,b,c |\n * | __nin | not-in | status__nin=x,y |\n * | __contains | array-contains | tags__contains=news |\n */\n\nimport { MiniRouter } from \"../admin/router\";\nimport type { HttpRequest, HttpResponse } from \"../http-types\";\nimport type { ConfiguredRepository } from \"../../repositories/types\";\nimport { createCrudHandlers } from \"./handlers\";\nimport { generateOpenAPISpec, type OpenAPIDocument } from \"./openapi\";\nimport type {\n CrudRepoEntry,\n CrudRepoRegistry,\n CrudServerOptions,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Scalar API docs HTML template\n// ---------------------------------------------------------------------------\n\n/** Returns a self-contained HTML page using Scalar to render the spec. */\nfunction scalarDocsHtml(title: string, specUrl: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${title}</title>\n</head>\n<body>\n <script id=\"api-reference\" data-url=\"${specUrl}\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n\n/**\n * Compute the URL prefix for links / spec URLs.\n * In the Firebase emulator the /{project}/{region}/{functionTarget} prefix\n * is visible in URLs but stripped before the handler receives `req.url`.\n * In production Firebase proxy strips it automatically.\n */\nfunction getLinkBase(req: any, staticBasePath: string): string {\n const base = staticBasePath === \"/\" ? \"\" : staticBasePath.replace(/\\/$/, \"\");\n\n if (process.env[\"FUNCTIONS_EMULATOR\"] === \"true\") {\n const project =\n process.env[\"GCLOUD_PROJECT\"] ??\n process.env[\"GOOGLE_CLOUD_PROJECT\"] ??\n \"demo-project\";\n const region = process.env[\"FUNCTION_REGION\"] ?? \"us-central1\";\n const target = process.env[\"FUNCTION_TARGET\"] ?? \"\";\n return `/${project}/${region}/${target}${base}`;\n }\n\n // Cloud Functions v2: K_SERVICE = function name = URL path prefix.\n // Only add it when accessed via cloudfunctions.net (not custom domains).\n // Cloud Run (Gen 2) lowercases service names, but K_SERVICE may still\n // carry the original mixed-case export name — normalise to lowercase\n // so that generated links match the canonical URL.\n const service = process.env[\"K_SERVICE\"];\n const host: string = req?.hostname ?? req?.headers?.[\"host\"] ?? \"\";\n if (service && host.includes(\"cloudfunctions.net\")) {\n return `/${service.toLowerCase()}${base}`;\n }\n\n return base;\n}\n\n// ---------------------------------------------------------------------------\n// Body parser\n// ---------------------------------------------------------------------------\n\n/** Eagerly reads the raw request body as a string */\nasync function readRawBody(req: HttpRequest): Promise<string> {\n if (typeof (req as any).rawBody === \"string\")\n return (req as any).rawBody as string;\n if (Buffer.isBuffer((req as any).rawBody))\n return ((req as any).rawBody as Buffer).toString(\"utf8\");\n return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an Express-compatible request handler for a REST CRUD API.\n *\n * @template TRepos - Shape of the repos map (inferred automatically)\n * @param options - CRUD server configuration\n * @returns Express-compatible request handler for Firebase Functions\n *\n * @example\n * ```typescript\n * // Basic CRUD server\n * import { onRequest } from \"firebase-functions/https\";\n * import { createCrudServer } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * export const api = onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * repos: {\n * users: {\n * repo: repos.users,\n * path: \"users\",\n * fieldsConfig: {\n * name: [\"create\", \"mutable\"],\n * email: [\"create\", \"mutable\", \"filterable\"],\n * isActive: [\"filterable\"],\n * },\n * allowDelete: false,\n * },\n * posts: {\n * repo: repos.posts,\n * path: \"posts\",\n * fieldsConfig: {\n * status: [\"filterable\"],\n * userId: [\"filterable\"],\n * },\n * allowDelete: true,\n * },\n * },\n * })\n * );\n *\n * // With authentication\n * export const api = onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * auth: {\n * type: \"basic\",\n * username: \"api\",\n * password: process.env.API_PASSWORD!,\n * },\n * repos: { ... },\n * })\n * );\n *\n * // With custom auth middleware\n * export const api = onRequest(\n * createCrudServer({\n * auth: async (req, res, next) => {\n * const token = req.headers?.authorization?.replace(\"Bearer \", \"\");\n * if (!token || !(await verifyToken(token))) {\n * res.status(401).json({ success: false, error: \"Unauthorized\" });\n * return;\n * }\n * next();\n * },\n * repos: { ... },\n * })\n * );\n *\n * // With custom validation\n * export const api = onRequest(\n * createCrudServer({\n * repos: {\n * posts: {\n * repo: repos.posts,\n * path: \"posts\",\n * validate: async (data, operation) => {\n * if (operation === \"create\" && !data.title) {\n * return \"Title is required\";\n * }\n * return undefined;\n * },\n * },\n * },\n * })\n * );\n * ```\n */\nexport function createCrudServer<\n TRepos extends Record<string, ConfiguredRepository<any>>,\n>(\n options: CrudServerOptions<TRepos>,\n): ((req: any, res: any) => Promise<void>) & { spec: () => OpenAPIDocument; httpsOptions?: Record<string, unknown> } {\n const {\n basePath = \"/\",\n repos,\n parseBody = true,\n auth,\n middleware: extraMiddleware = [],\n verbose = false,\n httpsOptions,\n } = options;\n\n // Normalise basePath: no trailing slash\n const base = basePath === \"/\" ? \"\" : basePath.replace(/\\/$/, \"\");\n\n // Build the registry\n const registry: CrudRepoRegistry = {};\n for (const [name, cfg] of Object.entries(repos)) {\n // Schema resolution: explicit cfg.schema > embedded in repo (createRepositoryConfig(schema))\n const resolvedSchema = cfg.schema ?? (cfg.repo as any).schema ?? null;\n if (!resolvedSchema) {\n throw new Error(\n `[createCrudServer] Repository \"${name}\" has no Zod schema. ` +\n `Either use createRepositoryConfig(schema)(config) or pass schema: explicitly.`,\n );\n }\n\n // Resolve fieldsConfig → separate arrays for runtime\n let filterableFields: string[] | undefined;\n let mutableFields: string[] | undefined;\n let createFields: string[] | undefined;\n if (cfg.fieldsConfig) {\n const fc = cfg.fieldsConfig as Record<string, readonly string[]>;\n filterableFields = [];\n mutableFields = [];\n createFields = [];\n for (const [field, roles] of Object.entries(fc)) {\n for (const role of roles) {\n if (role === \"filterable\") filterableFields.push(field);\n else if (role === \"mutable\") mutableFields.push(field);\n else if (role === \"create\") createFields.push(field);\n }\n }\n if (filterableFields.length === 0) filterableFields = undefined;\n if (mutableFields.length === 0) mutableFields = undefined;\n if (createFields.length === 0) createFields = undefined;\n }\n\n // For collection-group repos, ensure parentKeys are included in createFields\n // so the validation accepts them in the request body.\n const parentKeys = (() => {\n const pk = (cfg.repo as any)._parentKeys as string[] | undefined;\n return pk && pk.length > 0 ? pk : undefined;\n })();\n if (parentKeys && createFields) {\n for (const pk of parentKeys) {\n if (!createFields.includes(pk)) createFields.push(pk);\n }\n }\n\n const entry: CrudRepoEntry = {\n name,\n path: cfg.path,\n repo: cfg.repo,\n schema: resolvedSchema,\n systemKeys: (cfg.repo as any)._systemKeys ?? [cfg.documentKey ?? \"docId\"],\n documentKey: cfg.documentKey ?? \"docId\",\n pathKey: (cfg.repo as any)._pathKey ?? undefined,\n isGroup: !!(cfg.repo as any)._isGroup,\n parentKeys,\n createdKey: (cfg.repo as any)._createdKey ?? undefined,\n pageSize: cfg.pageSize ?? 25,\n filterableFields,\n mutableFields,\n createFields,\n allowDelete: cfg.allowDelete ?? false,\n allowedIncludes: cfg.allowedIncludes as string[] | undefined,\n validate: cfg.validate as CrudRepoEntry[\"validate\"],\n };\n\n registry[name] = entry;\n }\n\n const handlers = createCrudHandlers(registry, base, verbose);\n\n // ── OpenAPI spec (cached) ─────────────────────────────────────────────\n const openapi = options.openapi;\n const openapiOpts = openapi && typeof openapi === \"object\" ? openapi : {};\n let _specCache: OpenAPIDocument | null = null;\n function getSpec(): OpenAPIDocument {\n if (!_specCache) {\n const authType =\n auth && typeof auth !== \"function\"\n ? (\"basic\" as const)\n : auth\n ? (\"bearer\" as const)\n : false;\n _specCache = generateOpenAPISpec(registry, base, {\n ...openapiOpts,\n auth: openapiOpts.auth ?? authType,\n });\n }\n return _specCache;\n }\n\n // ── Router ─────────────────────────────────────────────────────────────\n const router = new MiniRouter();\n\n // ── CORS middleware ─────────────────────────────────────────────────────\n router.use((req, res, next) => {\n res.set(\"Access-Control-Allow-Origin\", \"*\");\n res.set(\"Access-Control-Allow-Credentials\", \"true\");\n next();\n });\n\n // ── 1. Body-parsing middleware ──────────────────────────────────────────\n if (parseBody) {\n router.use(async (req, _res, next) => {\n const r = req as unknown as HttpRequest;\n const contentType = String(r.headers?.[\"content-type\"] ?? \"\");\n if (contentType.includes(\"application/json\")) {\n if (typeof r.body === \"string\") {\n try {\n (req as any).body = JSON.parse(r.body);\n } catch {\n /* keep as string */\n }\n } else if (Buffer.isBuffer((req as any).rawBody)) {\n try {\n const raw = await readRawBody(r);\n (req as any).body = JSON.parse(raw);\n } catch {\n /* keep as is */\n }\n }\n }\n await next();\n });\n }\n\n // ── 2. Auth middleware ──────────────────────────────────────────────────\n if (auth) {\n if (typeof auth === \"function\") {\n // Custom middleware\n router.use(auth);\n } else {\n // HTTP Basic Auth\n const realm = auth.realm ?? \"API\";\n const expected =\n \"Basic \" +\n Buffer.from(`${auth.username}:${auth.password}`).toString(\"base64\");\n router.use((req, res, next) => {\n const authorization = (req as any).headers?.[\"authorization\"] ?? \"\";\n if (authorization !== expected) {\n res\n .status(401)\n .set(\"WWW-Authenticate\", `Basic realm=\"${realm}\"`)\n .set(\"Content-Type\", \"application/json\")\n .send(JSON.stringify({ success: false, error: \"Unauthorized\" }));\n return;\n }\n next();\n });\n }\n }\n\n // ── 3. Extra user middleware ────────────────────────────────────────────\n for (const mw of extraMiddleware) {\n router.use(mw);\n }\n\n // ── 4. Routes ─────────────────────────────────────────────────────────────\n\n // ── OpenAPI spec & docs endpoints (before auth so they're public) ────\n if (openapi !== false) {\n const specPath = `${base}/__spec.json`;\n const docsPath = `${base}/__docs`;\n\n router.get(specPath, (_req: any, res: any) => {\n const spec = getSpec();\n res\n .status(200)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(spec, null, 2));\n });\n\n router.get(docsPath, (req: any, res: any) => {\n // Rebuild spec URL with the correct prefix for the current context\n // (emulator, Cloud Functions URL, or custom domain).\n const specUrl = getLinkBase(req, base) + \"/__spec.json\";\n const html = scalarDocsHtml(openapiOpts.title ?? \"CRUD API\", specUrl);\n res\n .status(200)\n .set(\"Content-Type\", \"text/html; charset=utf-8\")\n .send(html);\n });\n }\n\n // OPTIONS for CORS preflight\n router.use((req, res, next) => {\n if (req.method === \"OPTIONS\") {\n handlers.handleOptions(req, res);\n return;\n }\n next();\n });\n\n // List: GET /:repoName\n router.get(`${base}/:repoName`, handlers.handleList);\n\n // Query: POST /:repoName/query (advanced filtering with body)\n router.post(`${base}/:repoName/query`, handlers.handleQuery);\n\n // Get: GET /:repoName/:id\n router.get(`${base}/:repoName/:id`, handlers.handleGet);\n\n // Create: POST /:repoName\n router.post(`${base}/:repoName`, handlers.handleCreate);\n\n // Update (full): PUT /:repoName/:id\n router.put(`${base}/:repoName/:id`, (req: any, res: any) =>\n handlers.handleUpdate(req, res, false),\n );\n\n // Update (partial): PATCH /:repoName/:id\n router.patch(`${base}/:repoName/:id`, (req: any, res: any) =>\n handlers.handleUpdate(req, res, true),\n );\n\n // Delete: DELETE /:repoName/:id\n router.delete(`${base}/:repoName/:id`, handlers.handleDelete);\n\n // ── Request handler ─────────────────────────────────────────────────────\n const handler = async (\n req: HttpRequest,\n res: HttpResponse,\n ): Promise<void> => {\n await router.handle(req as any, res as any);\n };\n\n // Attach spec getter so users can call server.spec() programmatically\n (handler as any).spec = getSpec;\n if (httpsOptions) (handler as any).httpsOptions = httpsOptions;\n\n return handler as ((req: any, res: any) => Promise<void>) & {\n /** Return the generated OpenAPI 3.1 document. */\n spec: () => OpenAPIDocument;\n /** Options to forward to `onRequest()` from firebase-functions. */\n httpsOptions?: Record<string, unknown>;\n };\n}\n\n// Re-exports for convenience\nexport { generateOpenAPISpec } from \"./openapi\";\nexport type { OpenAPIDocument, OpenAPISpecOptions } from \"./openapi\";\nexport type {\n ApiResponse,\n BasicAuthConfig,\n CrudRepoConfig,\n CrudRepoEntry,\n CrudRepoRegistry,\n CrudServerOptions,\n FieldRole,\n ListResponseData,\n Middleware,\n QueryRequestBody,\n RepoFieldPath,\n RepoRelationKeys,\n UserFieldPath,\n} from \"./types\";\n"]}
1
+ {"version":3,"sources":["../../../src/servers/admin/router.ts","../../../src/shared/date-config.ts","../../../src/servers/admin/index-url.ts","../../../src/servers/crud/handlers.ts","../../../src/servers/crud/openapi.ts","../../../src/servers/crud/index.ts"],"names":["compilePath","path","paramNames","src","c","_match","name","extractPath","req","raw","idx","MiniRouter","_req","res","err","middleware","handler","method","pattern","matchedRoute","params","route","m","i","enrichedReq","finalHandler","index","next","mw","currentMode","getDateHandling","isTimestampLike","v","coerceToDate","value","Timestamp","d","maybeNormalize","RANGE_OPS","ARRAY_OPS","toIndexOrder","dir","collectionIdFromPath","segments","buildIndexUrl","projectId","collectionId","isGroup","filters","sort","fields","seen","f","buildExemptionUrl","lastDir","buildCompositeUrl","databaseId","resource","payload","pbString","pbInt","pbMessage","encodeIndexField","urlDbId","encoded","toBase64","extractIndexUrl","message","pbVarint","n","out","pbTag","fieldNumber","wireType","bytes","bin","b64","field","extractProjectId","ref","r","candidates","isMissingIndexError","fe","toQueryError","ctx","isIndex","indexUrl","colId","sendJson","data","status","sendSuccess","meta","sendError","error","sendQueryError","fallbackMessage","verbose","qe","_idChars","generateFirestoreId","id","wrapDateSchemas","schema","def","typeName","z","shape","wrapped","k","inner","dflt","pickSchemaFields","systemKeys","picked","source","topLevel","validateData","partial","targetSchema","partialSchema","e","parseFilters","query","filterableFields","allowedFields","opMap","key","rawVal","val","match","op","opKey","parsedVal","parseValue","num","serializeCursor","snapshot","deserializeCursor","entry","cursor","docId","colRef","createCrudHandlers","registry","basePath","getRepoEntry","repoName","extractPathArgs","doc","pathKey","fullPath","args","fetchDocById","getterName","getter","handleList","ctxFilters","ctxSort","pageSize","direction","orderBy","orderDir","selectStr","select","s","includes","inc","queryOptions","cursorObj","result","responseData","handleQuery","body","w","validIncludes","allowed","invalid","group","handleGet","handleCreate","validation","customError","created","missingKeys","parentIds","handleUpdate","existingDoc","pathArgs","updated","handleDelete","handleOptions","zodToJsonSchema","schemaRef","errorResponse","description","successResponse","dataSchema","listResponse","itemSchema","paginationParams","filterParams","ops","queryBodySchema","buildPathsForEntry","base","modelSchemaName","createSchemaName","updateSchemaName","paths","tag","collectionPath","documentPath","idParam","capitalize","singularize","docOps","generateOpenAPISpec","options","title","version","servers","auth","schemas","allPaths","tags","modelName","createName","updateName","buildShape","fieldList","top","createShape","updateShape","entryPaths","securitySchemes","security","scalarDocsHtml","specUrl","getLinkBase","staticBasePath","project","region","target","service","host","readRawBody","createCrudServer","repos","parseBody","extraMiddleware","httpsOptions","cfg","resolvedSchema","mutableFields","createFields","fc","roles","role","parentKeys","pk","handlers","openapi","openapiOpts","_specCache","getSpec","authType","router","_res","realm","expected","specPath","docsPath","spec","html"],"mappings":"kFAqHA,SAASA,EAAAA,CAAYC,CAAAA,CAAyD,CAC5E,IAAMC,EAAuB,EAAC,CACxBC,CAAAA,CAAMF,CAAAA,CACT,QAAQ,qBAAA,CAAwBG,CAAAA,EAAOA,CAAAA,GAAM,GAAA,CAAMA,EAAI,CAAA,EAAA,EAAKA,CAAC,CAAA,CAAG,CAAA,CAChE,QAAQ,4BAAA,CAA8B,CAACC,CAAAA,CAAQC,CAAAA,IAC9CJ,EAAW,IAAA,CAAKI,CAAI,CAAA,CACb,SAAA,CACR,EAEH,OAAO,CAAE,OAAA,CAAS,IAAI,OAAO,CAAA,CAAA,EAAIH,CAAG,CAAA,CAAA,CAAG,CAAA,CAAG,WAAAD,CAAW,CACvD,CAEA,SAASK,GAAYC,CAAAA,CAAqB,CACxC,IAAMC,CAAAA,CAAMD,EAAI,IAAA,EAAQA,CAAAA,CAAI,GAAA,EAAO,GAAA,CAC7BE,EAAMD,CAAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAC3B,OAAOC,CAAAA,GAAQ,EAAA,CAAKD,CAAAA,CAAMA,CAAAA,CAAI,MAAM,CAAA,CAAGC,CAAG,CAC5C,CAMO,IAAMC,CAAAA,CAAN,KAAiB,CAAjB,WAAA,EAAA,CACL,KAAQ,MAAA,CAA0B,EAAC,CACnC,IAAA,CAAQ,WAAA,CAA4B,EAAC,CACrC,IAAA,CAAQ,gBAAgC,CAACC,CAAAA,CAAMC,CAAAA,GAAQ,CACrDA,EAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,WAAW,EAClC,CAAA,CACA,IAAA,CAAQ,YAAA,CAAiE,CACvEC,CAAAA,CACAF,CAAAA,CACAC,CAAAA,GACG,CACH,QAAQ,KAAA,CAAM,cAAA,CAAgBC,CAAG,CAAA,CACjCD,EAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,uBAAuB,EAC9C,EAAA,CAIA,GAAA,CAAIE,CAAAA,CAA8B,CAChC,OAAA,IAAA,CAAK,WAAA,CAAY,IAAA,CAAKA,CAAU,EACzB,IACT,CAEA,GAAA,CAAId,CAAAA,CAAce,EAA6B,CAC7C,OAAO,IAAA,CAAK,QAAA,CAAS,MAAOf,CAAAA,CAAMe,CAAO,CAC3C,CAEA,KAAKf,CAAAA,CAAce,CAAAA,CAA6B,CAC9C,OAAO,KAAK,QAAA,CAAS,MAAA,CAAQf,CAAAA,CAAMe,CAAO,CAC5C,CAEA,GAAA,CAAIf,CAAAA,CAAce,CAAAA,CAA6B,CAC7C,OAAO,IAAA,CAAK,QAAA,CAAS,KAAA,CAAOf,CAAAA,CAAMe,CAAO,CAC3C,CAEA,MAAMf,CAAAA,CAAce,CAAAA,CAA6B,CAC/C,OAAO,KAAK,QAAA,CAAS,OAAA,CAASf,CAAAA,CAAMe,CAAO,CAC7C,CAEA,MAAA,CAAOf,CAAAA,CAAce,CAAAA,CAA6B,CAChD,OAAO,IAAA,CAAK,QAAA,CAAS,QAAA,CAAUf,EAAMe,CAAO,CAC9C,CAEA,UAAA,CAAWA,EAA6B,CACtC,OAAA,IAAA,CAAK,eAAA,CAAkBA,CAAAA,CAChB,IACT,CAEA,OAAA,CAAQA,CAAAA,CAAiE,CACvE,YAAK,YAAA,CAAeA,CAAAA,CACb,IACT,CAEQ,SAASC,CAAAA,CAAgBhB,CAAAA,CAAce,CAAAA,CAA6B,CAC1E,GAAM,CAAE,OAAA,CAAAE,CAAAA,CAAS,UAAA,CAAAhB,CAAW,CAAA,CAAIF,EAAAA,CAAYC,CAAI,CAAA,CAChD,YAAK,MAAA,CAAO,IAAA,CAAK,CACf,MAAA,CAAQgB,EAAO,WAAA,EAAY,CAC3B,OAAA,CAAAC,CAAAA,CACA,WAAAhB,CAAAA,CACA,OAAA,CAAAc,CACF,CAAC,EACM,IACT,CAIA,MAAM,MAAA,CAAOR,CAAAA,CAAaK,CAAAA,CAA4B,CACpD,IAAMI,GAAUT,CAAAA,CAAI,MAAA,EAAU,KAAA,EAAO,WAAA,GAC/BP,CAAAA,CAAOM,EAAAA,CAAYC,CAAG,CAAA,CAGxBW,EAAqC,IAAA,CACrCC,CAAAA,CAAsB,EAAC,CAE3B,QAAWC,CAAAA,IAAS,IAAA,CAAK,MAAA,CAAQ,CAC/B,GAAIA,CAAAA,CAAM,MAAA,GAAWJ,CAAAA,CAAQ,SAC7B,IAAMK,CAAAA,CAAIrB,CAAAA,CAAK,KAAA,CAAMoB,CAAAA,CAAM,OAAO,CAAA,CAClC,GAAIC,CAAAA,CAAG,CACLH,EAAeE,CAAAA,CACfD,CAAAA,CAAS,EAAC,CACVC,EAAM,UAAA,CAAW,OAAA,CAAQ,CAACf,CAAAA,CAAMiB,IAAM,CACpCH,CAAAA,CAAOd,CAAI,CAAA,CAAI,mBAAmBgB,CAAAA,CAAEC,CAAAA,CAAI,CAAC,CAAA,EAAK,EAAE,EAClD,CAAC,CAAA,CACD,KACF,CACF,CAEA,IAAMC,CAAAA,CAAc,MAAA,CAAO,OAAOhB,CAAAA,CAAK,CAAE,MAAA,CAAAY,CAAO,CAAC,CAAA,CAG3CJ,CAAAA,CAAUG,CAAAA,CAAeA,CAAAA,CAAa,OAAA,CAAU,IAAA,CAAK,eAAA,CAE3D,GAAI,CACF,MAAM,IAAA,CAAK,kBAAA,CAAmBK,CAAAA,CAAaX,EAAKG,CAAO,EACzD,CAAA,MAASF,CAAAA,CAAK,CACZ,IAAA,CAAK,YAAA,CAAaA,CAAAA,CAAKN,CAAAA,CAAKK,CAAG,EACjC,CACF,CAEA,MAAc,mBACZL,CAAAA,CACAK,CAAAA,CACAY,CAAAA,CACe,CACf,IAAIC,CAAAA,CAAQ,CAAA,CAENC,CAAAA,CAAO,SAA2B,CACtC,GAAID,CAAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,OAAQ,CACnC,IAAME,CAAAA,CAAK,IAAA,CAAK,YAAYF,CAAAA,EAAO,CAAA,CACnC,MAAME,CAAAA,CAAGpB,EAAKK,CAAAA,CAAKc,CAAI,EACzB,CAAA,KACE,MAAMF,CAAAA,CAAajB,CAAAA,CAAKK,CAAG,EAE/B,EAEA,MAAMc,CAAAA,GACR,CACF,EC3PA,IAAIE,EAAAA,CAAgC,WAM7B,SAASC,EAAAA,EAAoC,CAClD,OAAOD,EACT,CAEA,SAASE,EAAAA,CACPC,EACiD,CACjD,OACE,OAAOA,CAAAA,EAAM,UACbA,CAAAA,GAAM,IAAA,EACN,OAAQA,CAAAA,CAA6B,UAAa,QAAA,EAClD,OAAQA,CAAAA,CAAiC,YAAA,EAAiB,QAE9D,CAEO,SAASC,EAAAA,CAAaC,CAAAA,CAA6B,CACxD,GAAIA,CAAAA,EAAU,IAAA,CAA6B,OAAO,KAClD,GAAIA,CAAAA,YAAiB,IAAA,CAAM,OAAO,OAAO,KAAA,CAAMA,CAAAA,CAAM,OAAA,EAAS,EAAI,IAAA,CAAOA,CAAAA,CACzE,GAAIA,CAAAA,YAAiBC,oBAAW,OAAOD,CAAAA,CAAM,MAAA,EAAO,CACpD,GAAIH,EAAAA,CAAgBG,CAAK,CAAA,CACvB,OAAO,IAAI,IAAA,CACTA,CAAAA,CAAM,QAAA,CAAW,GAAA,CAAO,KAAK,KAAA,CAAMA,CAAAA,CAAM,YAAA,CAAe,GAAG,CAC7D,CAAA,CAEF,GAAI,OAAOA,CAAAA,EAAU,SAAU,CAC7B,IAAME,CAAAA,CAAI,IAAI,KAAKF,CAAK,CAAA,CACxB,OAAO,MAAA,CAAO,KAAA,CAAME,CAAAA,CAAE,OAAA,EAAS,EAAI,IAAA,CAAOA,CAC5C,CACA,GAAI,OAAOF,CAAAA,EAAU,QAAA,CAAU,CAC7B,IAAME,EAAI,IAAI,IAAA,CAAKF,CAAK,CAAA,CACxB,OAAO,MAAA,CAAO,KAAA,CAAME,CAAAA,CAAE,OAAA,EAAS,CAAA,CAAI,IAAA,CAAOA,CAC5C,CACA,OAAO,IACT,CAuBO,SAASC,EAAAA,CAAkBH,CAAAA,CAAa,CAC7C,OAAkEA,CACpE,CC7BA,IAAMI,GAAY,IAAI,GAAA,CAAa,CAAC,GAAA,CAAK,KAAM,GAAA,CAAK,IAAA,CAAM,IAAI,CAAC,EACzDC,EAAAA,CAAY,IAAI,GAAA,CAAa,CAAC,iBAAkB,oBAAoB,CAAC,CAAA,CAE3E,SAASC,EAAaC,CAAAA,CAAkD,CACtE,OAAOA,CAAAA,GAAQ,OAAS,YAAA,CAAe,WACzC,CAMO,SAASC,GAAqBzC,CAAAA,CAAsB,CACzD,IAAM0C,CAAAA,CAAW1C,CAAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA,CAC/C,OAAO0C,CAAAA,CAASA,EAAS,MAAA,CAAS,CAAC,CAAA,EAAK1C,CAC1C,CAcO,SAAS2C,EAAAA,CACdC,CAAAA,CACAC,CAAAA,CACAC,EACAC,CAAAA,CACAC,CAAAA,CACQ,CACR,IAAMC,EAAuB,EAAC,CACxBC,CAAAA,CAAO,IAAI,IAGjB,IAAA,IAAWC,CAAAA,IAAKJ,CAAAA,CACd,GAAII,EAAE,EAAA,GAAO,IAAA,EAAQA,CAAAA,CAAE,EAAA,GAAO,MAAQA,CAAAA,CAAE,EAAA,GAAO,QAAA,CAAU,CACvD,GAAID,CAAAA,CAAK,GAAA,CAAIC,CAAAA,CAAE,KAAK,EAAG,SACvBD,CAAAA,CAAK,GAAA,CAAIC,CAAAA,CAAE,KAAK,CAAA,CAChBF,CAAAA,CAAO,IAAA,CAAK,CAAE,UAAWE,CAAAA,CAAE,KAAA,CAAO,KAAA,CAAO,WAAY,CAAC,EACxD,CAIF,IAAA,IAAWA,CAAAA,IAAKJ,EACd,GAAIT,EAAAA,CAAU,GAAA,CAAIa,CAAAA,CAAE,EAAE,CAAA,CAAG,CACvB,GAAID,CAAAA,CAAK,GAAA,CAAIC,CAAAA,CAAE,KAAK,CAAA,CAAG,SACvBD,CAAAA,CAAK,GAAA,CAAIC,CAAAA,CAAE,KAAK,EAChBF,CAAAA,CAAO,IAAA,CAAK,CAAE,SAAA,CAAWE,EAAE,KAAA,CAAO,WAAA,CAAa,UAAW,CAAC,EAC7D,CAIF,IAAA,IAAWA,CAAAA,IAAKJ,CAAAA,CACd,GAAIV,EAAAA,CAAU,GAAA,CAAIc,CAAAA,CAAE,EAAE,EAAG,CACvB,GAAID,CAAAA,CAAK,GAAA,CAAIC,EAAE,KAAK,CAAA,CAAG,SACvBD,CAAAA,CAAK,IAAIC,CAAAA,CAAE,KAAK,CAAA,CAEhB,IAAMX,EACJQ,CAAAA,EAAM,KAAA,GAAUG,CAAAA,CAAE,KAAA,CAAQZ,EAAaS,CAAAA,CAAK,GAAG,CAAA,CAAI,WAAA,CACrDC,EAAO,IAAA,CAAK,CAAE,SAAA,CAAWE,CAAAA,CAAE,MAAO,KAAA,CAAOX,CAAI,CAAC,EAChD,CAaF,GATIQ,CAAAA,EAAQ,CAACE,CAAAA,CAAK,IAAIF,CAAAA,CAAK,KAAK,CAAA,EAC9BC,CAAAA,CAAO,KAAK,CAAE,SAAA,CAAWD,CAAAA,CAAK,KAAA,CAAO,KAAA,CAAOT,CAAAA,CAAaS,CAAAA,CAAK,GAAG,CAAE,CAAC,CAAA,CAQlEC,CAAAA,CAAO,MAAA,GAAW,GAAKH,CAAAA,CACzB,OAAOM,EAAAA,CAAkBR,CAAAA,CAAWC,EAAcI,CAAAA,CAAO,CAAC,CAAE,CAAA,CAQ9D,IAAMI,CAAAA,CACJL,CAAAA,EAAQC,CAAAA,CAAO,IAAA,CAAME,GAAMA,CAAAA,CAAE,SAAA,GAAcH,CAAAA,CAAK,KAAK,EACjDT,CAAAA,CAAaS,CAAAA,CAAK,GAAG,CAAA,CACrB,YACN,OAAAC,CAAAA,CAAO,IAAA,CAAK,CAAE,UAAW,UAAA,CAAY,KAAA,CAAOI,CAAQ,CAAC,EAE9CC,EAAAA,CAAkBV,CAAAA,CAAWC,CAAAA,CAAcC,CAAAA,CAASG,CAAM,CACnE,CAeO,SAASK,EAAAA,CACdV,EACAC,CAAAA,CACAC,CAAAA,CACAG,CAAAA,CACAM,CAAAA,CAAqB,YACb,CACR,IAAMC,CAAAA,CAAW,CAAA,SAAA,EAAYZ,CAAS,CAAA,WAAA,EAAcW,CAAU,CAAA,kBAAA,EAAqBV,CAAY,aAEzFY,CAAAA,CAAoB,CACxB,GAAGC,CAAAA,CAAS,EAAGF,CAAQ,CAAA,CACvB,GAAGG,CAAAA,CAAM,CAAA,CAAGb,CAAAA,CAAU,CAAA,CAAI,CAAC,CAC7B,CAAA,CACA,IAAA,IAAWK,CAAAA,IAAKF,CAAAA,CACdQ,EAAQ,IAAA,CAAK,GAAGG,EAAAA,CAAU,CAAA,CAAGC,GAAiBV,CAAC,CAAC,CAAC,CAAA,CAGnD,IAAMW,CAAAA,CAAUP,CAAAA,GAAe,WAAA,CAAc,WAAA,CAAcA,EACrDQ,CAAAA,CAAU,kBAAA,CAAmBC,EAAAA,CAASP,CAAO,CAAC,CAAA,CACpD,OAAO,CAAA,4CAAA,EAA+Cb,CAAS,wBAAwBkB,CAAO,CAAA,0BAAA,EAA6BC,CAAO,CAAA,CACpI,CAMO,SAASE,EAAAA,CAAgBC,CAAAA,CAAqC,CAInE,OAHcA,CAAAA,CAAQ,KAAA,CACpB,kDACF,CAAA,GACe,CAAC,CAClB,CAwBA,SAASC,CAAAA,CAASC,EAAqB,CACrC,IAAMC,CAAAA,CAAgB,GAClBtC,CAAAA,CAAIqC,CAAAA,GAAM,CAAA,CACd,KAAOrC,GAAK,GAAA,EACVsC,CAAAA,CAAI,IAAA,CAAMtC,CAAAA,CAAI,IAAQ,GAAI,CAAA,CAC1BA,CAAAA,IAAO,CAAA,CAET,OAAAsC,CAAAA,CAAI,IAAA,CAAKtC,CAAAA,CAAI,GAAI,CAAA,CACVsC,CACT,CAEA,SAASC,EAAMC,CAAAA,CAAqBC,CAAAA,CAAyB,CAC3D,OAAQD,GAAe,CAAA,CAAKC,CAC9B,CAEA,SAASd,EAASa,CAAAA,CAAqBtC,CAAAA,CAAyB,CAC9D,IAAMwC,EAAQ,KAAA,CAAM,IAAA,CAAK,IAAI,WAAA,GAAc,MAAA,CAAOxC,CAAK,CAAC,CAAA,CACxD,OAAO,CAACqC,CAAAA,CAAMC,CAAAA,CAAa,CAAC,EAAG,GAAGJ,CAAAA,CAASM,CAAAA,CAAM,MAAM,EAAG,GAAGA,CAAK,CACpE,CAEA,SAASd,CAAAA,CAAMY,CAAAA,CAAqBtC,CAAAA,CAAyB,CAC3D,OAAO,CAACqC,CAAAA,CAAMC,CAAAA,CAAa,CAAC,EAAG,GAAGJ,CAAAA,CAASlC,CAAK,CAAC,CACnD,CAEA,SAAS2B,EAAAA,CAAUW,CAAAA,CAAqBd,EAA6B,CACnE,OAAO,CAACa,CAAAA,CAAMC,EAAa,CAAC,CAAA,CAAG,GAAGJ,CAAAA,CAASV,EAAQ,MAAM,CAAA,CAAG,GAAGA,CAAO,CACxE,CAGA,SAASI,EAAAA,CAAiBV,EAAyB,CACjD,IAAMkB,CAAAA,CAAgB,CAAC,GAAGX,CAAAA,CAAS,CAAA,CAAGP,CAAAA,CAAE,SAAS,CAAC,CAAA,CAClD,OAAIA,CAAAA,CAAE,WAAA,GAAgB,WACpBkB,CAAAA,CAAI,IAAA,CAAK,GAAGV,CAAAA,CAAM,EAAG,CAAC,CAAC,CAAA,CAEvBU,CAAAA,CAAI,KAAK,GAAGV,CAAAA,CAAM,CAAA,CAAGR,CAAAA,CAAE,QAAU,YAAA,CAAe,CAAA,CAAI,CAAC,CAAC,EAEjDkB,CACT,CAEA,SAASL,EAAAA,CAASS,EAAyB,CAGzC,IAAMC,CAAAA,CAAM,MAAA,CAAO,aAAa,GAAGD,CAAK,CAAA,CACpCE,CAAAA,CACJ,GAAI,OAAO,MAAA,CAAW,GAAA,CACpBA,CAAAA,CAAM,OAAO,IAAA,CAAKF,CAAK,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,CAAA,KAAA,GACjC,OAAO,IAAA,CAAS,GAAA,CACzBE,EAAM,IAAA,CAAKD,CAAG,CAAA,CAAA,KAEd,MAAM,IAAI,KAAA,CAAM,6BAA6B,CAAA,CAE/C,OAAOC,CAAAA,CAAI,OAAA,CAAQ,KAAA,CAAO,EAAE,CAC9B,CAMO,SAASvB,EAAAA,CACdR,CAAAA,CACAC,EACA+B,CAAAA,CACArB,CAAAA,CAAqB,WAAA,CACb,CACR,IAAMC,CAAAA,CAAW,CAAA,SAAA,EAAYZ,CAAS,CAAA,WAAA,EAAcW,CAAU,CAAA,kBAAA,EAAqBV,CAAY,CAAA,QAAA,EAAW+B,CAAAA,CAAM,SAAS,CAAA,CAAA,CAEnHnB,CAAAA,CAAoB,CACxB,GAAGC,EAAS,CAAA,CAAGF,CAAQ,CAAA,CACvB,GAAGG,EAAM,CAAA,CAAG,CAAC,CAAA,CACb,GAAGC,GAAU,CAAA,CAAGC,EAAAA,CAAiBe,CAAK,CAAC,CACzC,CAAA,CAIMd,CAAAA,CAAUP,CAAAA,GAAe,WAAA,CAAc,YAAcA,CAAAA,CAKrDQ,CAAAA,CAAU,kBAAA,CAAmBC,EAAAA,CAASP,CAAO,CAAC,CAAA,CACpD,OAAO,CAAA,4CAAA,EAA+Cb,CAAS,CAAA,qBAAA,EAAwBkB,CAAO,CAAA,oCAAA,EAAuCC,CAAO,EAC9I,CASO,SAASc,EAAAA,CAAiBC,CAAAA,CAAkC,CAEjE,IAAMC,CAAAA,CAAID,CAAAA,CACJE,CAAAA,CAAwB,CAC5BD,CAAAA,EAAG,SAAA,EAAW,SAAA,CACdA,CAAAA,EAAG,WAAW,GAAA,EAAK,OAAA,EAAS,SAAA,CAC5BA,CAAAA,EAAG,WAAW,SAAA,EAAW,SAAA,CACzBA,CAAAA,EAAG,SAAA,EAAW,YAAY,SAAA,CAC1BA,CAAAA,EAAG,UAAA,EAAY,SACjB,EACA,IAAA,IAAW5E,CAAAA,IAAK6E,CAAAA,CACd,GAAI,OAAO7E,CAAAA,EAAM,QAAA,EAAYA,CAAAA,CAAE,MAAA,CAAS,EAAG,OAAOA,CAAAA,CAMpD,OAHE,OAAA,CAAQ,IAAI,cAAA,EACZ,OAAA,CAAQ,GAAA,CAAI,oBAAA,EACZ,QAAQ,GAAA,CAAI,mBAAA,EACA,MAChB,CAyBO,SAAS8E,EAAAA,CAAoBpE,CAAAA,CAAuB,CACzD,IAAMqE,EAAKrE,CAAAA,CACX,OAAKqE,CAAAA,CACDA,CAAAA,CAAG,OAAS,CAAA,CAAU,IAAA,CACnB,OAAOA,CAAAA,CAAG,SAAY,QAAA,CACzBA,CAAAA,CAAG,OAAA,CAAQ,QAAA,CAAS,mBAAmB,CAAA,CACvC,KAAA,CAJY,KAKlB,CAUO,SAASC,EAAAA,CACdtE,CAAAA,CACAuE,CAAAA,CACY,CACZ,IAAMF,CAAAA,CAAMrE,CAAAA,EAAO,EAAC,CACdwE,EAAUJ,EAAAA,CAAoBpE,CAAG,CAAA,CAEnCyE,CAAAA,CACJ,GAAID,CAAAA,GACFC,CAAAA,CAAWJ,CAAAA,CAAG,QAAUjB,EAAAA,CAAgBiB,CAAAA,CAAG,OAAO,CAAA,CAAI,OAClD,CAACI,CAAAA,CAAAA,CAAU,CACb,IAAM1C,EAAYiC,EAAAA,CAAiBO,CAAAA,CAAI,GAAG,CAAA,CAC1C,GAAIxC,CAAAA,CAAW,CACb,IAAM2C,CAAAA,CAAQ9C,GAAqB2C,CAAAA,CAAI,IAAI,CAAA,CAC3CE,CAAAA,CAAW3C,GACTC,CAAAA,CACA2C,CAAAA,CACAH,CAAAA,CAAI,OAAA,CACJA,EAAI,OAAA,CACJA,CAAAA,CAAI,IACN,EACF,CACF,CAGF,OAAO,CACL,IAAA,CAAMC,EAAU,OAAA,CAAU,OAAA,CAC1B,OAAA,CAASA,CAAAA,CACL,iEACCH,CAAAA,CAAG,OAAA,EAAW,cAAA,CACnB,QAAA,CAAAI,CACF,CACF,CC9VA,SAASE,CAAAA,CAAY5E,EAAU6E,CAAAA,CAAsBC,CAAAA,CAAS,GAAA,CAAW,CACvE,IAAMjC,CAAAA,CAAUrB,EAAAA,CAAeqD,CAAI,CAAA,CACnC7E,EACG,MAAA,CAAO8E,CAAM,CAAA,CACb,GAAA,CAAI,eAAgB,iCAAiC,CAAA,CACrD,IAAA,CAAK,IAAA,CAAK,SAAA,CAAUjC,CAAO,CAAC,EACjC,CAEA,SAASkC,CAAAA,CACP/E,CAAAA,CACA6E,CAAAA,CACAG,EACAF,CAAAA,CAAS,GAAA,CACH,CACNF,CAAAA,CAAS5E,EAAK,CAAE,OAAA,CAAS,IAAA,CAAM,IAAA,CAAA6E,EAAM,IAAA,CAAAG,CAAK,CAAA,CAAGF,CAAM,EACrD,CAEA,SAASG,CAAAA,CAAUjF,CAAAA,CAAUkF,EAAeJ,CAAAA,CAAS,GAAA,CAAW,CAC9DF,CAAAA,CAAS5E,EAAK,CAAE,OAAA,CAAS,KAAA,CAAO,KAAA,CAAAkF,CAAM,CAAA,CAAGJ,CAAM,EACjD,CAQA,SAASK,CAAAA,CACPnF,CAAAA,CACAC,CAAAA,CACAuE,CAAAA,CACAY,EACAC,CAAAA,CACM,CACN,IAAMC,CAAAA,CAAKf,GAAatE,CAAAA,CAAKuE,CAAG,CAAA,CAC1BC,CAAAA,CAAUa,EAAG,IAAA,GAAS,OAAA,CACtBR,CAAAA,CAASL,CAAAA,CAAU,IAAM,GAAA,CAMzB5B,CAAAA,CAAuB,CAAE,OAAA,CAAS,MAAO,KAAA,CAL/B4B,CAAAA,CACZa,CAAAA,CAAG,OAAA,CACHD,GAAWpF,CAAAA,YAAe,KAAA,CACxBA,CAAAA,CAAI,OAAA,CACJmF,CACwD,CAAA,CAC1DX,CAAAA,GACF5B,CAAAA,CAAQ,UAAY,OAAA,CAChByC,CAAAA,CAAG,QAAA,GAAUzC,CAAAA,CAAQ,SAAWyC,CAAAA,CAAG,QAAA,CAAA,CAAA,CAEzCV,CAAAA,CAAS5E,CAAAA,CAAK6C,EAASiC,CAAM,EAC/B,CAMA,IAAMS,GACJ,gEAAA,CAGF,SAASC,EAAAA,EAA8B,CACrC,IAAIC,CAAAA,CAAK,EAAA,CACT,IAAA,IAAS/E,CAAAA,CAAI,EAAGA,CAAAA,CAAI,EAAA,CAAIA,CAAAA,EAAAA,CACtB+E,CAAAA,EAAMF,GAAS,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,QAAO,CAAIA,EAAAA,CAAS,MAAM,CAAC,EAEnE,OAAOE,CACT,CAWA,SAASC,EAAgBC,CAAAA,CAA8B,CACrD,IAAMC,CAAAA,CAAOD,EAAe,IAAA,EAASA,CAAAA,CAAe,GAAA,CACpD,GAAI,CAACC,CAAAA,CAAK,OAAOD,CAAAA,CACjB,IAAME,EAAWD,CAAAA,CAAI,QAAA,EAAYA,CAAAA,CAAI,IAAA,CAErC,GAAIC,CAAAA,GAAa,SAAA,EAAaA,CAAAA,GAAa,MAAA,CACzC,OAAOC,KAAA,CAAE,UAAA,CAAY3E,CAAAA,EAAMC,EAAAA,CAAaD,CAAC,CAAA,EAAKA,CAAAA,CAAGwE,CAAmB,EAEtE,GAAIE,CAAAA,GAAa,WAAA,EAAeA,CAAAA,GAAa,SAAU,CACrD,IAAME,CAAAA,CAASJ,CAAAA,CAA4B,MACrCK,CAAAA,CAAqC,EAAC,CAC5C,IAAA,GAAW,CAACC,CAAAA,CAAG9E,CAAC,CAAA,GAAK,MAAA,CAAO,QAAQ4E,CAAK,CAAA,CACvCC,CAAAA,CAAQC,CAAC,EAAIP,CAAAA,CAAgBvE,CAAc,CAAA,CAE7C,OAAO2E,MAAE,MAAA,CAAOE,CAAO,CACzB,CACA,GAAIH,CAAAA,GAAa,UAAA,EAAcA,CAAAA,GAAa,OAAA,CAAS,CACnD,IAAMK,CAAAA,CAAQN,CAAAA,CAAI,OAAA,EAAWA,EAAI,IAAA,CACjC,GAAIM,CAAAA,CAAO,OAAOJ,MAAE,KAAA,CAAMJ,CAAAA,CAAgBQ,CAAK,CAAC,CAClD,CACA,GAAIL,CAAAA,GAAa,aAAA,EAAiBA,IAAa,UAAA,CAAY,CACzD,IAAMK,CAAAA,CAAQN,EAAI,SAAA,CAClB,GAAIM,CAAAA,CAAO,OAAOR,EAAgBQ,CAAK,CAAA,CAAE,QAAA,EAC3C,CACA,GAAIL,CAAAA,GAAa,aAAA,EAAiBA,IAAa,UAAA,CAAY,CACzD,IAAMK,CAAAA,CAAQN,EAAI,SAAA,CAClB,GAAIM,CAAAA,CAAO,OAAOR,EAAgBQ,CAAK,CAAA,CAAE,QAAA,EAC3C,CACA,GAAIL,CAAAA,GAAa,YAAA,EAAgBA,CAAAA,GAAa,UAAW,CACvD,IAAMK,CAAAA,CAAQN,CAAAA,CAAI,UACZO,CAAAA,CAAOP,CAAAA,CAAI,YAAA,CACjB,GAAIM,EAAO,CACT,IAAMF,CAAAA,CAAUN,CAAAA,CAAgBQ,CAAK,CAAA,CACrC,OAAO,OAAOC,CAAAA,EAAS,WACnBH,CAAAA,CAAQ,OAAA,CAAQG,CAAAA,EAAM,EACtBH,CAAAA,CAAQ,OAAA,CAAQG,CAAI,CAC1B,CACF,CACA,OAAOR,CACT,CAQA,SAASS,EAAAA,CACPT,CAAAA,CACAtD,CAAAA,CACAgE,CAAAA,CAAuB,EAAC,CACN,CAClB,IAAMN,CAAAA,CAAQJ,EAAO,KAAA,CACfW,CAAAA,CAAoC,EAAC,CAErCC,EAASlE,CAAAA,EAAUA,CAAAA,CAAO,MAAA,CAAS,CAAA,CAAIA,CAAAA,CAAS,MAAA,CAAO,IAAA,CAAK0D,CAAK,EAEvE,IAAA,IAAW/B,CAAAA,IAASuC,CAAAA,CAAQ,CAC1B,GAAIF,CAAAA,CAAW,QAAA,CAASrC,CAAK,CAAA,CAAG,SAChC,IAAMwC,CAAAA,CAAWxC,CAAAA,CAAM,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,CAC/BwC,CAAAA,EAAYT,EAAMS,CAAQ,CAAA,GAC5BF,CAAAA,CAAOE,CAAQ,EAAIT,CAAAA,CAAMS,CAAQ,CAAA,EAErC,CAEA,OAAOV,KAAA,CAAE,MAAA,CAAOQ,CAAM,CACxB,CAKA,SAASG,EAAAA,CACPd,CAAAA,CACAd,CAAAA,CACAxC,EACAqE,CAAAA,CAAU,KAAA,CACVL,CAAAA,CAAuB,GAGa,CACpC,GAAI,CACF,IAAMM,EAAeP,EAAAA,CAAiBT,CAAAA,CAAQtD,CAAAA,CAAQgE,CAAU,EAC1DO,CAAAA,CAAgBF,CAAAA,CAAUC,CAAAA,CAAa,OAAA,GAAYA,CAAAA,CAMzD,OAAO,CAAE,OAAA,CAAS,GAAM,IAAA,CAAA,CAJtB1F,EAAAA,EAAgB,GAAM,WAAA,CACjByE,EAAgBkB,CAAa,CAAA,CAC9BA,CAAAA,EACqB,KAAA,CAAM/B,CAAI,CAC2B,CAClE,CAAA,MAAS5E,EAAK,CACZ,OAAIA,CAAAA,YAAe6F,KAAA,CAAE,SAIZ,CACL,OAAA,CAAS,KAAA,CACT,KAAA,CAAO,sBALQ7F,CAAAA,CAAI,MAAA,CAAO,GAAA,CACzB4G,CAAAA,EAAM,GAAGA,CAAAA,CAAE,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,EAAA,EAAKA,CAAAA,CAAE,OAAO,CAAA,CAC1C,EAGwC,IAAA,CAAK,IAAI,CAAC,CAAA,CAClD,EAEK,CAAE,OAAA,CAAS,KAAA,CAAO,KAAA,CAAO,mBAAoB,CACtD,CACF,CA+BA,SAASC,GACPC,CAAAA,CACAC,CAAAA,CACgB,CAChB,IAAM7E,EAA0B,EAAC,CAC3B8E,CAAAA,CAAgBD,CAAAA,CAAmB,IAAI,GAAA,CAAIA,CAAgB,CAAA,CAAI,IAAA,CAE/DE,EAAiC,CACrC,EAAA,CAAI,IAAA,CACJ,EAAA,CAAI,KACJ,EAAA,CAAI,GAAA,CACJ,GAAA,CAAK,IAAA,CACL,GAAI,GAAA,CACJ,GAAA,CAAK,IAAA,CACL,EAAA,CAAI,KACJ,GAAA,CAAK,QAAA,CACL,QAAA,CAAU,gBAAA,CACV,YAAa,oBACf,CAAA,CAEA,IAAA,GAAW,CAACC,EAAKC,CAAM,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQL,CAAK,CAAA,CAAG,CAIjD,GAHIK,CAAAA,GAAW,QAIb,CAAC,QAAA,CAAU,OAAA,CAAS,UAAA,CAAY,UAAW,UAAA,CAAY,QAAQ,CAAA,CAAE,QAAA,CAC/DD,CACF,CAAA,CAEA,SAEF,IAAME,CAAAA,CAAM,MAAM,OAAA,CAAQD,CAAM,CAAA,CAAIA,CAAAA,CAAO,CAAC,CAAA,CAAIA,CAAAA,CAChD,GAAIC,CAAAA,GAAQ,QAAaA,CAAAA,GAAQ,EAAA,CAAI,SAGrC,IAAMC,EAAQH,CAAAA,CAAI,KAAA,CAAM,gBAAgB,CAAA,CACpCnD,EACAuD,CAAAA,CAAc,IAAA,CAElB,GAAID,CAAAA,EAASA,EAAM,CAAC,CAAA,EAAKA,CAAAA,CAAM,CAAC,EAAG,CACjCtD,CAAAA,CAAQsD,CAAAA,CAAM,CAAC,EACf,IAAME,CAAAA,CAAQF,CAAAA,CAAM,CAAC,EACrB,GAAIJ,CAAAA,CAAMM,CAAK,CAAA,CACbD,EAAKL,CAAAA,CAAMM,CAAK,CAAA,CAAA,KAEhB,QAEJ,CAAA,KAAA,GAAW,CAACF,CAAAA,CACVtD,CAAAA,CAAQmD,OAER,SAIF,GAAIF,CAAAA,EAAiB,CAACA,EAAc,GAAA,CAAIjD,CAAK,CAAA,CAAG,SAGhD,IAAIyD,CAAAA,CAAqBJ,CAAAA,CAGrBE,CAAAA,GAAO,IAAA,EAAQA,IAAO,QAAA,EAAYA,CAAAA,GAAO,oBAAA,CAC3CE,CAAAA,CAAYJ,EAAI,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAKlG,GAAMuG,EAAAA,CAAWvG,CAAAA,CAAE,IAAA,EAAM,CAAC,CAAA,CAE1DsG,CAAAA,CAAYC,EAAAA,CAAWL,CAAG,EAG5BlF,CAAAA,CAAQ,IAAA,CAAK,CAAE,KAAA,CAAA6B,EAAO,EAAA,CAAAuD,CAAAA,CAAI,KAAA,CAAOE,CAAU,CAAC,EAC9C,CAEA,OAAOtF,CACT,CAKA,SAASuF,EAAAA,CAAWL,CAAAA,CAAsB,CAExC,GAAIA,CAAAA,GAAQ,MAAA,CAAQ,OAAO,KAAA,CAC3B,GAAIA,CAAAA,GAAQ,OAAA,CAAS,OAAO,MAAA,CAC5B,GAAIA,CAAAA,GAAQ,MAAA,CAAQ,OAAO,IAAA,CAG3B,IAAMM,CAAAA,CAAM,MAAA,CAAON,CAAG,CAAA,CACtB,OAAI,CAAC,KAAA,CAAMM,CAAG,GAAKN,CAAAA,GAAQ,EAAA,CAAWM,CAAAA,CAG/BN,CACT,CASA,SAASO,CAAAA,CACPC,CAAAA,CACgC,CAChC,OAAKA,CAAAA,CACE,CAAE,KAAA,CAAOA,CAAAA,CAAS,EAAG,CAAA,CADN,IAExB,CAMA,eAAeC,GACbC,CAAAA,CACAC,CAAAA,CAC0E,CAC1E,GAAI,CAACA,CAAAA,EAAU,OAAOA,CAAAA,EAAW,QAAA,CAAU,OAC3C,IAAMC,CAAAA,CAASD,CAAAA,CAAmC,KAAA,CAClD,GAAI,OAAOC,CAAAA,EAAU,QAAA,CAErB,GAAI,CAEF,IAAMC,CAAAA,CAASH,CAAAA,CAAM,IAAA,CAClB,IACH,GAAI,OAAOG,CAAAA,CAAO,GAAA,EAAQ,WAAY,OACtC,IAAML,CAAAA,CAAW,MAAMK,EAAO,GAAA,CAAID,CAAK,CAAA,CAAE,GAAA,GACzC,OAAOJ,CAAAA,CAAS,MAAA,CAASA,CAAAA,CAAW,MACtC,CAAA,KAAQ,CACN,MACF,CACF,CAMO,SAASM,EAAAA,CACdC,CAAAA,CACAC,CAAAA,CACAhD,CAAAA,CACA,CAEA,SAASiD,CAAAA,CACPC,EACAvI,CAAAA,CACsB,CACtB,OAAI,CAACuI,GAAY,CAACH,CAAAA,CAASG,CAAQ,CAAA,EACjCtD,EAAUjF,CAAAA,CAAK,CAAA,YAAA,EAAeuI,CAAQ,CAAA,WAAA,CAAA,CAAe,GAAG,CAAA,CACjD,IAAA,EAEFH,CAAAA,CAASG,CAAQ,CAC1B,CAMA,SAASC,CAAAA,CACPC,CAAAA,CACAC,EACsB,CACtB,GAAI,CAACA,CAAAA,CAAS,OACd,IAAMC,CAAAA,CAAWF,CAAAA,CAAIC,CAAO,EAC5B,GAAI,OAAOC,CAAAA,EAAa,QAAA,EAAY,CAACA,CAAAA,CAAU,OAC/C,IAAM7G,CAAAA,CAAW6G,EAAS,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAC7CC,CAAAA,CAAiB,EAAC,CACxB,QAASlI,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAIoB,CAAAA,CAAS,OAAQpB,CAAAA,EAAK,CAAA,CACxCkI,CAAAA,CAAK,IAAA,CAAK9G,EAASpB,CAAC,CAAE,CAAA,CAExB,OAAOkI,EAAK,MAAA,CAAS,CAAA,CAAIA,CAAAA,CAAO,MAClC,CAMA,eAAeC,CAAAA,CACbd,CAAAA,CACAE,EACyC,CACzC,IAAMa,CAAAA,CAAa,CAAA,EAAA,EAAKf,EAAM,WAAA,CAAY,MAAA,CAAO,CAAC,CAAA,CAAE,aAAa,CAAA,EAAGA,CAAAA,CAAM,WAAA,CAAY,MAAM,CAAC,CAAC,CAAA,CAAA,CACxFgB,CAAAA,CAAUhB,EAAM,IAAA,CAAK,GAAA,CAAYe,CAAU,CAAA,CAEjD,GAAI,OAAOC,CAAAA,EAAW,UAAA,CACpB,GAAI,CACF,IAAMN,CAAAA,CAAO,MAAMM,CAAAA,CAAOd,CAAK,CAAA,CAC/B,GAAIQ,CAAAA,CAAK,OAAOA,CAClB,CAAA,KAAQ,CAER,CAOF,OAAA,CAJgB,MAAMV,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,EAAA,CAAG,CACxC,KAAA,CAAO,CAAC,CAACA,CAAAA,CAAM,YAAa,IAAA,CAAME,CAAK,CAAC,CAAA,CACxC,MAAO,CACT,CAAC,CAAA,EACe,CAAC,GAAiC,IACpD,CAGA,eAAee,CAAAA,CAAWrJ,EAAUK,CAAAA,CAAyB,CAC3D,IAAMO,CAAAA,CAASZ,CAAAA,CAAI,MAAA,EAAU,EAAC,CACxBoI,EAAQO,CAAAA,CAAa/H,CAAAA,CAAO,QAAA,CAAUP,CAAG,EAC/C,GAAI,CAAC+H,CAAAA,CAAO,OAGZ,IAAIkB,CAAAA,CAA0D,EAAC,CAC3DC,CAAAA,CAEJ,GAAI,CACF,IAAMnC,CAAAA,CAAQpH,CAAAA,CAAI,OAAS,EAAC,CACtBwJ,CAAAA,CAAW,IAAA,CAAK,IACpB,MAAA,CAAOpC,CAAAA,CAAM,QAAQ,CAAA,EAAKgB,EAAM,QAAA,CAChC,GACF,CAAA,CACMC,CAAAA,CAASjB,EAAM,MAAA,CACfqC,CAAAA,CACHrC,CAAAA,CAAM,SAAA,EAAsB,aAAY,GAAM,MAAA,CAAS,MAAA,CAAS,MAAA,CAC7DsC,EAAUtC,CAAAA,CAAM,OAAA,CAChBuC,CAAAA,CACHvC,CAAAA,CAAM,UAAqB,WAAA,EAAY,GAAM,MAAA,CAAS,MAAA,CAAS,MAC5DwC,CAAAA,CAAYxC,CAAAA,CAAM,MAAA,CAClByC,CAAAA,CAASD,EACXA,CAAAA,CAAU,KAAA,CAAM,GAAG,CAAA,CAAE,IAAKE,CAAAA,EAAMA,CAAAA,CAAE,IAAA,EAAM,EACxC,KAAA,CAAA,CAGAC,CAAAA,CAGA3B,CAAAA,CAAM,eAAA,EAAmBhB,CAAAA,CAAM,QAAA,GAOjC2C,CAAAA,CAAAA,CALE,OAAO3C,EAAM,QAAA,EAAa,QAAA,CACtBA,CAAAA,CAAM,QAAA,CAAS,MAAM,GAAG,CAAA,CAAE,GAAA,CAAK0C,CAAAA,EAAcA,EAAE,IAAA,EAAM,CAAA,CACrD,KAAA,CAAM,QAAQ1C,CAAAA,CAAM,QAAQ,CAAA,CAC1BA,CAAAA,CAAM,SACN,EAAC,EACc,MAAA,CACpB4C,CAAAA,EACC,OAAOA,CAAAA,EAAQ,QAAA,EAAY5B,CAAAA,CAAM,eAAA,CAAiB,SAAS4B,CAAG,CAClE,CAAA,CACID,CAAAA,EAAU,SAAW,CAAA,GAAGA,CAAAA,CAAW,KAAA,CAAA,CAAA,CAAA,CAIzC,IAAMvH,EAAU2E,EAAAA,CAAaC,CAAAA,CAAOgB,CAAAA,CAAM,gBAAgB,EAC1DkB,CAAAA,CAAa9G,CAAAA,CAAQ,GAAA,CAAKI,CAAAA,GAAO,CAC/B,KAAA,CAAOA,CAAAA,CAAE,KAAA,CACT,EAAA,CAAIA,EAAE,EAAA,CACN,KAAA,CAAO,MAAA,CAAOA,CAAAA,CAAE,OAAS,EAAE,CAC7B,CAAA,CAAE,CAAA,CACE8G,IAASH,CAAAA,CAAU,CAAE,KAAA,CAAOG,CAAAA,CAAS,IAAKC,CAAS,CAAA,CAAA,CAGvD,IAAMM,CAAAA,CAAoB,CACxB,QAAA,CAAAT,CAAAA,CACA,SAAA,CAAAC,CACF,CAAA,CAEA,GAAIpB,CAAAA,CACF,GAAI,CACF,IAAM6B,CAAAA,CACJ,OAAO7B,CAAAA,EAAW,SAAW,IAAA,CAAK,KAAA,CAAMA,CAAM,CAAA,CAAIA,EACpD4B,CAAAA,CAAa,MAAA,CAAS,MAAM9B,EAAAA,CAAkBC,EAAO8B,CAAS,EAChE,CAAA,KAAQ,CAER,CAGER,CAAAA,GACFO,CAAAA,CAAa,OAAA,CAAU,CAAC,CAAE,KAAA,CAAOP,CAAAA,CAAS,SAAA,CAAWC,CAAS,CAAC,CAAA,CAAA,CAG7DnH,CAAAA,CAAQ,MAAA,CAAS,CAAA,GACnByH,EAAa,KAAA,CAAQzH,CAAAA,CAAQ,GAAA,CAAKI,CAAAA,EAAM,CAACA,CAAAA,CAAE,KAAA,CAAOA,CAAAA,CAAE,EAAA,CAAIA,EAAE,KAAK,CAAC,CAAA,CAAA,CAG9DiH,CAAAA,GACFI,EAAa,MAAA,CAASJ,CAAAA,CAAAA,CAGpBE,CAAAA,GACFE,CAAAA,CAAa,QAAUF,CAAAA,CAAAA,CAIzB,IAAMI,CAAAA,CAAS,MAAM/B,EAAM,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS6B,CAAY,EAErDG,EAAAA,CAAiC,CACrC,KAAA,CAAOD,CAAAA,CAAO,KACd,WAAA,CAAaA,CAAAA,CAAO,WAAA,CACpB,WAAA,CAAaA,EAAO,WAAA,CACpB,UAAA,CAAYlC,CAAAA,CAAgBkC,CAAAA,CAAO,UAAU,CAAA,CAC7C,UAAA,CAAYlC,CAAAA,CAAgBkC,CAAAA,CAAO,UAAU,CAC/C,CAAA,CAEA/E,CAAAA,CAAY/E,CAAAA,CAAK+J,GAAc,CAC7B,QAAA,CAAAZ,CAAAA,CACA,OAAA,CAASW,EAAO,WAClB,CAAC,EACH,CAAA,MAAS7J,EAAK,CACZkF,CAAAA,CACEnF,CAAAA,CACAC,CAAAA,CACA,CACE,GAAA,CAAK8H,CAAAA,CAAM,IAAA,CAAK,GAAA,CAChB,KAAMA,CAAAA,CAAM,IAAA,CACZ,OAAA,CAAS,CAAC,CAACA,CAAAA,CAAM,OAAA,CACjB,OAAA,CAASkB,CAAAA,CACT,KAAMC,CACR,CAAA,CACA,2BAAA,CACA7D,CACF,EACF,CACF,CAIA,eAAe2E,CAAAA,CAAYrK,EAAUK,CAAAA,CAAyB,CAC5D,IAAMO,CAAAA,CAASZ,EAAI,MAAA,EAAU,EAAC,CACxBoI,CAAAA,CAAQO,EAAa/H,CAAAA,CAAO,QAAA,CAAUP,CAAG,CAAA,CAC/C,GAAI,CAAC+H,CAAAA,CAAO,OAGZ,IAAIkB,CAAAA,CAA0D,EAAC,CAC3DC,CAAAA,CAEJ,GAAI,CACF,IAAMe,CAAAA,CAAyBtK,CAAAA,CAAI,MAAQ,EAAC,CACtCwJ,CAAAA,CAAW,IAAA,CAAK,IAAIc,CAAAA,CAAK,QAAA,EAAYlC,CAAAA,CAAM,QAAA,CAAU,GAAG,CAAA,CACxDqB,CAAAA,CAAYa,CAAAA,CAAK,SAAA,GAAc,OAAS,MAAA,CAAS,MAAA,CAGnDA,CAAAA,CAAK,KAAA,GACPhB,EAAagB,CAAAA,CAAK,KAAA,CAAM,GAAA,CAAKC,CAAAA,GAAO,CAClC,KAAA,CAAO,MAAA,CAAOA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAClB,EAAA,CAAIA,CAAAA,CAAE,CAAC,EACP,KAAA,CAAO,MAAA,CAAOA,CAAAA,CAAE,CAAC,GAAK,EAAE,CAC1B,CAAA,CAAE,CAAA,CAAA,CAEAD,EAAK,OAAA,EAAWA,CAAAA,CAAK,OAAA,CAAQ,CAAC,IAChCf,CAAAA,CAAU,CACR,KAAA,CAAOe,CAAAA,CAAK,QAAQ,CAAC,CAAA,CAAE,KAAA,CACvB,GAAA,CAAKA,EAAK,OAAA,CAAQ,CAAC,CAAA,CAAE,SAAA,GAAc,OAAS,MAAA,CAAS,KACvD,CAAA,CAAA,CAIF,IAAML,CAAAA,CAAoB,CACxB,QAAA,CAAAT,CAAAA,CACA,UAAAC,CACF,CAAA,CAGA,GAAIa,CAAAA,CAAK,OACP,GAAI,CACF,IAAMJ,CAAAA,CACJ,OAAOI,CAAAA,CAAK,MAAA,EAAW,QAAA,CACnB,IAAA,CAAK,MAAMA,CAAAA,CAAK,MAAM,CAAA,CACtBA,CAAAA,CAAK,OACXL,CAAAA,CAAa,MAAA,CAAS,MAAM9B,EAAAA,CAAkBC,EAAO8B,CAAS,EAChE,CAAA,KAAQ,CAER,CAIF,GAAI9B,CAAAA,CAAM,eAAA,EAAmBkC,CAAAA,CAAK,UAAYA,CAAAA,CAAK,QAAA,CAAS,MAAA,CAAS,CAAA,CAAG,CACtE,IAAME,CAAAA,CAAgBF,CAAAA,CAAK,QAAA,CAAS,OAAQN,CAAAA,EACtC,OAAOA,CAAAA,EAAQ,QAAA,CACV5B,EAAM,eAAA,CAAiB,QAAA,CAAS4B,CAAG,CAAA,CAG1C,OAAOA,CAAAA,EAAQ,QAAA,EACfA,CAAAA,GAAQ,IAAA,EACR,aAAcA,CAAAA,EACd,OAAOA,CAAAA,CAAI,QAAA,EAAa,SAEjB5B,CAAAA,CAAM,eAAA,CAAiB,QAAA,CAAS4B,CAAAA,CAAI,QAAQ,CAAA,CAE9C,CAAA,CACR,CAAA,CACGQ,CAAAA,CAAc,MAAA,CAAS,CAAA,GACzBP,CAAAA,CAAa,OAAA,CAAUO,GAE3B,CAGA,GAAIF,CAAAA,CAAK,KAAA,EAASA,EAAK,KAAA,CAAM,MAAA,CAAS,CAAA,CAAG,CAEvC,GAAIlC,CAAAA,CAAM,gBAAA,CAAkB,CAC1B,IAAMqC,EAAU,IAAI,GAAA,CAAIrC,CAAAA,CAAM,gBAAgB,EACxCsC,CAAAA,CAAUJ,CAAAA,CAAK,KAAA,CAAM,MAAA,CAAQC,GAAM,CAACE,CAAAA,CAAQ,GAAA,CAAIF,CAAAA,CAAE,CAAC,CAAC,CAAC,CAAA,CAC3D,GAAIG,EAAQ,MAAA,CAAS,CAAA,CAAG,CACtBpF,CAAAA,CACEjF,EACA,CAAA,uBAAA,EAA0BqK,CAAAA,CAAQ,GAAA,CAAKH,CAAAA,EAAMA,EAAE,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAC7D,GACF,CAAA,CACA,MACF,CACF,CACAN,CAAAA,CAAa,KAAA,CAAQK,EAAK,MAC5B,CAGA,GAAIA,CAAAA,CAAK,SAAWA,CAAAA,CAAK,OAAA,CAAQ,MAAA,CAAS,CAAA,CAAG,CAC3C,GAAIlC,CAAAA,CAAM,gBAAA,CAAkB,CAC1B,IAAMqC,CAAAA,CAAU,IAAI,GAAA,CAAIrC,EAAM,gBAAgB,CAAA,CACxCsC,CAAAA,CAAUJ,CAAAA,CAAK,QAAQ,MAAA,CAAQC,CAAAA,EAAM,CAACE,CAAAA,CAAQ,IAAIF,CAAAA,CAAE,CAAC,CAAC,CAAC,EAC7D,GAAIG,CAAAA,CAAQ,MAAA,CAAS,CAAA,CAAG,CACtBpF,CAAAA,CACEjF,CAAAA,CACA,CAAA,uBAAA,EAA0BqK,CAAAA,CAAQ,IAAKH,CAAAA,EAAMA,CAAAA,CAAE,CAAC,CAAC,EAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAC7D,GACF,CAAA,CACA,MACF,CACF,CACAN,EAAa,OAAA,CAAUK,CAAAA,CAAK,QAC9B,CAGA,GAAIA,CAAAA,CAAK,aAAA,EAAiBA,CAAAA,CAAK,aAAA,CAAc,OAAS,CAAA,CAAG,CACvD,GAAIlC,CAAAA,CAAM,iBAAkB,CAC1B,IAAMqC,CAAAA,CAAU,IAAI,IAAIrC,CAAAA,CAAM,gBAAgB,CAAA,CAC9C,IAAA,IAAWuC,KAASL,CAAAA,CAAK,aAAA,CAAe,CACtC,IAAMI,EAAUC,CAAAA,CAAM,MAAA,CAAQJ,CAAAA,EAAM,CAACE,CAAAA,CAAQ,GAAA,CAAIF,CAAAA,CAAE,CAAC,CAAC,CAAC,CAAA,CACtD,GAAIG,CAAAA,CAAQ,OAAS,CAAA,CAAG,CACtBpF,CAAAA,CACEjF,CAAAA,CACA,0BAA0BqK,CAAAA,CAAQ,GAAA,CAAKH,CAAAA,EAAMA,CAAAA,CAAE,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAC7D,GACF,CAAA,CACA,MACF,CACF,CACF,CACAN,CAAAA,CAAa,aAAA,CAAgBK,EAAK,cACpC,CAGIA,CAAAA,CAAK,OAAA,EAAWA,EAAK,OAAA,CAAQ,MAAA,CAAS,CAAA,GACxCL,CAAAA,CAAa,QAAUK,CAAAA,CAAK,OAAA,CAAA,CAI1BA,CAAAA,CAAK,MAAA,EAAUA,EAAK,MAAA,CAAO,MAAA,CAAS,CAAA,GACtCL,CAAAA,CAAa,OAASK,CAAAA,CAAK,MAAA,CAAA,CAI7B,IAAMH,CAAAA,CAAS,MAAM/B,CAAAA,CAAM,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS6B,CAAY,CAAA,CAErDG,CAAAA,CAAiC,CACrC,KAAA,CAAOD,EAAO,IAAA,CACd,WAAA,CAAaA,CAAAA,CAAO,WAAA,CACpB,YAAaA,CAAAA,CAAO,WAAA,CACpB,UAAA,CAAYlC,CAAAA,CAAgBkC,CAAAA,CAAO,UAAU,CAAA,CAC7C,UAAA,CAAYlC,EAAgBkC,CAAAA,CAAO,UAAU,CAC/C,CAAA,CAEA/E,EAAY/E,CAAAA,CAAK+J,CAAAA,CAAc,CAC7B,QAAA,CAAAZ,EACA,OAAA,CAASW,CAAAA,CAAO,WAClB,CAAC,EACH,CAAA,MAAS7J,CAAAA,CAAK,CACZkF,CAAAA,CACEnF,EACAC,CAAAA,CACA,CACE,GAAA,CAAK8H,CAAAA,CAAM,KAAK,GAAA,CAChB,IAAA,CAAMA,CAAAA,CAAM,IAAA,CACZ,QAAS,CAAC,CAACA,CAAAA,CAAM,OAAA,CACjB,QAASkB,CAAAA,CACT,IAAA,CAAMC,CACR,CAAA,CACA,4BACA7D,CACF,EACF,CACF,CAGA,eAAekF,CAAAA,CAAU5K,CAAAA,CAAUK,CAAAA,CAAyB,CAC1D,IAAMO,CAAAA,CAASZ,CAAAA,CAAI,MAAA,EAAU,GACvBoI,CAAAA,CAAQO,CAAAA,CAAa/H,CAAAA,CAAO,QAAA,CAAUP,CAAG,CAAA,CAC/C,GAAI,CAAC+H,CAAAA,CAAO,OAEZ,IAAMtC,CAAAA,CAAKlF,CAAAA,CAAO,EAAA,CAClB,GAAI,CAACkF,CAAAA,CAAI,CACPR,CAAAA,CAAUjF,CAAAA,CAAK,sBAAA,CAAwB,GAAG,CAAA,CAC1C,MACF,CAEA,GAAI,CACF,IAAMyI,EAAM,MAAMI,CAAAA,CAAad,CAAAA,CAAOtC,CAAE,EAExC,GAAI,CAACgD,CAAAA,CAAK,CACRxD,EAAUjF,CAAAA,CAAK,oBAAA,CAAsB,GAAG,CAAA,CACxC,MACF,CAEA+E,CAAAA,CAAY/E,CAAAA,CAAKyI,CAAG,EACtB,CAAA,MAASxI,CAAAA,CAAK,CACZkF,CAAAA,CACEnF,EACAC,CAAAA,CACA,CACE,GAAA,CAAK8H,CAAAA,CAAM,KAAK,GAAA,CAChB,IAAA,CAAMA,CAAAA,CAAM,IAAA,CACZ,QAAS,CAAC,CAACA,CAAAA,CAAM,OAAA,CACjB,QAAS,CAAC,CAAE,KAAA,CAAOA,CAAAA,CAAM,YAAa,EAAA,CAAI,IAAA,CAAM,KAAA,CAAOtC,CAAG,CAAC,CAC7D,CAAA,CACA,0BAAA,CACAJ,CACF,EACF,CACF,CAGA,eAAemF,CAAAA,CAAa7K,EAAUK,CAAAA,CAAyB,CAC7D,IAAMO,CAAAA,CAASZ,EAAI,MAAA,EAAU,EAAC,CACxBoI,CAAAA,CAAQO,EAAa/H,CAAAA,CAAO,QAAA,CAAUP,CAAG,CAAA,CAC/C,GAAK+H,CAAAA,CAEL,GAAI,CACF,IAAMkC,EAAOtK,CAAAA,CAAI,IAAA,EAAQ,EAAC,CAGpB8K,EAAahE,EAAAA,CACjBsB,CAAAA,CAAM,MAAA,CACNkC,CAAAA,CACAlC,EAAM,YAAA,CACN,CAAA,CAAA,CACAA,CAAAA,CAAM,UACR,EACA,GAAI,CAAC0C,CAAAA,CAAW,OAAA,CAAS,CACvBxF,CAAAA,CAAUjF,CAAAA,CAAKyK,CAAAA,CAAW,KAAA,CAAO,GAAG,CAAA,CACpC,MACF,CAGA,GAAI1C,EAAM,QAAA,CAAU,CAClB,IAAM2C,CAAAA,CAAc,MAAM3C,CAAAA,CAAM,QAAA,CAAS0C,CAAAA,CAAW,IAAA,CAAM,QAAQ,CAAA,CAClE,GAAIC,CAAAA,CAAa,CACfzF,EAAUjF,CAAAA,CAAK0K,CAAAA,CAAa,GAAG,CAAA,CAC/B,MACF,CACF,CAGA,IAAIC,CAAAA,CACJ,GAAI5C,CAAAA,CAAM,OAAA,EAAWA,CAAAA,CAAM,UAAA,EAAcA,EAAM,UAAA,CAAW,MAAA,CAAS,CAAA,CAAG,CAEpE,IAAMlD,CAAAA,CAA4B,CAAE,GAAG4F,CAAAA,CAAW,IAAK,CAAA,CAEnD1C,CAAAA,CAAM,UAAA,GACRlD,EAAKkD,CAAAA,CAAM,UAAU,CAAA,CAAI,IAAI,MAE/B,IAAM6C,CAAAA,CAAc7C,CAAAA,CAAM,UAAA,CAAW,OAAQ9B,CAAAA,EAAM,CAACpB,CAAAA,CAAKoB,CAAC,CAAC,CAAA,CAC3D,GAAI2E,CAAAA,CAAY,MAAA,CAAS,EAAG,CAC1B3F,CAAAA,CACEjF,CAAAA,CACA,CAAA,gDAAA,EAAmD4K,EAAY,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CACzE,GACF,CAAA,CACA,MACF,CACA,IAAMC,EAAY9C,CAAAA,CAAM,UAAA,CAAW,GAAA,CAAK9B,CAAAA,EAAMpB,EAAKoB,CAAC,CAAW,CAAA,CACzDgC,CAAAA,CACJpD,EAAKkD,CAAAA,CAAM,WAAW,CAAA,EAAKvC,EAAAA,GAC7BmF,CAAAA,CAAU,MAAM5C,CAAAA,CAAM,IAAA,CAAK,IAAI,GAAG8C,CAAAA,CAAW5C,CAAAA,CAAOpD,CAAI,EAC1D,CAAA,KACE8F,CAAAA,CAAU,MAAM5C,CAAAA,CAAM,KAAK,MAAA,CAAO0C,CAAAA,CAAW,IAAW,CAAA,CAG1D1F,EAAY/E,CAAAA,CAAK2K,CAAAA,CAAS,KAAA,CAAA,CAAW,GAAG,EAC1C,CAAA,MAAS1K,CAAAA,CAAK,CACZ,IAAMqD,CAAAA,CACJ+B,CAAAA,EAAWpF,CAAAA,YAAe,KAAA,CACtBA,EAAI,OAAA,CACJ,2BAAA,CACNgF,CAAAA,CAAUjF,CAAAA,CAAKsD,EAAS,GAAG,EAC7B,CACF,CAGA,eAAewH,CAAAA,CACbnL,CAAAA,CACAK,CAAAA,CACA0G,CAAAA,CACe,CACf,IAAMnG,CAAAA,CAASZ,CAAAA,CAAI,MAAA,EAAU,EAAC,CACxBoI,CAAAA,CAAQO,CAAAA,CAAa/H,CAAAA,CAAO,SAAUP,CAAG,CAAA,CAC/C,GAAI,CAAC+H,EAAO,OAEZ,IAAMtC,CAAAA,CAAKlF,CAAAA,CAAO,GAClB,GAAI,CAACkF,CAAAA,CAAI,CACPR,EAAUjF,CAAAA,CAAK,sBAAA,CAAwB,GAAG,CAAA,CAC1C,MACF,CAEA,GAAI,CACF,IAAMiK,EAAOtK,CAAAA,CAAI,IAAA,EAAQ,EAAC,CAGpB8K,EAAahE,EAAAA,CACjBsB,CAAAA,CAAM,MAAA,CACNkC,CAAAA,CACAlC,EAAM,aAAA,CACNrB,CAAAA,CACAqB,CAAAA,CAAM,UACR,EACA,GAAI,CAAC0C,CAAAA,CAAW,OAAA,CAAS,CACvBxF,CAAAA,CAAUjF,CAAAA,CAAKyK,CAAAA,CAAW,MAAO,GAAG,CAAA,CACpC,MACF,CAGA,GAAI1C,CAAAA,CAAM,QAAA,CAAU,CAClB,IAAM2C,EAAc,MAAM3C,CAAAA,CAAM,QAAA,CAAS0C,CAAAA,CAAW,KAAM,QAAQ,CAAA,CAClE,GAAIC,CAAAA,CAAa,CACfzF,CAAAA,CAAUjF,CAAAA,CAAK0K,CAAAA,CAAa,GAAG,EAC/B,MACF,CACF,CAGA,IAAMK,EAAc,MAAMlC,CAAAA,CAAad,CAAAA,CAAOtC,CAAE,EAC1CuF,CAAAA,CAAAA,CACHD,CAAAA,EAAevC,CAAAA,CAAgBuC,CAAAA,CAAahD,EAAM,OAAO,CAAA,GAAM,CAACtC,CAAE,EAC/DwF,CAAAA,CAAU,MAAMlD,CAAAA,CAAM,IAAA,CAAK,OAC/B,GAAGiD,CAAAA,CACHP,CAAAA,CAAW,IACb,EAEA1F,CAAAA,CAAY/E,CAAAA,CAAKiL,CAAO,EAC1B,OAAShL,CAAAA,CAAK,CACZ,IAAMqD,CAAAA,CACJ+B,GAAWpF,CAAAA,YAAe,KAAA,CACtBA,CAAAA,CAAI,OAAA,CACJ,4BACNgF,CAAAA,CAAUjF,CAAAA,CAAKsD,CAAAA,CAAS,GAAG,EAC7B,CACF,CAGA,eAAe4H,EAAavL,CAAAA,CAAUK,CAAAA,CAAyB,CAC7D,IAAMO,EAASZ,CAAAA,CAAI,MAAA,EAAU,EAAC,CACxBoI,EAAQO,CAAAA,CAAa/H,CAAAA,CAAO,QAAA,CAAUP,CAAG,EAC/C,GAAI,CAAC+H,CAAAA,CAAO,OAEZ,GAAI,CAACA,CAAAA,CAAM,WAAA,CAAa,CACtB9C,EAAUjF,CAAAA,CAAK,wCAAA,CAA0C,GAAG,CAAA,CAC5D,MACF,CAEA,IAAMyF,CAAAA,CAAKlF,CAAAA,CAAO,GAClB,GAAI,CAACkF,CAAAA,CAAI,CACPR,EAAUjF,CAAAA,CAAK,sBAAA,CAAwB,GAAG,CAAA,CAC1C,MACF,CAEA,GAAI,CAEF,IAAMyI,EAAM,MAAMI,CAAAA,CAAad,CAAAA,CAAOtC,CAAE,EAClCuF,CAAAA,CAAAA,CACHvC,CAAAA,EAAOD,CAAAA,CAAgBC,CAAAA,CAAKV,EAAM,OAAO,CAAA,GAAM,CAACtC,CAAE,EACrD,MAAMsC,CAAAA,CAAM,IAAA,CAAK,MAAA,CAAO,GAAGiD,CAAQ,CAAA,CACnCjG,CAAAA,CAAY/E,CAAAA,CAAK,CAAE,OAAA,CAAS,CAAA,CAAK,CAAC,EACpC,CAAA,MAASC,CAAAA,CAAK,CACZ,IAAMqD,EACJ+B,CAAAA,EAAWpF,CAAAA,YAAe,KAAA,CACtBA,CAAAA,CAAI,QACJ,2BAAA,CACNgF,CAAAA,CAAUjF,CAAAA,CAAKsD,CAAAA,CAAS,GAAG,EAC7B,CACF,CAGA,SAAS6H,EAAcxL,CAAAA,CAAUK,CAAAA,CAAgB,CAC/CA,CAAAA,CACG,OAAO,GAAG,CAAA,CACV,GAAA,CACC,8BAAA,CACA,wCACF,CAAA,CACC,GAAA,CAAI,8BAAA,CAAgC,6BAA6B,EACjE,GAAA,CAAI,wBAAA,CAA0B,OAAO,CAAA,CACrC,KAAK,EAAE,EACZ,CAEA,OAAO,CACL,UAAA,CAAAgJ,CAAAA,CACA,WAAA,CAAAgB,CAAAA,CACA,UAAAO,CAAAA,CACA,YAAA,CAAAC,CAAAA,CACA,YAAA,CAAAM,EACA,YAAA,CAAAI,CAAAA,CACA,aAAA,CAAAC,CACF,CACF,CCp2BA,SAASC,CAAAA,CAAgBzF,CAAAA,CAA4C,CACnE,GAAI,CACF,OAAOG,KAAAA,CAAE,YAAA,CAAaH,CAAAA,CAAQ,CAC5B,MAAA,CAAQ,aAAA,CACR,gBAAiB,KAAA,CACjB,QAAA,CAAWnB,CAAAA,EAAQ,CACjB,IAAMoB,CAAAA,CAAYpB,CAAAA,CAAI,SAAA,EAAmB,IAAA,EAAM,IAC1CoB,CAAAA,GACDA,CAAAA,CAAI,IAAA,GAAS,MAAA,EACdpB,EAAI,UAAA,CAAmB,IAAA,CAAO,QAAA,CAC9BA,CAAAA,CAAI,WAAmB,MAAA,CAAS,WAAA,EACxBoB,CAAAA,CAAI,IAAA,GAAS,WACrBpB,CAAAA,CAAI,UAAA,CAAmB,IAAA,CAAO,QAAA,CAC9BA,EAAI,UAAA,CAAmB,MAAA,CAAS,OAAA,CAAA,EAErC,CACF,CAAC,CACH,CAAA,MAASvE,CAAAA,CAAK,CACZ,OAAI,OAAO,OAAA,CAAY,GAAA,EAAe,OAAA,CAAQ,MAC5C,OAAA,CAAQ,IAAA,CACN,mGAAA,CACAA,CACF,EAEK,CAAE,IAAA,CAAM,QAAS,CAC1B,CACF,CAGA,SAASoL,CAAAA,CAAU5L,CAAAA,CAAuC,CACxD,OAAO,CAAE,IAAA,CAAM,CAAA,qBAAA,EAAwBA,CAAI,CAAA,CAAG,CAChD,CAGA,SAAS6L,EAAcC,CAAAA,CAA8C,CACnE,OAAO,CACL,WAAA,CAAAA,CAAAA,CACA,OAAA,CAAS,CACP,mBAAoB,CAClB,MAAA,CAAQF,CAAAA,CAAU,eAAe,CACnC,CACF,CACF,CACF,CAGA,SAASG,CAAAA,CACPD,CAAAA,CACAE,CAAAA,CACyB,CACzB,OAAO,CACL,WAAA,CAAAF,CAAAA,CACA,OAAA,CAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQ,CACN,KAAM,QAAA,CACN,UAAA,CAAY,CACV,OAAA,CAAS,CAAE,IAAA,CAAM,SAAA,CAAW,IAAA,CAAM,CAAC,IAAI,CAAE,CAAA,CACzC,IAAA,CAAME,CACR,EACA,QAAA,CAAU,CAAC,SAAA,CAAW,MAAM,CAC9B,CACF,CACF,CACF,CACF,CAGA,SAASC,EAAAA,CACPC,CAAAA,CACyB,CACzB,OAAO,CACL,WAAA,CAAa,6BAAA,CACb,OAAA,CAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQ,CACN,KAAM,QAAA,CACN,UAAA,CAAY,CACV,OAAA,CAAS,CAAE,IAAA,CAAM,SAAA,CAAW,IAAA,CAAM,CAAC,IAAI,CAAE,CAAA,CACzC,IAAA,CAAM,CACJ,KAAM,QAAA,CACN,UAAA,CAAY,CACV,KAAA,CAAO,CAAE,IAAA,CAAM,OAAA,CAAS,KAAA,CAAOA,CAAW,EAC1C,UAAA,CAAY,CACV,KAAA,CAAO,CAAC,CAAE,IAAA,CAAM,QAAS,CAAA,CAAG,CAAE,KAAM,MAAO,CAAC,CAC9C,CAAA,CACA,WAAY,CACV,KAAA,CAAO,CAAC,CAAE,KAAM,QAAS,CAAA,CAAG,CAAE,IAAA,CAAM,MAAO,CAAC,CAC9C,CAAA,CACA,WAAA,CAAa,CAAE,IAAA,CAAM,SAAU,CAAA,CAC/B,WAAA,CAAa,CAAE,IAAA,CAAM,SAAU,CACjC,CAAA,CACA,SAAU,CAAC,OAAA,CAAS,aAAA,CAAe,aAAa,CAClD,CAAA,CACA,IAAA,CAAM,CACJ,IAAA,CAAM,SACN,UAAA,CAAY,CACV,QAAA,CAAU,CAAE,KAAM,SAAU,CAAA,CAC5B,OAAA,CAAS,CAAE,KAAM,SAAU,CAAA,CAC3B,MAAA,CAAQ,CACN,KAAA,CAAO,CAAC,CAAE,IAAA,CAAM,QAAS,CAAA,CAAG,CAAE,IAAA,CAAM,MAAO,CAAC,CAC9C,CACF,CACF,CACF,EACA,QAAA,CAAU,CAAC,SAAA,CAAW,MAAM,CAC9B,CACF,CACF,CACF,CACF,CAMA,SAASC,EAAAA,CAAiB7D,CAAAA,CAAiD,CACzE,OAAO,CACL,CACE,IAAA,CAAM,UAAA,CACN,GAAI,OAAA,CACJ,MAAA,CAAQ,CAAE,IAAA,CAAM,UAAW,OAAA,CAASA,CAAAA,CAAM,QAAA,CAAU,OAAA,CAAS,GAAI,CAAA,CACjE,WAAA,CAAa,0BACf,CAAA,CACA,CACE,IAAA,CAAM,QAAA,CACN,EAAA,CAAI,OAAA,CACJ,OAAQ,CAAE,IAAA,CAAM,QAAS,CAAA,CACzB,YAAa,0BACf,CAAA,CACA,CACE,IAAA,CAAM,UACN,EAAA,CAAI,OAAA,CACJ,MAAA,CAAQ,CAAE,KAAM,QAAS,CAAA,CACzB,WAAA,CAAa,wBACf,EACA,CACE,IAAA,CAAM,UAAA,CACN,EAAA,CAAI,OAAA,CACJ,MAAA,CAAQ,CAAE,IAAA,CAAM,SAAU,IAAA,CAAM,CAAC,KAAA,CAAO,MAAM,CAAE,CAAA,CAChD,WAAA,CAAa,iBACf,CAAA,CACA,CACE,IAAA,CAAM,QAAA,CACN,EAAA,CAAI,OAAA,CACJ,OAAQ,CAAE,IAAA,CAAM,QAAS,CAAA,CACzB,YAAa,0CACf,CACF,CACF,CAEA,SAAS8D,EAAAA,CAAa9D,CAAAA,CAAiD,CACrE,IAAM1F,EAAS0F,CAAAA,CAAM,gBAAA,EAAoB,MAAA,CAAO,IAAA,CAAKA,EAAM,MAAA,CAAO,KAAK,CAAA,CACjE+D,CAAAA,CAAM,CAAC,IAAA,CAAM,IAAA,CAAM,IAAA,CAAM,KAAA,CAAO,KAAM,KAAA,CAAO,IAAA,CAAM,KAAA,CAAO,UAAU,EAEpEvL,CAAAA,CAAoC,EAAC,CAC3C,IAAA,IAAWyD,KAAS3B,CAAAA,CAAQ,CAE1B9B,CAAAA,CAAO,IAAA,CAAK,CACV,IAAA,CAAMyD,CAAAA,CACN,EAAA,CAAI,OAAA,CACJ,OAAQ,CAAE,IAAA,CAAM,QAAS,CAAA,CACzB,YAAa,CAAA,UAAA,EAAaA,CAAK,CAAA,WAAA,CACjC,CAAC,CAAA,CAED,IAAA,IAAWuD,CAAAA,IAAMuE,CAAAA,CACfvL,EAAO,IAAA,CAAK,CACV,IAAA,CAAM,CAAA,EAAGyD,CAAK,CAAA,EAAA,EAAKuD,CAAE,CAAA,CAAA,CACrB,EAAA,CAAI,QACJ,MAAA,CAAQ,CAAE,IAAA,CAAM,QAAS,EACzB,WAAA,CAAa,CAAA,OAAA,EAAUvD,CAAK,CAAA,eAAA,EAAkBuD,CAAE,CAAA,CAClD,CAAC,EAEL,CACA,OAAOhH,CACT,CAMA,SAASwL,EAAAA,EAA2C,CAClD,OAAO,CACL,IAAA,CAAM,QAAA,CACN,WAAY,CACV,KAAA,CAAO,CACL,IAAA,CAAM,QACN,KAAA,CAAO,CACL,IAAA,CAAM,OAAA,CACN,MAAO,EAAC,CACR,QAAA,CAAU,CAAA,CACV,SAAU,CACZ,CAAA,CACA,WAAA,CAAa,4CACf,EACA,OAAA,CAAS,CACP,IAAA,CAAM,OAAA,CACN,MAAO,CACL,IAAA,CAAM,OAAA,CACN,KAAA,CAAO,EAAC,CACR,QAAA,CAAU,CAAA,CACV,QAAA,CAAU,CACZ,CAAA,CACA,WAAA,CAAa,gDACf,CAAA,CACA,aAAA,CAAe,CACb,IAAA,CAAM,OAAA,CACN,MAAO,CACL,IAAA,CAAM,OAAA,CACN,KAAA,CAAO,CACL,IAAA,CAAM,OAAA,CACN,KAAA,CAAO,GACP,QAAA,CAAU,CAAA,CACV,QAAA,CAAU,CACZ,CACF,CAAA,CACA,WAAA,CAAa,mDACf,CAAA,CACA,QAAS,CACP,IAAA,CAAM,OAAA,CACN,KAAA,CAAO,CACL,IAAA,CAAM,QAAA,CACN,UAAA,CAAY,CACV,MAAO,CAAE,IAAA,CAAM,QAAS,CAAA,CACxB,UAAW,CAAE,IAAA,CAAM,QAAA,CAAU,IAAA,CAAM,CAAC,KAAA,CAAO,MAAM,CAAE,CACrD,EACA,QAAA,CAAU,CAAC,OAAO,CACpB,CACF,CAAA,CACA,MAAA,CAAQ,CACN,IAAA,CAAM,QACN,KAAA,CAAO,CAAE,IAAA,CAAM,QAAS,EACxB,WAAA,CAAa,+BACf,CAAA,CACA,QAAA,CAAU,CACR,IAAA,CAAM,SAAA,CACN,OAAA,CAAS,GAAA,CACT,YAAa,0BACf,CAAA,CACA,MAAA,CAAQ,CACN,KAAA,CAAO,CAAC,CAAE,IAAA,CAAM,QAAS,CAAA,CAAG,CAAE,IAAA,CAAM,QAAS,CAAC,CAAA,CAC9C,WAAA,CAAa,mBACf,CAAA,CACA,UAAW,CACT,IAAA,CAAM,QAAA,CACN,IAAA,CAAM,CAAC,MAAA,CAAQ,MAAM,CAAA,CACrB,WAAA,CAAa,sBACf,CAAA,CACA,QAAA,CAAU,CACR,IAAA,CAAM,QACN,KAAA,CAAO,CACL,KAAA,CAAO,CACL,CAAE,IAAA,CAAM,QAAS,CAAA,CACjB,CACE,KAAM,QAAA,CACN,UAAA,CAAY,CACV,QAAA,CAAU,CAAE,IAAA,CAAM,QAAS,CAAA,CAC3B,MAAA,CAAQ,CAAE,IAAA,CAAM,OAAA,CAAS,KAAA,CAAO,CAAE,KAAM,QAAS,CAAE,CACrD,CAAA,CACA,SAAU,CAAC,UAAU,CACvB,CACF,CACF,CAAA,CACA,WAAA,CAAa,iCACf,CACF,CACF,CACF,CAMA,SAASC,EAAAA,CACPjE,EACAkE,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACkD,CAClD,IAAMC,CAAAA,CAA0D,GAC1DC,CAAAA,CAAMvE,CAAAA,CAAM,IAAA,CACZwE,CAAAA,CAAiB,GAAGN,CAAI,CAAA,CAAA,EAAIlE,CAAAA,CAAM,IAAI,GACtCyE,CAAAA,CAAe,CAAA,EAAGD,CAAc,CAAA,EAAA,EAAKxE,EAAM,WAAW,CAAA,CAAA,CAAA,CAEtD0E,CAAAA,CAAU,CACd,KAAM1E,CAAAA,CAAM,WAAA,CACZ,EAAA,CAAI,MAAA,CACJ,SAAU,IAAA,CACV,MAAA,CAAQ,CAAE,IAAA,CAAM,QAAS,CAAA,CACzB,WAAA,CAAa,4BACf,CAAA,CAGAsE,EAAME,CAAc,CAAA,CAAI,CACtB,GAAA,CAAK,CACH,WAAA,CAAa,CAAA,IAAA,EAAOG,CAAAA,CAAW3E,CAAAA,CAAM,IAAI,CAAC,CAAA,CAAA,CAC1C,OAAA,CAAS,CAAA,KAAA,EAAQA,EAAM,IAAI,CAAA,YAAA,CAAA,CAC3B,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,UAAA,CAAY,CAAC,GAAGV,GAAiB7D,CAAK,CAAA,CAAG,GAAG8D,EAAAA,CAAa9D,CAAK,CAAC,CAAA,CAC/D,SAAA,CAAW,CACT,IAAO2D,EAAAA,CAAaL,CAAAA,CAAUa,CAAe,CAAC,CAAA,CAC9C,GAAA,CAAOZ,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CAAA,CAEA,IAAA,CAAM,CACJ,YAAa,CAAA,MAAA,EAASoB,CAAAA,CAAW3E,CAAAA,CAAM,IAAI,CAAC,CAAA,CAAA,CAC5C,OAAA,CAAS,CAAA,SAAA,EAAY4E,CAAAA,CAAY5E,EAAM,IAAI,CAAC,CAAA,CAAA,CAC5C,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,WAAA,CAAa,CACX,SAAU,IAAA,CACV,OAAA,CAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQjB,CAAAA,CAAUc,CAAAA,EAAoBD,CAAe,CACvD,CACF,CACF,CAAA,CACA,SAAA,CAAW,CACT,GAAA,CAAOV,CAAAA,CAAgB,kBAAA,CAAoBH,CAAAA,CAAUa,CAAe,CAAC,CAAA,CACrE,GAAA,CAAOZ,CAAAA,CAAc,kBAAkB,CAAA,CACvC,GAAA,CAAOA,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CACF,CAAA,CAGAe,CAAAA,CAAM,GAAGE,CAAc,CAAA,MAAA,CAAQ,CAAA,CAAI,CACjC,KAAM,CACJ,WAAA,CAAa,CAAA,KAAA,EAAQG,CAAAA,CAAW3E,EAAM,IAAI,CAAC,CAAA,CAAA,CAC3C,OAAA,CAAS,SAASA,CAAAA,CAAM,IAAI,CAAA,sBAAA,CAAA,CAC5B,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,WAAA,CAAa,CACX,SAAU,IAAA,CACV,OAAA,CAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQjB,CAAAA,CAAU,kBAAkB,CACtC,CACF,CACF,CAAA,CACA,SAAA,CAAW,CACT,IAAOK,EAAAA,CAAaL,CAAAA,CAAUa,CAAe,CAAC,EAC9C,GAAA,CAAOZ,CAAAA,CAAc,eAAe,CAAA,CACpC,IAAOA,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CACF,CAAA,CAGA,IAAMsB,CAAAA,CAA2C,GAGjD,OAAAA,CAAAA,CAAO,GAAA,CAAM,CACX,YAAa,CAAA,GAAA,EAAMF,CAAAA,CAAWC,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAC,CAAA,CAAA,CACtD,OAAA,CAAS,gBAAgB4E,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,GAChD,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,WAAY,CAACG,CAAO,CAAA,CACpB,SAAA,CAAW,CACT,GAAA,CAAOjB,CAAAA,CAAgB,gBAAA,CAAkBH,CAAAA,CAAUa,CAAe,CAAC,CAAA,CACnE,GAAA,CAAOZ,EAAc,oBAAoB,CAAA,CACzC,GAAA,CAAOA,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CAAA,CAGAsB,CAAAA,CAAO,IAAM,CACX,WAAA,CAAa,CAAA,MAAA,EAASF,CAAAA,CAAWC,EAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAC,GACzD,OAAA,CAAS,CAAA,SAAA,EAAY4E,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAA,eAAA,CAAA,CAC5C,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,UAAA,CAAY,CAACG,CAAO,EACpB,WAAA,CAAa,CACX,QAAA,CAAU,IAAA,CACV,QAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQpB,EAAUe,CAAAA,EAAoBF,CAAe,CACvD,CACF,CACF,CAAA,CACA,SAAA,CAAW,CACT,GAAA,CAAOV,EAAgB,kBAAA,CAAoBH,CAAAA,CAAUa,CAAe,CAAC,EACrE,GAAA,CAAOZ,CAAAA,CAAc,kBAAkB,CAAA,CACvC,IAAOA,CAAAA,CAAc,oBAAoB,CAAA,CACzC,GAAA,CAAOA,EAAc,uBAAuB,CAC9C,CACF,CAAA,CAGAsB,CAAAA,CAAO,KAAA,CAAQ,CACb,WAAA,CAAa,QAAQF,CAAAA,CAAWC,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAC,CAAA,CAAA,CACxD,OAAA,CAAS,CAAA,mBAAA,EAAsB4E,EAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAA,CAAA,CACtD,KAAM,CAACuE,CAAG,CAAA,CACV,UAAA,CAAY,CAACG,CAAO,CAAA,CACpB,WAAA,CAAa,CACX,SAAU,IAAA,CACV,OAAA,CAAS,CACP,kBAAA,CAAoB,CAClB,MAAA,CAAQ,CACN,KAAA,CAAO,CAACpB,EAAUe,CAAAA,EAAoBF,CAAe,CAAC,CAAA,CACtD,YAAa,6CACf,CACF,CACF,CACF,EACA,SAAA,CAAW,CACT,GAAA,CAAOV,CAAAA,CAAgB,mBAAoBH,CAAAA,CAAUa,CAAe,CAAC,CAAA,CACrE,IAAOZ,CAAAA,CAAc,kBAAkB,CAAA,CACvC,GAAA,CAAOA,EAAc,oBAAoB,CAAA,CACzC,GAAA,CAAOA,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CAAA,CAGIvD,CAAAA,CAAM,cACR6E,CAAAA,CAAO,MAAA,CAAS,CACd,WAAA,CAAa,CAAA,MAAA,EAASF,CAAAA,CAAWC,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,CAAC,CAAA,CAAA,CACzD,OAAA,CAAS,YAAY4E,CAAAA,CAAY5E,CAAAA,CAAM,IAAI,CAAC,GAC5C,IAAA,CAAM,CAACuE,CAAG,CAAA,CACV,WAAY,CAACG,CAAO,CAAA,CACpB,SAAA,CAAW,CACT,GAAA,CAAOjB,CAAAA,CAAgB,kBAAA,CAAoB,CACzC,KAAM,QAAA,CACN,UAAA,CAAY,CAAE,EAAA,CAAI,CAAE,IAAA,CAAM,QAAS,CAAE,CACvC,CAAC,CAAA,CACD,GAAA,CAAOF,CAAAA,CAAc,oBAAoB,EACzC,GAAA,CAAOA,CAAAA,CAAc,uBAAuB,CAC9C,CACF,CAAA,CAAA,CAGFe,CAAAA,CAAMG,CAAY,CAAA,CAAII,EAEfP,CACT,CA4BO,SAASQ,EAAAA,CACdzE,EACAC,CAAAA,CACAyE,CAAAA,CAA8B,EAAC,CACd,CACjB,GAAM,CACJ,KAAA,CAAAC,CAAAA,CAAQ,WACR,OAAA,CAAAC,CAAAA,CAAU,OAAA,CACV,WAAA,CAAAzB,EACA,OAAA,CAAA0B,CAAAA,CACA,IAAA,CAAAC,CAAAA,CAAO,KACT,CAAA,CAAIJ,CAAAA,CAEEb,CAAAA,CAAO5D,CAAAA,GAAa,IAAM,EAAA,CAAKA,CAAAA,CAAS,OAAA,CAAQ,KAAA,CAAO,EAAE,CAAA,CAGzD8E,CAAAA,CAAmD,EAAC,CACpDC,EAA6D,EAAC,CAC9DC,CAAAA,CAAiD,GAGvDF,CAAAA,CAAQ,aAAA,CAAmB,CACzB,IAAA,CAAM,SACN,UAAA,CAAY,CACV,OAAA,CAAS,CAAE,KAAM,SAAA,CAAW,IAAA,CAAM,CAAC,KAAK,CAAE,CAAA,CAC1C,KAAA,CAAO,CAAE,IAAA,CAAM,QAAS,CAC1B,CAAA,CACA,QAAA,CAAU,CAAC,UAAW,OAAO,CAC/B,CAAA,CAEAA,CAAAA,CAAQ,iBAAsBpB,EAAAA,EAAgB,CAG9C,IAAA,GAAW,CAACtM,EAAMsI,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQK,CAAQ,CAAA,CAAG,CACpD,IAAMkF,CAAAA,CAAYZ,EAAWC,CAAAA,CAAYlN,CAAI,CAAC,CAAA,CACxC8N,EAAa,CAAA,EAAGD,CAAS,CAAA,MAAA,CAAA,CACzBE,CAAAA,CAAa,GAAGF,CAAS,CAAA,MAAA,CAAA,CAG/BH,CAAAA,CAAQG,CAAS,CAAA,CAAIlC,CAAAA,CAAgBrD,CAAAA,CAAM,MAAM,EAGjD,IAAM0F,CAAAA,CACJC,CAAAA,EAC8B,CAC9B,IAAMnH,CAAAA,CACJmH,CAAAA,EAAaA,CAAAA,CAAU,MAAA,CAAS,EAC5BA,CAAAA,CACA,MAAA,CAAO,IAAA,CAAK3F,CAAAA,CAAM,OAAO,KAAK,CAAA,CAC9BhC,CAAAA,CAAmC,GACzC,IAAA,IAAWxD,CAAAA,IAAKgE,CAAAA,CAAQ,CACtB,IAAMoH,CAAAA,CAAMpL,CAAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAC,CAAA,CACtBoL,CAAAA,EAAO5F,CAAAA,CAAM,OAAO,KAAA,CAAM4F,CAAG,CAAA,EAAK,CAAC5F,EAAM,UAAA,CAAW,QAAA,CAAS4F,CAAG,CAAA,GAClE5H,EAAM4H,CAAG,CAAA,CAAI5F,CAAAA,CAAM,MAAA,CAAO,MAAM4F,CAAG,CAAA,EAEvC,CACA,OAAO5H,CACT,CAAA,CAGIoG,CAAAA,CAAkC,IAAA,CAChCyB,CAAAA,CAAcH,EAAW1F,CAAAA,CAAM,YAAY,CAAA,CAC7C,MAAA,CAAO,KAAK6F,CAAW,CAAA,CAAE,MAAA,CAAS,CAAA,GACpCT,EAAQI,CAAU,CAAA,CAAInC,CAAAA,CAAgBtF,KAAAA,CAAE,MAAA,CAAO8H,CAAW,CAAC,CAAA,CAC3DzB,EAAmBoB,CAAAA,CAAAA,CAIrB,IAAInB,CAAAA,CAAkC,IAAA,CAChCyB,EAAcJ,CAAAA,CAAW1F,CAAAA,CAAM,aAAa,CAAA,CAC9C,OAAO,IAAA,CAAK8F,CAAW,CAAA,CAAE,MAAA,CAAS,IACpCV,CAAAA,CAAQK,CAAU,CAAA,CAAIpC,CAAAA,CAAgBtF,MAAE,MAAA,CAAO+H,CAAW,CAAC,CAAA,CAC3DzB,EAAmBoB,CAAAA,CAAAA,CAIrB,IAAMM,CAAAA,CAAa9B,EAAAA,CACjBjE,EACAkE,CAAAA,CACAqB,CAAAA,CACAnB,CAAAA,CACAC,CACF,EACA,MAAA,CAAO,MAAA,CAAOgB,CAAAA,CAAUU,CAAU,EAGlCT,CAAAA,CAAK,IAAA,CAAK,CACR,IAAA,CAAA5N,EACA,WAAA,CAAa,CAAA,cAAA,EAAiBA,CAAI,CAAA,cAAA,EAAiBsI,EAAM,IAAI,CAAA,CAAA,CAC/D,CAAC,EACH,CAGA,IAAMgG,CAAAA,CAA2D,EAAC,CAC9DC,EAEJ,OAAId,CAAAA,GAAS,OAAA,EACXa,CAAAA,CAAgB,UAAe,CAC7B,IAAA,CAAM,MAAA,CACN,MAAA,CAAQ,OACV,CAAA,CACAC,CAAAA,CAAW,CAAC,CAAE,SAAA,CAAW,EAAG,CAAC,GACpBd,CAAAA,GAAS,QAAA,GAClBa,CAAAA,CAAgB,UAAA,CAAgB,CAC9B,IAAA,CAAM,MAAA,CACN,MAAA,CAAQ,QAAA,CACR,aAAc,KAChB,CAAA,CACAC,CAAAA,CAAW,CAAC,CAAE,UAAA,CAAY,EAAG,CAAC,GAIH,CAC3B,OAAA,CAAS,OAAA,CACT,IAAA,CAAM,CACJ,KAAA,CAAAjB,CAAAA,CACA,OAAA,CAAAC,CAAAA,CACA,GAAIzB,CAAAA,CAAc,CAAE,WAAA,CAAAA,CAAY,EAAI,EACtC,CAAA,CACA,GAAI0B,GAAWA,CAAAA,CAAQ,MAAA,CAAS,CAAA,CAAI,CAAE,QAAAA,CAAQ,CAAA,CAAI,EAAC,CACnD,MAAOG,CAAAA,CACP,UAAA,CAAY,CACV,OAAA,CAAAD,EACA,GAAI,MAAA,CAAO,IAAA,CAAKY,CAAe,EAAE,MAAA,CAAS,CAAA,CAAI,CAAE,eAAA,CAAAA,CAAgB,CAAA,CAAI,EACtE,CAAA,CACA,GAAIC,CAAAA,CAAW,CAAE,QAAA,CAAAA,CAAS,EAAI,EAAC,CAC/B,IAAA,CAAAX,CACF,CAGF,CAMA,SAASX,CAAAA,CAAWjD,CAAAA,CAAmB,CACrC,OAAOA,CAAAA,CAAE,MAAA,CAAO,CAAC,EAAE,WAAA,EAAY,CAAIA,CAAAA,CAAE,KAAA,CAAM,CAAC,CAC9C,CAGA,SAASkD,CAAAA,CAAYlD,EAAmB,CACtC,OAAIA,CAAAA,CAAE,QAAA,CAAS,KAAK,CAAA,CAAUA,CAAAA,CAAE,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CAAI,GAAA,CAC3CA,CAAAA,CAAE,QAAA,CAAS,KAAK,CAAA,EAAKA,CAAAA,CAAE,QAAA,CAAS,KAAK,GAAKA,CAAAA,CAAE,QAAA,CAAS,KAAK,CAAA,CACrDA,EAAE,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CAClBA,EAAE,QAAA,CAAS,GAAG,CAAA,EAAK,CAACA,EAAE,QAAA,CAAS,IAAI,CAAA,CAAUA,CAAAA,CAAE,MAAM,CAAA,CAAG,EAAE,CAAA,CACvDA,CACT,CCjkBA,SAASwE,EAAAA,CAAelB,CAAAA,CAAemB,CAAAA,CAAyB,CAC9D,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAA,EAKEnB,CAAK,CAAA;AAAA;AAAA;AAAA,uCAAA,EAGyBmB,CAAO,CAAA;AAAA;AAAA;AAAA,OAAA,CAIhD,CAQA,SAASC,EAAAA,CAAYxO,EAAUyO,CAAAA,CAAgC,CAC7D,IAAMnC,CAAAA,CAAOmC,CAAAA,GAAmB,IAAM,EAAA,CAAKA,CAAAA,CAAe,QAAQ,KAAA,CAAO,EAAE,EAE3E,GAAI,OAAA,CAAQ,IAAI,kBAAA,GAA0B,MAAA,CAAQ,CAChD,IAAMC,CAAAA,CACJ,QAAQ,GAAA,CAAI,cAAA,EACZ,QAAQ,GAAA,CAAI,oBAAA,EACZ,eACIC,CAAAA,CAAS,OAAA,CAAQ,IAAI,eAAA,EAAsB,aAAA,CAC3CC,EAAS,OAAA,CAAQ,GAAA,CAAI,iBAAsB,EAAA,CACjD,OAAO,IAAIF,CAAO,CAAA,CAAA,EAAIC,CAAM,CAAA,CAAA,EAAIC,CAAM,GAAGtC,CAAI,CAAA,CAC/C,CAOA,IAAMuC,CAAAA,CAAU,QAAQ,GAAA,CAAI,SAAA,CACtBC,EAAe9O,CAAAA,EAAK,QAAA,EAAYA,GAAK,OAAA,EAAU,IAAA,EAAW,GAChE,OAAI6O,CAAAA,EAAWC,EAAK,QAAA,CAAS,oBAAoB,EACxC,CAAA,CAAA,EAAID,CAAAA,CAAQ,aAAa,CAAA,EAAGvC,CAAI,CAAA,CAAA,CAGlCA,CACT,CAOA,eAAeyC,EAAAA,CAAY/O,EAAmC,CAC5D,OAAI,OAAQA,CAAAA,CAAY,OAAA,EAAY,SAC1BA,CAAAA,CAAY,OAAA,CAClB,OAAO,QAAA,CAAUA,CAAAA,CAAY,OAAO,CAAA,CAC7BA,EAAY,OAAA,CAAmB,QAAA,CAAS,MAAM,CAAA,CAClD,EACT,CA6FO,SAASgP,EAAAA,CAGd7B,EACwG,CACxG,GAAM,CACJ,QAAA,CAAAzE,CAAAA,CAAW,IACX,KAAA,CAAAuG,CAAAA,CACA,UAAAC,CAAAA,CAAY,IAAA,CACZ,KAAA3B,CAAAA,CACA,UAAA,CAAY4B,EAAkB,EAAC,CAC/B,QAAAzJ,CAAAA,CAAU,KAAA,CACV,aAAA0J,CACF,CAAA,CAAIjC,EAGEb,CAAAA,CAAO5D,CAAAA,GAAa,IAAM,EAAA,CAAKA,CAAAA,CAAS,QAAQ,KAAA,CAAO,EAAE,EAGzDD,CAAAA,CAA6B,GACnC,IAAA,GAAW,CAAC3I,EAAMuP,CAAG,CAAA,GAAK,OAAO,OAAA,CAAQJ,CAAK,EAAG,CAE/C,IAAMK,EAAiBD,CAAAA,CAAI,MAAA,EAAWA,EAAI,IAAA,CAAa,MAAA,EAAU,KACjE,GAAI,CAACC,EACH,MAAM,IAAI,MACR,CAAA,+BAAA,EAAkCxP,CAAI,oGAExC,CAAA,CAIF,IAAIuH,EACAkI,CAAAA,CACAC,CAAAA,CACJ,GAAIH,CAAAA,CAAI,YAAA,CAAc,CACpB,IAAMI,CAAAA,CAAKJ,EAAI,YAAA,CACfhI,CAAAA,CAAmB,EAAC,CACpBkI,CAAAA,CAAgB,EAAC,CACjBC,CAAAA,CAAe,EAAC,CAChB,OAAW,CAACnL,CAAAA,CAAOqL,CAAK,CAAA,GAAK,MAAA,CAAO,QAAQD,CAAE,CAAA,CAC5C,QAAWE,CAAAA,IAAQD,CAAAA,CACbC,IAAS,YAAA,CAActI,CAAAA,CAAiB,KAAKhD,CAAK,CAAA,CAC7CsL,IAAS,SAAA,CAAWJ,CAAAA,CAAc,KAAKlL,CAAK,CAAA,CAC5CsL,IAAS,QAAA,EAAUH,CAAAA,CAAa,KAAKnL,CAAK,CAAA,CAGnDgD,EAAiB,MAAA,GAAW,CAAA,GAAGA,EAAmB,MAAA,CAAA,CAClDkI,CAAAA,CAAc,SAAW,CAAA,GAAGA,CAAAA,CAAgB,QAC5CC,CAAAA,CAAa,MAAA,GAAW,IAAGA,CAAAA,CAAe,MAAA,EAChD,CAIA,IAAMI,CAAAA,CAAAA,CAAc,IAAM,CACxB,IAAMC,EAAMR,CAAAA,CAAI,IAAA,CAAa,YAC7B,OAAOQ,CAAAA,EAAMA,EAAG,MAAA,CAAS,CAAA,CAAIA,EAAK,MACpC,CAAA,IACA,GAAID,CAAAA,EAAcJ,EAChB,IAAA,IAAWK,CAAAA,IAAMD,EACVJ,CAAAA,CAAa,QAAA,CAASK,CAAE,CAAA,EAAGL,CAAAA,CAAa,KAAKK,CAAE,CAAA,CAIxD,IAAMzH,CAAAA,CAAuB,CAC3B,KAAAtI,CAAAA,CACA,IAAA,CAAMuP,EAAI,IAAA,CACV,IAAA,CAAMA,EAAI,IAAA,CACV,MAAA,CAAQC,EACR,UAAA,CAAaD,CAAAA,CAAI,KAAa,WAAA,EAAe,CAACA,EAAI,WAAA,EAAe,OAAO,EACxE,WAAA,CAAaA,CAAAA,CAAI,aAAe,OAAA,CAChC,OAAA,CAAUA,EAAI,IAAA,CAAa,QAAA,EAAY,OACvC,OAAA,CAAS,CAAC,CAAEA,CAAAA,CAAI,IAAA,CAAa,SAC7B,UAAA,CAAAO,CAAAA,CACA,WAAaP,CAAAA,CAAI,IAAA,CAAa,aAAe,MAAA,CAC7C,QAAA,CAAUA,EAAI,QAAA,EAAY,EAAA,CAC1B,iBAAAhI,CAAAA,CACA,aAAA,CAAAkI,EACA,YAAA,CAAAC,CAAAA,CACA,YAAaH,CAAAA,CAAI,WAAA,EAAe,MAChC,eAAA,CAAiBA,CAAAA,CAAI,gBACrB,QAAA,CAAUA,CAAAA,CAAI,QAChB,CAAA,CAEA5G,CAAAA,CAAS3I,CAAI,CAAA,CAAIsI,EACnB,CAEA,IAAM0H,CAAAA,CAAWtH,GAAmBC,CAAAA,CAAU6D,CAAAA,CAAM5G,CAAO,CAAA,CAGrDqK,CAAAA,CAAU5C,EAAQ,OAAA,CAClB6C,CAAAA,CAAcD,GAAW,OAAOA,CAAAA,EAAY,SAAWA,CAAAA,CAAU,GACnEE,CAAAA,CAAqC,IAAA,CACzC,SAASC,CAAAA,EAA2B,CAClC,GAAI,CAACD,CAAAA,CAAY,CACf,IAAME,CAAAA,CACJ5C,GAAQ,OAAOA,CAAAA,EAAS,WACnB,OAAA,CACDA,CAAAA,CACG,SACD,KAAA,CACR0C,CAAAA,CAAa/C,GAAoBzE,CAAAA,CAAU6D,CAAAA,CAAM,CAC/C,GAAG0D,EACH,IAAA,CAAMA,CAAAA,CAAY,MAAQG,CAC5B,CAAC,EACH,CACA,OAAOF,CACT,CAGA,IAAMG,EAAS,IAAIjQ,CAAAA,CAmCnB,GAhCAiQ,CAAAA,CAAO,GAAA,CAAI,CAACpQ,CAAAA,CAAKK,CAAAA,CAAKc,IAAS,CAC7Bd,CAAAA,CAAI,IAAI,6BAAA,CAA+B,GAAG,EAC1CA,CAAAA,CAAI,GAAA,CAAI,mCAAoC,MAAM,CAAA,CAClDc,IACF,CAAC,EAGG+N,CAAAA,EACFkB,CAAAA,CAAO,IAAI,MAAOpQ,CAAAA,CAAKqQ,EAAMlP,CAAAA,GAAS,CACpC,IAAMqD,CAAAA,CAAIxE,CAAAA,CAEV,GADoB,MAAA,CAAOwE,CAAAA,CAAE,UAAU,cAAc,CAAA,EAAK,EAAE,CAAA,CAC5C,QAAA,CAAS,kBAAkB,CAAA,CAAA,CACzC,GAAI,OAAOA,CAAAA,CAAE,IAAA,EAAS,SACpB,GAAI,CACDxE,EAAY,IAAA,CAAO,IAAA,CAAK,MAAMwE,CAAAA,CAAE,IAAI,EACvC,CAAA,KAAQ,CAER,SACS,MAAA,CAAO,QAAA,CAAUxE,EAAY,OAAO,CAAA,CAC7C,GAAI,CACF,IAAMC,EAAM,MAAM8O,EAAAA,CAAYvK,CAAC,CAAA,CAC9BxE,CAAAA,CAAY,KAAO,IAAA,CAAK,KAAA,CAAMC,CAAG,EACpC,MAAQ,CAER,CAAA,CAGJ,MAAMkB,CAAAA,GACR,CAAC,CAAA,CAICoM,CAAAA,CACF,GAAI,OAAOA,CAAAA,EAAS,WAElB6C,CAAAA,CAAO,GAAA,CAAI7C,CAAI,CAAA,CAAA,KACV,CAEL,IAAM+C,CAAAA,CAAQ/C,CAAAA,CAAK,OAAS,KAAA,CACtBgD,CAAAA,CACJ,SACA,MAAA,CAAO,IAAA,CAAK,GAAGhD,CAAAA,CAAK,QAAQ,IAAIA,CAAAA,CAAK,QAAQ,EAAE,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,CACpE6C,CAAAA,CAAO,IAAI,CAACpQ,CAAAA,CAAKK,EAAKc,CAAAA,GAAS,CAE7B,IADuBnB,CAAAA,CAAY,OAAA,EAAU,eAAoB,EAAA,IAC3CuQ,CAAAA,CAAU,CAC9BlQ,CAAAA,CACG,MAAA,CAAO,GAAG,CAAA,CACV,GAAA,CAAI,mBAAoB,CAAA,aAAA,EAAgBiQ,CAAK,GAAG,CAAA,CAChD,GAAA,CAAI,eAAgB,kBAAkB,CAAA,CACtC,KAAK,IAAA,CAAK,SAAA,CAAU,CAAE,OAAA,CAAS,KAAA,CAAO,MAAO,cAAe,CAAC,CAAC,CAAA,CACjE,MACF,CACAnP,CAAAA,GACF,CAAC,EACH,CAIF,QAAWC,CAAAA,IAAM+N,CAAAA,CACfiB,EAAO,GAAA,CAAIhP,CAAE,EAMf,GAAI2O,CAAAA,GAAY,KAAA,CAAO,CACrB,IAAMS,CAAAA,CAAW,CAAA,EAAGlE,CAAI,CAAA,YAAA,CAAA,CAClBmE,CAAAA,CAAW,GAAGnE,CAAI,CAAA,OAAA,CAAA,CAExB8D,EAAO,GAAA,CAAII,CAAAA,CAAU,CAACpQ,CAAAA,CAAWC,CAAAA,GAAa,CAC5C,IAAMqQ,CAAAA,CAAOR,GAAQ,CACrB7P,CAAAA,CACG,OAAO,GAAG,CAAA,CACV,IAAI,cAAA,CAAgB,iCAAiC,EACrD,IAAA,CAAK,IAAA,CAAK,UAAUqQ,CAAAA,CAAM,IAAA,CAAM,CAAC,CAAC,EACvC,CAAC,CAAA,CAEDN,CAAAA,CAAO,IAAIK,CAAAA,CAAU,CAACzQ,EAAUK,CAAAA,GAAa,CAG3C,IAAMkO,CAAAA,CAAUC,EAAAA,CAAYxO,EAAKsM,CAAI,CAAA,CAAI,eACnCqE,CAAAA,CAAOrC,EAAAA,CAAe0B,EAAY,KAAA,EAAS,UAAA,CAAYzB,CAAO,CAAA,CACpElO,CAAAA,CACG,OAAO,GAAG,CAAA,CACV,IAAI,cAAA,CAAgB,0BAA0B,EAC9C,IAAA,CAAKsQ,CAAI,EACd,CAAC,EACH,CAGAP,CAAAA,CAAO,GAAA,CAAI,CAACpQ,CAAAA,CAAKK,CAAAA,CAAKc,IAAS,CAC7B,GAAInB,EAAI,MAAA,GAAW,SAAA,CAAW,CAC5B8P,CAAAA,CAAS,aAAA,CAAc9P,EAAKK,CAAG,CAAA,CAC/B,MACF,CACAc,CAAAA,GACF,CAAC,CAAA,CAGDiP,EAAO,GAAA,CAAI,CAAA,EAAG9D,CAAI,CAAA,UAAA,CAAA,CAAcwD,CAAAA,CAAS,UAAU,CAAA,CAGnDM,CAAAA,CAAO,KAAK,CAAA,EAAG9D,CAAI,mBAAoBwD,CAAAA,CAAS,WAAW,EAG3DM,CAAAA,CAAO,GAAA,CAAI,GAAG9D,CAAI,CAAA,cAAA,CAAA,CAAkBwD,EAAS,SAAS,CAAA,CAGtDM,EAAO,IAAA,CAAK,CAAA,EAAG9D,CAAI,CAAA,UAAA,CAAA,CAAcwD,CAAAA,CAAS,YAAY,CAAA,CAGtDM,CAAAA,CAAO,IAAI,CAAA,EAAG9D,CAAI,iBAAkB,CAACtM,CAAAA,CAAUK,IAC7CyP,CAAAA,CAAS,YAAA,CAAa9P,EAAKK,CAAAA,CAAK,KAAK,CACvC,CAAA,CAGA+P,CAAAA,CAAO,MAAM,CAAA,EAAG9D,CAAI,iBAAkB,CAACtM,CAAAA,CAAUK,IAC/CyP,CAAAA,CAAS,YAAA,CAAa9P,EAAKK,CAAAA,CAAK,IAAI,CACtC,CAAA,CAGA+P,CAAAA,CAAO,OAAO,CAAA,EAAG9D,CAAI,iBAAkBwD,CAAAA,CAAS,YAAY,EAG5D,IAAMtP,CAAAA,CAAU,MACdR,CAAAA,CACAK,CAAAA,GACkB,CAClB,MAAM+P,CAAAA,CAAO,OAAOpQ,CAAAA,CAAYK,CAAU,EAC5C,CAAA,CAGA,OAACG,EAAgB,IAAA,CAAO0P,CAAAA,CACpBd,IAAe5O,CAAAA,CAAgB,YAAA,CAAe4O,GAE3C5O,CAMT","file":"index.cjs","sourcesContent":["/**\n * Minimal zero-dependency HTTP router for Firebase Functions.\n * Compatible with any Express-like (req, res) handler.\n *\n * Supports:\n * - Named path parameters (e.g. \"/repos/:name/:id\")\n * - GET, POST, DELETE methods\n * - Global middleware (before each route)\n * - 404 / error fallbacks\n *\n * @example\n * ```typescript\n * import { MiniRouter } from \"@lpdjs/firestore-repo-service/servers/admin\";\n *\n * // Create router\n * const router = new MiniRouter();\n *\n * // Add global middleware (executed before every route)\n * router.use(async (req, res, next) => {\n * console.log(`${req.method} ${req.url}`);\n * await next();\n * });\n *\n * // Auth middleware\n * router.use((req, res, next) => {\n * if (!req.headers?.authorization) {\n * res.status(401).send(\"Unauthorized\");\n * return;\n * }\n * next();\n * });\n *\n * // Define routes with path parameters\n * router.get(\"/users\", async (req, res) => {\n * res.json({ users: await getAllUsers() });\n * });\n *\n * router.get(\"/users/:id\", async (req, res) => {\n * const user = await getUser(req.params.id); // Access path params\n * if (!user) {\n * res.status(404).send(\"User not found\");\n * return;\n * }\n * res.json(user);\n * });\n *\n * router.post(\"/users\", async (req, res) => {\n * const user = await createUser(req.body);\n * res.status(201).json(user);\n * });\n *\n * router.delete(\"/users/:id\", async (req, res) => {\n * await deleteUser(req.params.id);\n * res.status(204).end();\n * });\n *\n * // Custom 404 handler\n * router.onNotFound((req, res) => {\n * res.status(404).json({ error: \"Route not found\", path: req.url });\n * });\n *\n * // Custom error handler\n * router.onError((err, req, res) => {\n * console.error(\"Error:\", err);\n * res.status(500).json({ error: \"Internal server error\" });\n * });\n *\n * // Use with Firebase Functions\n * export const api = onRequest(async (req, res) => {\n * await router.handle(req, res);\n * });\n * ```\n */\n\nexport type AnyReq = {\n method?: string;\n url?: string;\n /** Express originalUrl — preserved before any router stripping, contains the full path including the Firebase Functions prefix */\n originalUrl?: string;\n path?: string;\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n query?: Record<string, string | string[] | undefined>;\n};\n\nexport type AnyRes = {\n status: (code: number) => AnyRes;\n set: (key: string, value: string) => AnyRes;\n send: (body: string) => void;\n json: (body: unknown) => void;\n end: () => void;\n};\n\nexport type RouteParams = Record<string, string>;\n\nexport type RouteHandler = (\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n) => void | Promise<void>;\n\nexport type Middleware = (\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n next: () => void | Promise<void>,\n) => void | Promise<void>;\n\n// ---------------------------------------------------------------------------\n// Route matching\n// ---------------------------------------------------------------------------\n\ninterface CompiledRoute {\n method: string;\n pattern: RegExp;\n paramNames: string[];\n handler: RouteHandler;\n}\n\nfunction compilePath(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n const src = path\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (c) => (c === \":\" ? c : `\\\\${c}`))\n .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_match, name: string) => {\n paramNames.push(name);\n return \"([^/]+)\";\n });\n\n return { pattern: new RegExp(`^${src}$`), paramNames };\n}\n\nfunction extractPath(req: AnyReq): string {\n const raw = req.path ?? req.url ?? \"/\";\n const idx = raw.indexOf(\"?\");\n return idx === -1 ? raw : raw.slice(0, idx);\n}\n\n// ---------------------------------------------------------------------------\n// Router class\n// ---------------------------------------------------------------------------\n\nexport class MiniRouter {\n private routes: CompiledRoute[] = [];\n private middlewares: Middleware[] = [];\n private notFoundHandler: RouteHandler = (_req, res) => {\n res.status(404).send(\"Not Found\");\n };\n private errorHandler: (err: unknown, req: AnyReq, res: AnyRes) => void = (\n err,\n _req,\n res,\n ) => {\n console.error(\"[MiniRouter]\", err);\n res.status(500).send(\"Internal Server Error\");\n };\n\n // ── Route registration ────────────────────────────────────────────────────\n\n use(middleware: Middleware): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n get(path: string, handler: RouteHandler): this {\n return this.addRoute(\"GET\", path, handler);\n }\n\n post(path: string, handler: RouteHandler): this {\n return this.addRoute(\"POST\", path, handler);\n }\n\n put(path: string, handler: RouteHandler): this {\n return this.addRoute(\"PUT\", path, handler);\n }\n\n patch(path: string, handler: RouteHandler): this {\n return this.addRoute(\"PATCH\", path, handler);\n }\n\n delete(path: string, handler: RouteHandler): this {\n return this.addRoute(\"DELETE\", path, handler);\n }\n\n onNotFound(handler: RouteHandler): this {\n this.notFoundHandler = handler;\n return this;\n }\n\n onError(handler: (err: unknown, req: AnyReq, res: AnyRes) => void): this {\n this.errorHandler = handler;\n return this;\n }\n\n private addRoute(method: string, path: string, handler: RouteHandler): this {\n const { pattern, paramNames } = compilePath(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n paramNames,\n handler,\n });\n return this;\n }\n\n // ── Dispatch ──────────────────────────────────────────────────────────────\n\n async handle(req: AnyReq, res: AnyRes): Promise<void> {\n const method = (req.method ?? \"GET\").toUpperCase();\n const path = extractPath(req);\n\n // Find matching route\n let matchedRoute: CompiledRoute | null = null;\n let params: RouteParams = {};\n\n for (const route of this.routes) {\n if (route.method !== method) continue;\n const m = path.match(route.pattern);\n if (m) {\n matchedRoute = route;\n params = {};\n route.paramNames.forEach((name, i) => {\n params[name] = decodeURIComponent(m[i + 1] ?? \"\");\n });\n break;\n }\n }\n\n const enrichedReq = Object.assign(req, { params });\n\n // Run middleware chain → then handler\n const handler = matchedRoute ? matchedRoute.handler : this.notFoundHandler;\n\n try {\n await this.runMiddlewareChain(enrichedReq, res, handler);\n } catch (err) {\n this.errorHandler(err, req, res);\n }\n }\n\n private async runMiddlewareChain(\n req: AnyReq & { params: RouteParams },\n res: AnyRes,\n finalHandler: RouteHandler,\n ): Promise<void> {\n let index = 0;\n\n const next = async (): Promise<void> => {\n if (index < this.middlewares.length) {\n const mw = this.middlewares[index++]!;\n await mw(req, res, next);\n } else {\n await finalHandler(req, res);\n }\n };\n\n await next();\n }\n}\n","import { Timestamp } from \"firebase-admin/firestore\";\n\nexport type DateHandlingMode = \"preserve\" | \"normalize\";\n\nlet currentMode: DateHandlingMode = \"preserve\";\n\nexport function setDateHandling(mode: DateHandlingMode): void {\n currentMode = mode;\n}\n\nexport function getDateHandling(): DateHandlingMode {\n return currentMode;\n}\n\nfunction isTimestampLike(\n v: unknown,\n): v is { _seconds: number; _nanoseconds: number } {\n return (\n typeof v === \"object\" &&\n v !== null &&\n typeof (v as { _seconds?: unknown })._seconds === \"number\" &&\n typeof (v as { _nanoseconds?: unknown })._nanoseconds === \"number\"\n );\n}\n\nexport function coerceToDate(value: unknown): Date | null {\n if (value === null || value === undefined) return null;\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (value instanceof Timestamp) return value.toDate();\n if (isTimestampLike(value)) {\n return new Date(\n value._seconds * 1000 + Math.floor(value._nanoseconds / 1e6),\n );\n }\n if (typeof value === \"string\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n if (typeof value === \"number\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n if (typeof v !== \"object\" || v === null) return false;\n const proto = Object.getPrototypeOf(v);\n return proto === Object.prototype || proto === null;\n}\n\nexport function normalizeTimestamps<T>(value: T): T {\n if (value instanceof Timestamp) return value.toDate() as unknown as T;\n if (Array.isArray(value)) {\n return value.map((v) => normalizeTimestamps(v)) as unknown as T;\n }\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n out[k] = normalizeTimestamps(v);\n }\n return out as unknown as T;\n }\n return value;\n}\n\nexport function maybeNormalize<T>(value: T): T {\n return currentMode === \"normalize\" ? normalizeTimestamps(value) : value;\n}\n","/**\n * Generates a Firebase Console URL to create a composite index OR a\n * single-field index exemption.\n *\n * When Firestore throws FAILED_PRECONDITION (code 9) for missing indexes,\n * the error message sometimes includes a creation link (regular collections)\n * but often does NOT include it (collection groups). This utility builds the\n * link from the query context so the admin UI can always present it.\n *\n * Two URL shapes are produced depending on the query:\n *\n * - **Composite index** (≥ 2 indexed fields, or any COLLECTION-scope multi-field\n * query). Encoded as a JSON `create_composite=` parameter under\n * `/v1/r/project/{p}/firestore/indexes`.\n *\n * - **Single-field exemption** (exactly one indexed field on a COLLECTION_GROUP\n * query — Firestore disables single-field collection-group indexes by\n * default and requires an explicit exemption). Encoded as a base64\n * protobuf `create_exemption=` parameter under\n * `/project/{p}/firestore/databases/-default-/indexes/automatic`.\n */\n\nimport type {\n FilterState,\n QueryError,\n SortState,\n WhereOp,\n} from \"./components/types\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\ninterface IndexField {\n fieldPath: string;\n order?: \"ASCENDING\" | \"DESCENDING\";\n arrayConfig?: \"CONTAINS\";\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nconst RANGE_OPS = new Set<WhereOp>([\"<\", \"<=\", \">\", \">=\", \"!=\"]);\nconst ARRAY_OPS = new Set<WhereOp>([\"array-contains\", \"array-contains-any\"]);\n\nfunction toIndexOrder(dir?: \"asc\" | \"desc\"): \"ASCENDING\" | \"DESCENDING\" {\n return dir === \"desc\" ? \"DESCENDING\" : \"ASCENDING\";\n}\n\n/**\n * Extract the collection ID (last path segment) from a Firestore path.\n * e.g. \"posts/{postId}/comments\" → \"comments\"\n */\nexport function collectionIdFromPath(path: string): string {\n const segments = path.split(\"/\").filter(Boolean);\n return segments[segments.length - 1] ?? path;\n}\n\n// ── Main ─────────────────────────────────────────────────────────────────────\n\n/**\n * Build a Firebase Console URL that pre-fills the composite-index creation form.\n *\n * @param projectId - GCP project ID (e.g. \"firestore-repo-services\")\n * @param collectionId - Firestore collection ID (e.g. \"posts\", \"comments\")\n * @param isGroup - Whether this is a collection group query\n * @param filters - Active filter states from the admin UI\n * @param sort - Active sort state (optional)\n * @returns - Full HTTPS URL to the Firebase Console index wizard\n */\nexport function buildIndexUrl(\n projectId: string,\n collectionId: string,\n isGroup: boolean,\n filters: FilterState[],\n sort?: SortState,\n): string {\n const fields: IndexField[] = [];\n const seen = new Set<string>();\n\n // 1. Equality filters first (order doesn't matter for equality)\n for (const f of filters) {\n if (f.op === \"==\" || f.op === \"in\" || f.op === \"not-in\") {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n fields.push({ fieldPath: f.field, order: \"ASCENDING\" });\n }\n }\n\n // 2. Array operators\n for (const f of filters) {\n if (ARRAY_OPS.has(f.op)) {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n fields.push({ fieldPath: f.field, arrayConfig: \"CONTAINS\" });\n }\n }\n\n // 3. Range / inequality filters\n for (const f of filters) {\n if (RANGE_OPS.has(f.op)) {\n if (seen.has(f.field)) continue;\n seen.add(f.field);\n // Use the sort direction if the range field matches the orderBy field\n const dir =\n sort?.field === f.field ? toIndexOrder(sort.dir) : \"ASCENDING\";\n fields.push({ fieldPath: f.field, order: dir });\n }\n }\n\n // 4. OrderBy fields not already covered by filters\n if (sort && !seen.has(sort.field)) {\n fields.push({ fieldPath: sort.field, order: toIndexOrder(sort.dir) });\n }\n\n // ── Single-field collection-group exemption ──────────────────────────────\n // Firestore disables single-field collection-group indexes by default; you\n // must create an explicit \"field exemption\". The Firebase Console wizard\n // for that uses a totally different URL (`create_exemption=`) with its own\n // protobuf shape — see buildExemptionUrl below.\n if (fields.length === 1 && isGroup) {\n return buildExemptionUrl(projectId, collectionId, fields[0]!);\n }\n\n // ── Composite index ──────────────────────────────────────────────────────\n // Firebase Console encodes composite indexes as a base64 protobuf payload\n // under `create_composite=`. Every composite index implicitly ends with\n // `__name__` as a tie-breaker, so we always append it (matches what the\n // Console itself produces).\n const lastDir =\n sort && fields.some((f) => f.fieldPath === sort.field)\n ? toIndexOrder(sort.dir)\n : \"ASCENDING\";\n fields.push({ fieldPath: \"__name__\", order: lastDir });\n\n return buildCompositeUrl(projectId, collectionId, isGroup, fields);\n}\n\n/**\n * Build a Firebase Console URL that pre-fills the composite index creation\n * form for either a regular collection or a collection-group query.\n *\n * The URL uses a base64-encoded protobuf payload (the same shape the Console\n * itself produces when you click \"Add index\" in the UI). Schema:\n *\n * message CompositeIndex {\n * string resource_path = 1; // projects/.../collectionGroups/{cg}/indexes/_\n * int32 query_scope = 2; // 1 = COLLECTION, 2 = COLLECTION_GROUP\n * repeated IndexField fields = 3;\n * }\n */\nexport function buildCompositeUrl(\n projectId: string,\n collectionId: string,\n isGroup: boolean,\n fields: IndexField[],\n databaseId: string = \"(default)\",\n): string {\n const resource = `projects/${projectId}/databases/${databaseId}/collectionGroups/${collectionId}/indexes/_`;\n\n const payload: number[] = [\n ...pbString(1, resource),\n ...pbInt(2, isGroup ? 2 : 1),\n ];\n for (const f of fields) {\n payload.push(...pbMessage(3, encodeIndexField(f)));\n }\n\n const urlDbId = databaseId === \"(default)\" ? \"-default-\" : databaseId;\n const encoded = encodeURIComponent(toBase64(payload));\n return `https://console.firebase.google.com/project/${projectId}/firestore/databases/${urlDbId}/indexes?create_composite=${encoded}`;\n}\n\n/**\n * Try to extract an index-creation URL from a Firestore error message.\n * Returns `undefined` if no URL is found.\n */\nexport function extractIndexUrl(message: string): string | undefined {\n const match = message.match(\n /https:\\/\\/console\\.firebase\\.google\\.com[^\\s)\"]*/,\n );\n return match?.[0];\n}\n\n// ── Single-field exemption URL (collection-group) ────────────────────────────\n\n/**\n * Minimal protobuf encoder for the field-exemption payload used by the\n * Firebase Console URL `?create_exemption=…`.\n *\n * Wire format (subset):\n * tag = (field_number << 3) | wire_type\n * wire types: 0 = varint, 2 = length-delimited\n *\n * Schema we encode (reverse-engineered from real Firebase Console URLs):\n * message FieldExemption {\n * string resource_path = 1; // projects/.../fields/{field}\n * int32 query_scope = 2; // 1 = COLLECTION, 2 = COLLECTION_GROUP\n * IndexConfig index = 3;\n * }\n * message IndexConfig {\n * string field_path = 1;\n * int32 order = 2; // 1 = ASCENDING, 2 = DESCENDING\n * int32 array_config = 3; // 1 = CONTAINS (mutually exclusive with order)\n * }\n */\nfunction pbVarint(n: number): number[] {\n const out: number[] = [];\n let v = n >>> 0;\n while (v >= 0x80) {\n out.push((v & 0x7f) | 0x80);\n v >>>= 7;\n }\n out.push(v & 0x7f);\n return out;\n}\n\nfunction pbTag(fieldNumber: number, wireType: 0 | 2): number {\n return (fieldNumber << 3) | wireType;\n}\n\nfunction pbString(fieldNumber: number, value: string): number[] {\n const bytes = Array.from(new TextEncoder().encode(value));\n return [pbTag(fieldNumber, 2), ...pbVarint(bytes.length), ...bytes];\n}\n\nfunction pbInt(fieldNumber: number, value: number): number[] {\n return [pbTag(fieldNumber, 0), ...pbVarint(value)];\n}\n\nfunction pbMessage(fieldNumber: number, payload: number[]): number[] {\n return [pbTag(fieldNumber, 2), ...pbVarint(payload.length), ...payload];\n}\n\n/** Encode an IndexField submessage: { field_path:1, order:2 OR array_config:3 } */\nfunction encodeIndexField(f: IndexField): number[] {\n const out: number[] = [...pbString(1, f.fieldPath)];\n if (f.arrayConfig === \"CONTAINS\") {\n out.push(...pbInt(3, 1));\n } else {\n out.push(...pbInt(2, f.order === \"DESCENDING\" ? 2 : 1));\n }\n return out;\n}\n\nfunction toBase64(bytes: number[]): string {\n // Standard base64 (no padding) — matches what Firebase Console produces.\n // Use Buffer when available (Node), otherwise fall back to btoa.\n const bin = String.fromCharCode(...bytes);\n let b64: string;\n if (typeof Buffer !== \"undefined\") {\n b64 = Buffer.from(bytes).toString(\"base64\");\n } else if (typeof btoa !== \"undefined\") {\n b64 = btoa(bin);\n } else {\n throw new Error(\"No base64 encoder available\");\n }\n return b64.replace(/=+$/, \"\");\n}\n\n/**\n * Build a Firebase Console URL that pre-fills the single-field index\n * exemption form for a collection-group query.\n */\nexport function buildExemptionUrl(\n projectId: string,\n collectionId: string,\n field: IndexField,\n databaseId: string = \"(default)\",\n): string {\n const resource = `projects/${projectId}/databases/${databaseId}/collectionGroups/${collectionId}/fields/${field.fieldPath}`;\n\n const payload: number[] = [\n ...pbString(1, resource),\n ...pbInt(2, 2), // COLLECTION_GROUP\n ...pbMessage(3, encodeIndexField(field)),\n ];\n\n // Database ID for the URL path: \"(default)\" → \"-default-\",\n // any other ID is used as-is (Firebase Console uses the bare ID).\n const urlDbId = databaseId === \"(default)\" ? \"-default-\" : databaseId;\n\n // create_exemption is base64 (URL-safe characters only — `+` and `/` are\n // valid in query string values per RFC, and Firebase accepts them, but we\n // URL-encode just to be safe).\n const encoded = encodeURIComponent(toBase64(payload));\n return `https://console.firebase.google.com/project/${projectId}/firestore/databases/${urlDbId}/indexes/automatic?create_exemption=${encoded}`;\n}\n\n// ── Project ID extraction ────────────────────────────────────────────────────\n\n/**\n * Robustly extract the GCP project ID from a Firestore reference.\n * Falls back through several known locations across firebase-admin versions\n * and finally to standard environment variables.\n */\nexport function extractProjectId(ref: unknown): string | undefined {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const r = ref as any;\n const candidates: unknown[] = [\n r?.firestore?.projectId,\n r?.firestore?.app?.options?.projectId,\n r?.firestore?._settings?.projectId,\n r?.firestore?.databaseId?.projectId,\n r?._firestore?.projectId,\n ];\n for (const c of candidates) {\n if (typeof c === \"string\" && c.length > 0) return c;\n }\n const env =\n process.env[\"GCLOUD_PROJECT\"] ||\n process.env[\"GOOGLE_CLOUD_PROJECT\"] ||\n process.env[\"FIREBASE_PROJECT_ID\"];\n return env || undefined;\n}\n\n// ── Centralized error → QueryError conversion ────────────────────────────────\n\n/**\n * Context required to build a fallback index URL when Firestore does not\n * include one in its error (typical for collection-group queries).\n */\nexport interface QueryErrorContext {\n /** Repository ref (CollectionReference / Query) used to extract project id */\n ref: unknown;\n /** Firestore collection path of the repo (e.g. \"posts/{postId}/comments\") */\n path: string;\n /** Whether the repo is a collection-group */\n isGroup: boolean;\n /** Active where filters at the time of the failed query */\n filters: FilterState[];\n /** Active orderBy state at the time of the failed query (if any) */\n sort?: SortState;\n}\n\n/**\n * Detect whether an unknown error thrown by Firestore is a missing-index\n * (`FAILED_PRECONDITION` / code 9) error.\n */\nexport function isMissingIndexError(err: unknown): boolean {\n const fe = err as { code?: number; message?: string } | null | undefined;\n if (!fe) return false;\n if (fe.code === 9) return true;\n return typeof fe.message === \"string\"\n ? fe.message.includes(\"requires an index\")\n : false;\n}\n\n/**\n * Convert a Firestore error into a typed `QueryError` with a guaranteed\n * `indexUrl` for missing-index cases (extracted from the message when\n * present, otherwise rebuilt from the query context — necessary for\n * collection-group queries where the SDK omits the link).\n *\n * Returns `null` when `err` is falsy.\n */\nexport function toQueryError(\n err: unknown,\n ctx: QueryErrorContext,\n): QueryError {\n const fe = (err ?? {}) as { code?: number; message?: string };\n const isIndex = isMissingIndexError(err);\n\n let indexUrl: string | undefined;\n if (isIndex) {\n indexUrl = fe.message ? extractIndexUrl(fe.message) : undefined;\n if (!indexUrl) {\n const projectId = extractProjectId(ctx.ref);\n if (projectId) {\n const colId = collectionIdFromPath(ctx.path);\n indexUrl = buildIndexUrl(\n projectId,\n colId,\n ctx.isGroup,\n ctx.filters,\n ctx.sort,\n );\n }\n }\n }\n\n return {\n type: isIndex ? \"index\" : \"error\",\n message: isIndex\n ? \"This query requires a composite index that does not exist yet.\"\n : (fe.message ?? \"Query failed\"),\n indexUrl,\n };\n}\n","/**\n * HTTP route handlers for the CRUD API server.\n *\n * Routes:\n * GET /:repoName → list documents (paginated)\n * GET /:repoName/:id → get single document\n * POST /:repoName → create document\n * PUT /:repoName/:id → update document (full)\n * PATCH /:repoName/:id → update document (partial)\n * DELETE /:repoName/:id → delete document\n */\n\nimport { z } from \"zod\";\nimport {\n coerceToDate,\n getDateHandling,\n maybeNormalize,\n} from \"../../shared/date-config\";\nimport {\n isMissingIndexError,\n toQueryError,\n type QueryErrorContext,\n} from \"../admin/index-url\";\nimport type {\n ApiResponse,\n CrudRepoEntry,\n CrudRepoRegistry,\n ListResponseData,\n QueryRequestBody,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\nfunction sendJson<T>(res: any, data: ApiResponse<T>, status = 200): void {\n const payload = maybeNormalize(data);\n res\n .status(status)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(payload));\n}\n\nfunction sendSuccess<T>(\n res: any,\n data: T,\n meta?: ApiResponse[\"meta\"],\n status = 200,\n): void {\n sendJson(res, { success: true, data, meta }, status);\n}\n\nfunction sendError(res: any, error: string, status = 400): void {\n sendJson(res, { success: false, error }, status);\n}\n\n/**\n * Send a structured error response. When the underlying Firestore error is a\n * missing-index (`FAILED_PRECONDITION` / code 9), include `errorType: \"index\"`\n * and an `indexUrl` pointing to the Firebase Console index-creation wizard —\n * crucial for collection-group queries where the SDK omits the link.\n */\nfunction sendQueryError(\n res: any,\n err: unknown,\n ctx: QueryErrorContext,\n fallbackMessage: string,\n verbose: boolean,\n): void {\n const qe = toQueryError(err, ctx);\n const isIndex = qe.type === \"index\";\n const status = isIndex ? 424 : 500;\n const message = isIndex\n ? qe.message\n : verbose && err instanceof Error\n ? err.message\n : fallbackMessage;\n const payload: ApiResponse = { success: false, error: message };\n if (isIndex) {\n payload.errorType = \"index\";\n if (qe.indexUrl) payload.indexUrl = qe.indexUrl;\n }\n sendJson(res, payload, status);\n}\n\n// ---------------------------------------------------------------------------\n// ID generation\n// ---------------------------------------------------------------------------\n\nconst _idChars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n/** Generate a random 20-char alphanumeric ID matching Firestore's native format. */\nfunction generateFirestoreId(): string {\n let id = \"\";\n for (let i = 0; i < 20; i++) {\n id += _idChars.charAt(Math.floor(Math.random() * _idChars.length));\n }\n return id;\n}\n\n// ---------------------------------------------------------------------------\n// Zod schema helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively wrap z.date() with z.preprocess(coerceToDate) so that ISO strings,\n * Firestore Timestamps and {_seconds,_nanoseconds} payloads are accepted as input.\n * Only invoked when global date handling mode is \"normalize\".\n */\nfunction wrapDateSchemas(schema: z.ZodType): z.ZodType {\n const def = (schema as any)._def ?? (schema as any).def;\n if (!def) return schema;\n const typeName = def.typeName ?? def.type;\n\n if (typeName === \"ZodDate\" || typeName === \"date\") {\n return z.preprocess((v) => coerceToDate(v) ?? v, schema as z.ZodDate);\n }\n if (typeName === \"ZodObject\" || typeName === \"object\") {\n const shape = (schema as z.ZodObject<any>).shape;\n const wrapped: Record<string, z.ZodType> = {};\n for (const [k, v] of Object.entries(shape)) {\n wrapped[k] = wrapDateSchemas(v as z.ZodType);\n }\n return z.object(wrapped);\n }\n if (typeName === \"ZodArray\" || typeName === \"array\") {\n const inner = def.element ?? def.type;\n if (inner) return z.array(wrapDateSchemas(inner));\n }\n if (typeName === \"ZodOptional\" || typeName === \"optional\") {\n const inner = def.innerType;\n if (inner) return wrapDateSchemas(inner).optional();\n }\n if (typeName === \"ZodNullable\" || typeName === \"nullable\") {\n const inner = def.innerType;\n if (inner) return wrapDateSchemas(inner).nullable();\n }\n if (typeName === \"ZodDefault\" || typeName === \"default\") {\n const inner = def.innerType;\n const dflt = def.defaultValue;\n if (inner) {\n const wrapped = wrapDateSchemas(inner);\n return typeof dflt === \"function\"\n ? wrapped.default(dflt())\n : wrapped.default(dflt);\n }\n }\n return schema;\n}\n\n/**\n * Pick only specified fields from a Zod schema, always excluding system-managed keys.\n *\n * - `fields` undefined or empty → all schema fields minus systemKeys\n * - `fields` with values → only those fields, minus systemKeys\n */\nfunction pickSchemaFields(\n schema: z.ZodObject<any>,\n fields: string[] | undefined,\n systemKeys: string[] = [],\n): z.ZodObject<any> {\n const shape = schema.shape;\n const picked: Record<string, z.ZodType> = {};\n\n const source = fields && fields.length > 0 ? fields : Object.keys(shape);\n\n for (const field of source) {\n if (systemKeys.includes(field)) continue;\n const topLevel = field.split(\".\")[0];\n if (topLevel && shape[topLevel]) {\n picked[topLevel] = shape[topLevel]!;\n }\n }\n\n return z.object(picked);\n}\n\n/**\n * Validate data against schema and return parsed result or error.\n */\nfunction validateData(\n schema: z.ZodObject<any>,\n data: unknown,\n fields: string[] | undefined,\n partial = false,\n systemKeys: string[] = [],\n):\n | { success: true; data: Record<string, unknown> }\n | { success: false; error: string } {\n try {\n const targetSchema = pickSchemaFields(schema, fields, systemKeys);\n const partialSchema = partial ? targetSchema.partial() : targetSchema;\n const finalSchema =\n getDateHandling() === \"normalize\"\n ? (wrapDateSchemas(partialSchema) as z.ZodObject<any>)\n : partialSchema;\n const parsed = finalSchema.parse(data);\n return { success: true, data: parsed as Record<string, unknown> };\n } catch (err) {\n if (err instanceof z.ZodError) {\n const messages = err.issues.map(\n (e) => `${e.path.join(\".\")}: ${e.message}`,\n );\n return {\n success: false,\n error: `Validation failed: ${messages.join(\", \")}`,\n };\n }\n return { success: false, error: \"Validation failed\" };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Filter parsing\n// ---------------------------------------------------------------------------\n\ntype WhereOp =\n | \"==\"\n | \"!=\"\n | \"<\"\n | \"<=\"\n | \">\"\n | \">=\"\n | \"in\"\n | \"not-in\"\n | \"array-contains\"\n | \"array-contains-any\";\n\ninterface ParsedFilter {\n field: string;\n op: WhereOp;\n value: unknown;\n}\n\n/**\n * Parse query params into filter conditions.\n * Supports:\n * - field=value → field == value\n * - field__op=value → field op value (e.g., status__ne=draft → status != draft)\n * - field__in=a,b,c → field in [a, b, c]\n */\nfunction parseFilters(\n query: Record<string, string | string[] | undefined>,\n filterableFields: string[] | undefined,\n): ParsedFilter[] {\n const filters: ParsedFilter[] = [];\n const allowedFields = filterableFields ? new Set(filterableFields) : null;\n\n const opMap: Record<string, WhereOp> = {\n eq: \"==\",\n ne: \"!=\",\n lt: \"<\",\n lte: \"<=\",\n gt: \">\",\n gte: \">=\",\n in: \"in\",\n nin: \"not-in\",\n contains: \"array-contains\",\n containsAny: \"array-contains-any\",\n };\n\n for (const [key, rawVal] of Object.entries(query)) {\n if (rawVal === undefined) continue;\n\n // Skip pagination/meta params\n if (\n [\"cursor\", \"limit\", \"pageSize\", \"orderBy\", \"orderDir\", \"select\"].includes(\n key,\n )\n )\n continue;\n\n const val = Array.isArray(rawVal) ? rawVal[0] : rawVal;\n if (val === undefined || val === \"\") continue;\n\n // Parse field__op format\n const match = key.match(/^(\\w+)__(\\w+)$/);\n let field: string;\n let op: WhereOp = \"==\";\n\n if (match && match[1] && match[2]) {\n field = match[1];\n const opKey = match[2];\n if (opMap[opKey]) {\n op = opMap[opKey];\n } else {\n continue; // Unknown operator, skip\n }\n } else if (!match) {\n field = key;\n } else {\n continue; // Invalid match\n }\n\n // Check if field is filterable\n if (allowedFields && !allowedFields.has(field)) continue;\n\n // Parse value\n let parsedVal: unknown = val;\n\n // Handle \"in\" and \"not-in\" operators (comma-separated)\n if (op === \"in\" || op === \"not-in\" || op === \"array-contains-any\") {\n parsedVal = val.split(\",\").map((v) => parseValue(v.trim()));\n } else {\n parsedVal = parseValue(val);\n }\n\n filters.push({ field, op, value: parsedVal });\n }\n\n return filters;\n}\n\n/**\n * Parse a string value into appropriate type.\n */\nfunction parseValue(val: string): unknown {\n // Boolean\n if (val === \"true\") return true;\n if (val === \"false\") return false;\n if (val === \"null\") return null;\n\n // Number\n const num = Number(val);\n if (!isNaN(num) && val !== \"\") return num;\n\n // String\n return val;\n}\n\n// ---------------------------------------------------------------------------\n// Cursor serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a Firestore DocumentSnapshot to a JSON-safe cursor object.\n */\nfunction serializeCursor(\n snapshot: import(\"firebase-admin/firestore\").DocumentSnapshot | undefined,\n): Record<string, unknown> | null {\n if (!snapshot) return null;\n return { docId: snapshot.id };\n}\n\n/**\n * Deserialize a cursor object back to a DocumentSnapshot.\n * Fetches the document from Firestore to get the actual snapshot.\n */\nasync function deserializeCursor(\n entry: CrudRepoEntry,\n cursor: unknown,\n): Promise<import(\"firebase-admin/firestore\").DocumentSnapshot | undefined> {\n if (!cursor || typeof cursor !== \"object\") return undefined;\n const docId = (cursor as Record<string, unknown>).docId;\n if (typeof docId !== \"string\") return undefined;\n\n try {\n // Get the collection reference from the repo\n const colRef = entry.repo\n .ref as import(\"firebase-admin/firestore\").CollectionReference;\n if (typeof colRef.doc !== \"function\") return undefined;\n const snapshot = await colRef.doc(docId).get();\n return snapshot.exists ? snapshot : undefined;\n } catch {\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Handlers factory\n// ---------------------------------------------------------------------------\n\nexport function createCrudHandlers(\n registry: CrudRepoRegistry,\n basePath: string,\n verbose: boolean,\n) {\n // ── Helper to get repo entry ────────────────────────────────────────────\n function getRepoEntry(\n repoName: string | undefined,\n res: any,\n ): CrudRepoEntry | null {\n if (!repoName || !registry[repoName]) {\n sendError(res, `Repository \"${repoName}\" not found`, 404);\n return null;\n }\n return registry[repoName];\n }\n\n /**\n * Extract Firestore document path args from a document's stored path.\n * e.g. \"posts/abc/comments/xyz\" → [\"abc\", \"xyz\"] (the doc-ID segments).\n */\n function extractPathArgs(\n doc: Record<string, unknown>,\n pathKey?: string,\n ): string[] | undefined {\n if (!pathKey) return undefined;\n const fullPath = doc[pathKey];\n if (typeof fullPath !== \"string\" || !fullPath) return undefined;\n const segments = fullPath.split(\"/\").filter(Boolean);\n const args: string[] = [];\n for (let i = 1; i < segments.length; i += 2) {\n args.push(segments[i]!);\n }\n return args.length > 0 ? args : undefined;\n }\n\n /**\n * Fetch a single document by its documentKey, with fallback to query\n * for collection-group repos where direct documentRef may fail.\n */\n async function fetchDocById(\n entry: CrudRepoEntry,\n docId: string,\n ): Promise<Record<string, unknown> | null> {\n const getterName = `by${entry.documentKey.charAt(0).toUpperCase()}${entry.documentKey.slice(1)}`;\n const getter = (entry.repo.get as any)[getterName];\n\n if (typeof getter === \"function\") {\n try {\n const doc = (await getter(docId)) as Record<string, unknown> | null;\n if (doc) return doc;\n } catch {\n // Direct ref may fail for subcollections — fall through to query\n }\n }\n\n const results = await entry.repo.query.by({\n where: [[entry.documentKey, \"==\", docId]],\n limit: 1,\n });\n return (results[0] as Record<string, unknown>) ?? null;\n }\n\n // ── LIST: GET /:repoName ────────────────────────────────────────────────\n async function handleList(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n // Captured for error handling (so the catch can build an index URL)\n let ctxFilters: { field: string; op: any; value: string }[] = [];\n let ctxSort: { field: string; dir: \"asc\" | \"desc\" } | undefined;\n\n try {\n const query = req.query ?? {};\n const pageSize = Math.min(\n Number(query.pageSize) || entry.pageSize,\n 100, // Max page size\n );\n const cursor = query.cursor as string | undefined;\n const direction =\n (query.direction as string)?.toLowerCase() === \"prev\" ? \"prev\" : \"next\";\n const orderBy = query.orderBy as string | undefined;\n const orderDir =\n (query.orderDir as string)?.toLowerCase() === \"desc\" ? \"desc\" : \"asc\";\n const selectStr = query.select as string | undefined;\n const select = selectStr\n ? selectStr.split(\",\").map((s) => s.trim())\n : undefined;\n\n // Parse includes (relation population)\n let includes:\n | (string | { relation: string; select?: string[] })[]\n | undefined;\n if (entry.allowedIncludes && query.includes) {\n const rawIncludes =\n typeof query.includes === \"string\"\n ? query.includes.split(\",\").map((s: string) => s.trim())\n : Array.isArray(query.includes)\n ? query.includes\n : [];\n includes = rawIncludes.filter(\n (inc: string) =>\n typeof inc === \"string\" && entry.allowedIncludes!.includes(inc),\n );\n if (includes?.length === 0) includes = undefined;\n }\n\n // Parse filters\n const filters = parseFilters(query, entry.filterableFields);\n ctxFilters = filters.map((f) => ({\n field: f.field,\n op: f.op,\n value: String(f.value ?? \"\"),\n }));\n if (orderBy) ctxSort = { field: orderBy, dir: orderDir };\n\n // Build query options\n const queryOptions: any = {\n pageSize,\n direction,\n };\n\n if (cursor) {\n try {\n const cursorObj =\n typeof cursor === \"string\" ? JSON.parse(cursor) : cursor;\n queryOptions.cursor = await deserializeCursor(entry, cursorObj);\n } catch {\n // Invalid cursor, ignore\n }\n }\n\n if (orderBy) {\n queryOptions.orderBy = [{ field: orderBy, direction: orderDir }];\n }\n\n if (filters.length > 0) {\n queryOptions.where = filters.map((f) => [f.field, f.op, f.value]);\n }\n\n if (select) {\n queryOptions.select = select as any;\n }\n\n if (includes) {\n queryOptions.include = includes as any;\n }\n\n // Execute query\n const result = await entry.repo.query.paginate(queryOptions);\n\n const responseData: ListResponseData = {\n items: result.data,\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n nextCursor: serializeCursor(result.nextCursor),\n prevCursor: serializeCursor(result.prevCursor),\n };\n\n sendSuccess(res, responseData, {\n pageSize,\n hasMore: result.hasNextPage,\n });\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: ctxFilters,\n sort: ctxSort,\n },\n \"Failed to fetch documents\",\n verbose,\n );\n }\n }\n\n // ── QUERY: POST /:repoName/query ────────────────────────────────────────\n // Advanced query endpoint supporting OR conditions, array filters, etc.\n async function handleQuery(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n // Captured for error handling (so the catch can build an index URL)\n let ctxFilters: { field: string; op: any; value: string }[] = [];\n let ctxSort: { field: string; dir: \"asc\" | \"desc\" } | undefined;\n\n try {\n const body: QueryRequestBody = req.body ?? {};\n const pageSize = Math.min(body.pageSize || entry.pageSize, 100);\n const direction = body.direction === \"prev\" ? \"prev\" : \"next\";\n\n // Capture context for index URL fallback\n if (body.where) {\n ctxFilters = body.where.map((w) => ({\n field: String(w[0]),\n op: w[1] as any,\n value: String(w[2] ?? \"\"),\n }));\n }\n if (body.orderBy && body.orderBy[0]) {\n ctxSort = {\n field: body.orderBy[0].field,\n dir: body.orderBy[0].direction === \"desc\" ? \"desc\" : \"asc\",\n };\n }\n\n // Build query options\n const queryOptions: any = {\n pageSize,\n direction,\n };\n\n // Cursor\n if (body.cursor) {\n try {\n const cursorObj =\n typeof body.cursor === \"string\"\n ? JSON.parse(body.cursor)\n : body.cursor;\n queryOptions.cursor = await deserializeCursor(entry, cursorObj);\n } catch {\n // Invalid cursor, ignore\n }\n }\n\n // Includes (relation population)\n if (entry.allowedIncludes && body.includes && body.includes.length > 0) {\n const validIncludes = body.includes.filter((inc) => {\n if (typeof inc === \"string\") {\n return entry.allowedIncludes!.includes(inc);\n }\n if (\n typeof inc === \"object\" &&\n inc !== null &&\n \"relation\" in inc &&\n typeof inc.relation === \"string\"\n ) {\n return entry.allowedIncludes!.includes(inc.relation);\n }\n return false;\n });\n if (validIncludes.length > 0) {\n queryOptions.include = validIncludes as any;\n }\n }\n\n // Where conditions (AND)\n if (body.where && body.where.length > 0) {\n // Validate filterable fields if configured\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n const invalid = body.where.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n queryOptions.where = body.where;\n }\n\n // OR where conditions (simple)\n if (body.orWhere && body.orWhere.length > 0) {\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n const invalid = body.orWhere.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n queryOptions.orWhere = body.orWhere;\n }\n\n // OR where groups (advanced)\n if (body.orWhereGroups && body.orWhereGroups.length > 0) {\n if (entry.filterableFields) {\n const allowed = new Set(entry.filterableFields);\n for (const group of body.orWhereGroups) {\n const invalid = group.filter((w) => !allowed.has(w[0]));\n if (invalid.length > 0) {\n sendError(\n res,\n `Fields not filterable: ${invalid.map((w) => w[0]).join(\", \")}`,\n 400,\n );\n return;\n }\n }\n }\n queryOptions.orWhereGroups = body.orWhereGroups;\n }\n\n // Order by\n if (body.orderBy && body.orderBy.length > 0) {\n queryOptions.orderBy = body.orderBy;\n }\n\n // Select\n if (body.select && body.select.length > 0) {\n queryOptions.select = body.select;\n }\n\n // Execute query\n const result = await entry.repo.query.paginate(queryOptions);\n\n const responseData: ListResponseData = {\n items: result.data,\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n nextCursor: serializeCursor(result.nextCursor),\n prevCursor: serializeCursor(result.prevCursor),\n };\n\n sendSuccess(res, responseData, {\n pageSize,\n hasMore: result.hasNextPage,\n });\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: ctxFilters,\n sort: ctxSort,\n },\n \"Failed to query documents\",\n verbose,\n );\n }\n }\n\n // ── GET: GET /:repoName/:id ─────────────────────────────────────────────\n async function handleGet(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n const doc = await fetchDocById(entry, id);\n\n if (!doc) {\n sendError(res, \"Document not found\", 404);\n return;\n }\n\n sendSuccess(res, doc);\n } catch (err) {\n sendQueryError(\n res,\n err,\n {\n ref: entry.repo.ref,\n path: entry.path,\n isGroup: !!entry.isGroup,\n filters: [{ field: entry.documentKey, op: \"==\", value: id }],\n },\n \"Failed to fetch document\",\n verbose,\n );\n }\n }\n\n // ── CREATE: POST /:repoName ─────────────────────────────────────────────\n async function handleCreate(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n try {\n const body = req.body ?? {};\n\n // Validate against schema\n const validation = validateData(\n entry.schema,\n body,\n entry.createFields,\n false,\n entry.systemKeys,\n );\n if (!validation.success) {\n sendError(res, validation.error, 400);\n return;\n }\n\n // Custom validation\n if (entry.validate) {\n const customError = await entry.validate(validation.data, \"create\");\n if (customError) {\n sendError(res, customError, 400);\n return;\n }\n }\n\n // Create document\n let created: any;\n if (entry.isGroup && entry.parentKeys && entry.parentKeys.length > 0) {\n // Collection-group repos cannot use create(); use set() with parent path args.\n const data: Record<string, any> = { ...validation.data };\n // set() doesn't auto-set createdKey, so inject it here\n if (entry.createdKey) {\n data[entry.createdKey] = new Date();\n }\n const missingKeys = entry.parentKeys.filter((k) => !data[k]);\n if (missingKeys.length > 0) {\n sendError(\n res,\n `Missing parent key(s) for subcollection create: ${missingKeys.join(\", \")}`,\n 400,\n );\n return;\n }\n const parentIds = entry.parentKeys.map((k) => data[k] as string);\n const docId =\n data[entry.documentKey] || generateFirestoreId();\n created = await entry.repo.set(...parentIds, docId, data);\n } else {\n created = await entry.repo.create(validation.data as any);\n }\n\n sendSuccess(res, created, undefined, 201);\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to create document\";\n sendError(res, message, 500);\n }\n }\n\n // ── UPDATE: PUT/PATCH /:repoName/:id ────────────────────────────────────\n async function handleUpdate(\n req: any,\n res: any,\n partial: boolean,\n ): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n const body = req.body ?? {};\n\n // Validate against schema\n const validation = validateData(\n entry.schema,\n body,\n entry.mutableFields,\n partial,\n entry.systemKeys,\n );\n if (!validation.success) {\n sendError(res, validation.error, 400);\n return;\n }\n\n // Custom validation\n if (entry.validate) {\n const customError = await entry.validate(validation.data, \"update\");\n if (customError) {\n sendError(res, customError, 400);\n return;\n }\n }\n\n // Update document — fetch first to get path args for subcollections\n const existingDoc = await fetchDocById(entry, id);\n const pathArgs =\n (existingDoc && extractPathArgs(existingDoc, entry.pathKey)) ?? [id];\n const updated = await entry.repo.update(\n ...pathArgs,\n validation.data as any,\n );\n\n sendSuccess(res, updated);\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to update document\";\n sendError(res, message, 500);\n }\n }\n\n // ── DELETE: DELETE /:repoName/:id ───────────────────────────────────────\n async function handleDelete(req: any, res: any): Promise<void> {\n const params = req.params || {};\n const entry = getRepoEntry(params.repoName, res);\n if (!entry) return;\n\n if (!entry.allowDelete) {\n sendError(res, \"Delete not allowed for this repository\", 403);\n return;\n }\n\n const id = params.id;\n if (!id) {\n sendError(res, \"Document ID required\", 400);\n return;\n }\n\n try {\n // Fetch first to get path args for subcollections\n const doc = await fetchDocById(entry, id);\n const pathArgs =\n (doc && extractPathArgs(doc, entry.pathKey)) ?? [id];\n await entry.repo.delete(...pathArgs);\n sendSuccess(res, { deleted: true });\n } catch (err) {\n const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to delete document\";\n sendError(res, message, 500);\n }\n }\n\n // ── OPTIONS: for CORS preflight ─────────────────────────────────────────\n function handleOptions(req: any, res: any): void {\n res\n .status(204)\n .set(\n \"Access-Control-Allow-Methods\",\n \"GET, POST, PUT, PATCH, DELETE, OPTIONS\",\n )\n .set(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n .set(\"Access-Control-Max-Age\", \"86400\")\n .send(\"\");\n }\n\n return {\n handleList,\n handleQuery,\n handleGet,\n handleCreate,\n handleUpdate,\n handleDelete,\n handleOptions,\n };\n}\n","/**\n * OpenAPI 3.1 specification generator for the CRUD server.\n *\n * Introspects each `CrudRepoEntry` and uses Zod 4's native `z.toJSONSchema()`\n * to produce a fully typed OpenAPI document ready for Scalar UI or codegen.\n *\n * @module servers/crud/openapi\n */\n\nimport { z } from \"zod\";\nimport type { CrudRepoEntry, CrudRepoRegistry } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Minimal subset of an OpenAPI 3.1 document we produce. */\nexport interface OpenAPIDocument {\n openapi: \"3.1.0\";\n info: OpenAPIInfo;\n servers?: { url: string; description?: string }[];\n paths: Record<string, Record<string, OpenAPIOperation>>;\n components: {\n schemas: Record<string, Record<string, unknown>>;\n securitySchemes?: Record<string, Record<string, unknown>>;\n };\n security?: Record<string, string[]>[];\n tags?: { name: string; description?: string }[];\n}\n\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\nexport interface OpenAPISpecOptions {\n /** Document title (default: \"CRUD API\") */\n title?: string;\n /** API version (default: \"1.0.0\") */\n version?: string;\n /** Description shown in Scalar UI / Swagger */\n description?: string;\n /** Server URLs */\n servers?: { url: string; description?: string }[];\n /** Whether the API requires auth — adds securitySchemes */\n auth?: \"basic\" | \"bearer\" | false;\n}\n\ninterface OpenAPIOperation {\n operationId: string;\n summary: string;\n tags: string[];\n parameters?: Record<string, unknown>[];\n requestBody?: Record<string, unknown>;\n responses: Record<string, Record<string, unknown>>;\n security?: Record<string, string[]>[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a Zod schema to a JSON Schema object suitable for OpenAPI 3.1.\n *\n * Uses `unrepresentable: \"any\"` so that types Zod can't natively serialize\n * (e.g. `z.date()`, `z.bigint()`, custom classes — common with Firestore\n * Timestamps stored as `Date`) don't throw and collapse the whole schema to\n * a bare `{ type: \"object\" }`. We additionally override a few well-known\n * cases to a sensible OpenAPI representation.\n */\nfunction zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {\n try {\n return z.toJSONSchema(schema, {\n target: \"openapi-3.1\",\n unrepresentable: \"any\",\n override: (ctx) => {\n const def: any = (ctx.zodSchema as any)?._zod?.def;\n if (!def) return;\n if (def.type === \"date\") {\n (ctx.jsonSchema as any).type = \"string\";\n (ctx.jsonSchema as any).format = \"date-time\";\n } else if (def.type === \"bigint\") {\n (ctx.jsonSchema as any).type = \"string\";\n (ctx.jsonSchema as any).format = \"int64\";\n }\n },\n }) as Record<string, unknown>;\n } catch (err) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n \"[generateOpenAPISpec] Failed to convert Zod schema to JSON Schema; falling back to {type:object}.\",\n err,\n );\n }\n return { type: \"object\" };\n }\n}\n\n/** Wraps a JSON Schema in a `#/components/schemas/<name>` $ref. */\nfunction schemaRef(name: string): Record<string, unknown> {\n return { $ref: `#/components/schemas/${name}` };\n}\n\n/** Standard error response schema. */\nfunction errorResponse(description: string): Record<string, unknown> {\n return {\n description,\n content: {\n \"application/json\": {\n schema: schemaRef(\"ErrorResponse\"),\n },\n },\n };\n}\n\n/** Standard success response wrapping data. */\nfunction successResponse(\n description: string,\n dataSchema: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n description,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [true] },\n data: dataSchema,\n },\n required: [\"success\", \"data\"],\n },\n },\n },\n };\n}\n\n/** Build list response with pagination metadata. */\nfunction listResponse(\n itemSchema: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n description: \"Paginated list of documents\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [true] },\n data: {\n type: \"object\",\n properties: {\n items: { type: \"array\", items: itemSchema },\n nextCursor: {\n oneOf: [{ type: \"object\" }, { type: \"null\" }],\n },\n prevCursor: {\n oneOf: [{ type: \"object\" }, { type: \"null\" }],\n },\n hasNextPage: { type: \"boolean\" },\n hasPrevPage: { type: \"boolean\" },\n },\n required: [\"items\", \"hasNextPage\", \"hasPrevPage\"],\n },\n meta: {\n type: \"object\",\n properties: {\n pageSize: { type: \"integer\" },\n hasMore: { type: \"boolean\" },\n cursor: {\n oneOf: [{ type: \"string\" }, { type: \"null\" }],\n },\n },\n },\n },\n required: [\"success\", \"data\"],\n },\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pagination / filter query parameters (shared across list endpoints)\n// ---------------------------------------------------------------------------\n\nfunction paginationParams(entry: CrudRepoEntry): Record<string, unknown>[] {\n return [\n {\n name: \"pageSize\",\n in: \"query\",\n schema: { type: \"integer\", default: entry.pageSize, maximum: 100 },\n description: \"Number of items per page\",\n },\n {\n name: \"cursor\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Base64 pagination cursor\",\n },\n {\n name: \"orderBy\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Field name to order by\",\n },\n {\n name: \"orderDir\",\n in: \"query\",\n schema: { type: \"string\", enum: [\"asc\", \"desc\"] },\n description: \"Order direction\",\n },\n {\n name: \"select\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of fields to return\",\n },\n ];\n}\n\nfunction filterParams(entry: CrudRepoEntry): Record<string, unknown>[] {\n const fields = entry.filterableFields ?? Object.keys(entry.schema.shape);\n const ops = [\"eq\", \"ne\", \"lt\", \"lte\", \"gt\", \"gte\", \"in\", \"nin\", \"contains\"];\n\n const params: Record<string, unknown>[] = [];\n for (const field of fields) {\n // Direct equality filter: ?field=value\n params.push({\n name: field,\n in: \"query\",\n schema: { type: \"string\" },\n description: `Filter by ${field} (equality)`,\n });\n // Operator filters: ?field__op=value\n for (const op of ops) {\n params.push({\n name: `${field}__${op}`,\n in: \"query\",\n schema: { type: \"string\" },\n description: `Filter ${field} with operator ${op}`,\n });\n }\n }\n return params;\n}\n\n// ---------------------------------------------------------------------------\n// Query body schema (POST /query)\n// ---------------------------------------------------------------------------\n\nfunction queryBodySchema(): Record<string, unknown> {\n return {\n type: \"object\",\n properties: {\n where: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n description: \"AND conditions: [field, operator, value][]\",\n },\n orWhere: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n description: \"Simple OR conditions (each independently OR'd)\",\n },\n orWhereGroups: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {\n type: \"array\",\n items: {},\n minItems: 3,\n maxItems: 3,\n },\n },\n description: \"Advanced OR groups (AND within, OR across groups)\",\n },\n orderBy: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n field: { type: \"string\" },\n direction: { type: \"string\", enum: [\"asc\", \"desc\"] },\n },\n required: [\"field\"],\n },\n },\n select: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Fields to select (projection)\",\n },\n pageSize: {\n type: \"integer\",\n maximum: 100,\n description: \"Number of items per page\",\n },\n cursor: {\n oneOf: [{ type: \"string\" }, { type: \"object\" }],\n description: \"Pagination cursor\",\n },\n direction: {\n type: \"string\",\n enum: [\"next\", \"prev\"],\n description: \"Pagination direction\",\n },\n includes: {\n type: \"array\",\n items: {\n oneOf: [\n { type: \"string\" },\n {\n type: \"object\",\n properties: {\n relation: { type: \"string\" },\n select: { type: \"array\", items: { type: \"string\" } },\n },\n required: [\"relation\"],\n },\n ],\n },\n description: \"Relations to include (populate)\",\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Path generation per repo entry\n// ---------------------------------------------------------------------------\n\nfunction buildPathsForEntry(\n entry: CrudRepoEntry,\n base: string,\n modelSchemaName: string,\n createSchemaName: string | null,\n updateSchemaName: string | null,\n): Record<string, Record<string, OpenAPIOperation>> {\n const paths: Record<string, Record<string, OpenAPIOperation>> = {};\n const tag = entry.name;\n const collectionPath = `${base}/${entry.name}`;\n const documentPath = `${collectionPath}/{${entry.documentKey}}`;\n\n const idParam = {\n name: entry.documentKey,\n in: \"path\",\n required: true,\n schema: { type: \"string\" },\n description: `Unique document identifier`,\n };\n\n // ── GET /:repo → list ──────────────────────────────────────────────\n paths[collectionPath] = {\n get: {\n operationId: `list${capitalize(entry.name)}`,\n summary: `List ${entry.name} (paginated)`,\n tags: [tag],\n parameters: [...paginationParams(entry), ...filterParams(entry)],\n responses: {\n \"200\": listResponse(schemaRef(modelSchemaName)),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n // ── POST /:repo → create ────────────────────────────────────────\n post: {\n operationId: `create${capitalize(entry.name)}`,\n summary: `Create a ${singularize(entry.name)}`,\n tags: [tag],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(createSchemaName ?? modelSchemaName),\n },\n },\n },\n responses: {\n \"201\": successResponse(\"Document created\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n };\n\n // ── POST /:repo/query → advanced query ────────────────────────────\n paths[`${collectionPath}/query`] = {\n post: {\n operationId: `query${capitalize(entry.name)}`,\n summary: `Query ${entry.name} with advanced filters`,\n tags: [tag],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(\"QueryRequestBody\"),\n },\n },\n },\n responses: {\n \"200\": listResponse(schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Invalid query\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n },\n };\n\n // ── Single-document paths ────────────────────────────────────────\n const docOps: Record<string, OpenAPIOperation> = {};\n\n // GET /:repo/:id\n docOps.get = {\n operationId: `get${capitalize(singularize(entry.name))}`,\n summary: `Get a single ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n responses: {\n \"200\": successResponse(\"Document found\", schemaRef(modelSchemaName)),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // PUT /:repo/:id (full update)\n docOps.put = {\n operationId: `update${capitalize(singularize(entry.name))}`,\n summary: `Update a ${singularize(entry.name)} (full replace)`,\n tags: [tag],\n parameters: [idParam],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: schemaRef(updateSchemaName ?? modelSchemaName),\n },\n },\n },\n responses: {\n \"200\": successResponse(\"Document updated\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // PATCH /:repo/:id (partial update)\n docOps.patch = {\n operationId: `patch${capitalize(singularize(entry.name))}`,\n summary: `Partially update a ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: {\n allOf: [schemaRef(updateSchemaName ?? modelSchemaName)],\n description: \"All fields are optional for partial updates\",\n },\n },\n },\n },\n responses: {\n \"200\": successResponse(\"Document patched\", schemaRef(modelSchemaName)),\n \"400\": errorResponse(\"Validation error\"),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n\n // DELETE /:repo/:id (only if allowDelete)\n if (entry.allowDelete) {\n docOps.delete = {\n operationId: `delete${capitalize(singularize(entry.name))}`,\n summary: `Delete a ${singularize(entry.name)}`,\n tags: [tag],\n parameters: [idParam],\n responses: {\n \"200\": successResponse(\"Document deleted\", {\n type: \"object\",\n properties: { id: { type: \"string\" } },\n }),\n \"404\": errorResponse(\"Document not found\"),\n \"500\": errorResponse(\"Internal server error\"),\n },\n };\n }\n\n paths[documentPath] = docOps;\n\n return paths;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a full OpenAPI 3.1 specification from a `CrudRepoRegistry`.\n *\n * Uses Zod 4's native `z.toJSONSchema()` to convert each repo's schema\n * into a JSON Schema component, then assembles paths for the standard\n * CRUD endpoints.\n *\n * @example\n * ```ts\n * import { generateOpenAPISpec } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * const spec = generateOpenAPISpec(registry, \"/api\", {\n * title: \"My API\",\n * version: \"1.0.0\",\n * servers: [{ url: \"https://my-api.example.com\" }],\n * auth: \"bearer\",\n * });\n *\n * // Write to file\n * fs.writeFileSync(\"openapi.json\", JSON.stringify(spec, null, 2));\n * ```\n */\nexport function generateOpenAPISpec(\n registry: CrudRepoRegistry,\n basePath: string,\n options: OpenAPISpecOptions = {},\n): OpenAPIDocument {\n const {\n title = \"CRUD API\",\n version = \"1.0.0\",\n description,\n servers,\n auth = false,\n } = options;\n\n const base = basePath === \"/\" ? \"\" : basePath.replace(/\\/$/, \"\");\n\n // ── Components: schemas ───────────────────────────────────────────\n const schemas: Record<string, Record<string, unknown>> = {};\n const allPaths: Record<string, Record<string, OpenAPIOperation>> = {};\n const tags: { name: string; description?: string }[] = [];\n\n // Shared schemas\n schemas[\"ErrorResponse\"] = {\n type: \"object\",\n properties: {\n success: { type: \"boolean\", enum: [false] },\n error: { type: \"string\" },\n },\n required: [\"success\", \"error\"],\n };\n\n schemas[\"QueryRequestBody\"] = queryBodySchema();\n\n // Per-repo schemas & paths\n for (const [name, entry] of Object.entries(registry)) {\n const modelName = capitalize(singularize(name));\n const createName = `${modelName}Create`;\n const updateName = `${modelName}Update`;\n\n // Full model schema\n schemas[modelName] = zodToJsonSchema(entry.schema);\n\n // Helper: build a filtered shape (respects systemKeys + field list)\n const buildShape = (\n fieldList: string[] | undefined,\n ): Record<string, z.ZodType> => {\n const source =\n fieldList && fieldList.length > 0\n ? fieldList\n : Object.keys(entry.schema.shape);\n const shape: Record<string, z.ZodType> = {};\n for (const f of source) {\n const top = f.split(\".\")[0];\n if (top && entry.schema.shape[top] && !entry.systemKeys.includes(top)) {\n shape[top] = entry.schema.shape[top];\n }\n }\n return shape;\n };\n\n // Create schema\n let createSchemaName: string | null = null;\n const createShape = buildShape(entry.createFields);\n if (Object.keys(createShape).length > 0) {\n schemas[createName] = zodToJsonSchema(z.object(createShape));\n createSchemaName = createName;\n }\n\n // Update schema\n let updateSchemaName: string | null = null;\n const updateShape = buildShape(entry.mutableFields);\n if (Object.keys(updateShape).length > 0) {\n schemas[updateName] = zodToJsonSchema(z.object(updateShape));\n updateSchemaName = updateName;\n }\n\n // Build paths\n const entryPaths = buildPathsForEntry(\n entry,\n base,\n modelName,\n createSchemaName,\n updateSchemaName,\n );\n Object.assign(allPaths, entryPaths);\n\n // Tag\n tags.push({\n name,\n description: `Operations on ${name} (collection: ${entry.path})`,\n });\n }\n\n // ── Security ──────────────────────────────────────────────────────\n const securitySchemes: Record<string, Record<string, unknown>> = {};\n let security: Record<string, string[]>[] | undefined;\n\n if (auth === \"basic\") {\n securitySchemes[\"basicAuth\"] = {\n type: \"http\",\n scheme: \"basic\",\n };\n security = [{ basicAuth: [] }];\n } else if (auth === \"bearer\") {\n securitySchemes[\"bearerAuth\"] = {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n };\n security = [{ bearerAuth: [] }];\n }\n\n // ── Assemble ──────────────────────────────────────────────────────\n const doc: OpenAPIDocument = {\n openapi: \"3.1.0\",\n info: {\n title,\n version,\n ...(description ? { description } : {}),\n },\n ...(servers && servers.length > 0 ? { servers } : {}),\n paths: allPaths,\n components: {\n schemas,\n ...(Object.keys(securitySchemes).length > 0 ? { securitySchemes } : {}),\n },\n ...(security ? { security } : {}),\n tags,\n };\n\n return doc;\n}\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n/** Naive singularize: strip trailing 's' for display. */\nfunction singularize(s: string): string {\n if (s.endsWith(\"ies\")) return s.slice(0, -3) + \"y\";\n if (s.endsWith(\"ses\") || s.endsWith(\"xes\") || s.endsWith(\"zes\"))\n return s.slice(0, -2);\n if (s.endsWith(\"s\") && !s.endsWith(\"ss\")) return s.slice(0, -1);\n return s;\n}\n","/**\n * @module servers/crud\n *\n * Creates a REST API server for CRUD operations on Firestore repositories.\n *\n * Features:\n * - RESTful endpoints for List, Get, Create, Update, Delete\n * - Request validation using Zod schemas\n * - Cursor-based pagination\n * - Query filtering with operators (eq, ne, lt, gt, in, etc.)\n * - Field selection\n * - CORS support\n * - Configurable auth (Basic Auth or custom middleware)\n *\n * @example\n * ```ts\n * import * as functions from \"firebase-functions\";\n * import { z } from \"zod\";\n * import { createCrudServer } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * const postSchema = z.object({\n * title: z.string().min(1),\n * content: z.string(),\n * status: z.enum([\"draft\", \"published\"]),\n * authorId: z.string(),\n * });\n *\n * export const api = functions.https.onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * repos: {\n * posts: {\n * repo: repos.posts,\n * schema: postSchema,\n * path: \"posts\",\n * fieldsConfig: {\n * status: [\"filterable\"],\n * authorId: [\"filterable\"],\n * },\n * allowDelete: true,\n * },\n * },\n * })\n * );\n * ```\n *\n * ## API Endpoints\n *\n * | Method | Path | Description |\n * |--------|-------------------|--------------------------|\n * | GET | /:repo | List documents (paginated) |\n * | GET | /:repo/:id | Get single document |\n * | POST | /:repo | Create document |\n * | PUT | /:repo/:id | Update document (full) |\n * | PATCH | /:repo/:id | Update document (partial)|\n * | DELETE | /:repo/:id | Delete document |\n *\n * ## Query Parameters (GET list)\n *\n * | Param | Description |\n * |------------|------------------------------------------|\n * | pageSize | Number of items per page (max 100) |\n * | cursor | Base64 pagination cursor |\n * | orderBy | Field to order by |\n * | orderDir | Order direction (asc/desc) |\n * | select | Comma-separated fields to return |\n * | field | Filter by field (field=value) |\n * | field__op | Filter with operator (field__gt=10) |\n *\n * ## Filter Operators\n *\n * | Suffix | Firestore Op | Example |\n * |-------------|-------------------|-----------------------|\n * | (none) | == | status=active |\n * | __eq | == | status__eq=active |\n * | __ne | != | status__ne=draft |\n * | __lt | < | age__lt=18 |\n * | __lte | <= | age__lte=18 |\n * | __gt | > | age__gt=18 |\n * | __gte | >= | age__gte=18 |\n * | __in | in | status__in=a,b,c |\n * | __nin | not-in | status__nin=x,y |\n * | __contains | array-contains | tags__contains=news |\n */\n\nimport { MiniRouter } from \"../admin/router\";\nimport type { HttpsOptions } from \"firebase-functions/v2/https\";\nimport type { HttpRequest, HttpResponse } from \"../http-types\";\nimport type { ConfiguredRepository } from \"../../repositories/types\";\nimport { createCrudHandlers } from \"./handlers\";\nimport { generateOpenAPISpec, type OpenAPIDocument } from \"./openapi\";\nimport type {\n CrudRepoEntry,\n CrudRepoRegistry,\n CrudServerOptions,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Scalar API docs HTML template\n// ---------------------------------------------------------------------------\n\n/** Returns a self-contained HTML page using Scalar to render the spec. */\nfunction scalarDocsHtml(title: string, specUrl: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${title}</title>\n</head>\n<body>\n <script id=\"api-reference\" data-url=\"${specUrl}\"></script>\n <script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n\n/**\n * Compute the URL prefix for links / spec URLs.\n * In the Firebase emulator the /{project}/{region}/{functionTarget} prefix\n * is visible in URLs but stripped before the handler receives `req.url`.\n * In production Firebase proxy strips it automatically.\n */\nfunction getLinkBase(req: any, staticBasePath: string): string {\n const base = staticBasePath === \"/\" ? \"\" : staticBasePath.replace(/\\/$/, \"\");\n\n if (process.env[\"FUNCTIONS_EMULATOR\"] === \"true\") {\n const project =\n process.env[\"GCLOUD_PROJECT\"] ??\n process.env[\"GOOGLE_CLOUD_PROJECT\"] ??\n \"demo-project\";\n const region = process.env[\"FUNCTION_REGION\"] ?? \"us-central1\";\n const target = process.env[\"FUNCTION_TARGET\"] ?? \"\";\n return `/${project}/${region}/${target}${base}`;\n }\n\n // Cloud Functions v2: K_SERVICE = function name = URL path prefix.\n // Only add it when accessed via cloudfunctions.net (not custom domains).\n // Cloud Run (Gen 2) lowercases service names, but K_SERVICE may still\n // carry the original mixed-case export name — normalise to lowercase\n // so that generated links match the canonical URL.\n const service = process.env[\"K_SERVICE\"];\n const host: string = req?.hostname ?? req?.headers?.[\"host\"] ?? \"\";\n if (service && host.includes(\"cloudfunctions.net\")) {\n return `/${service.toLowerCase()}${base}`;\n }\n\n return base;\n}\n\n// ---------------------------------------------------------------------------\n// Body parser\n// ---------------------------------------------------------------------------\n\n/** Eagerly reads the raw request body as a string */\nasync function readRawBody(req: HttpRequest): Promise<string> {\n if (typeof (req as any).rawBody === \"string\")\n return (req as any).rawBody as string;\n if (Buffer.isBuffer((req as any).rawBody))\n return ((req as any).rawBody as Buffer).toString(\"utf8\");\n return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an Express-compatible request handler for a REST CRUD API.\n *\n * @template TRepos - Shape of the repos map (inferred automatically)\n * @param options - CRUD server configuration\n * @returns Express-compatible request handler for Firebase Functions\n *\n * @example\n * ```typescript\n * // Basic CRUD server\n * import { onRequest } from \"firebase-functions/https\";\n * import { createCrudServer } from \"@lpdjs/firestore-repo-service/servers/crud\";\n *\n * export const api = onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * repos: {\n * users: {\n * repo: repos.users,\n * path: \"users\",\n * fieldsConfig: {\n * name: [\"create\", \"mutable\"],\n * email: [\"create\", \"mutable\", \"filterable\"],\n * isActive: [\"filterable\"],\n * },\n * allowDelete: false,\n * },\n * posts: {\n * repo: repos.posts,\n * path: \"posts\",\n * fieldsConfig: {\n * status: [\"filterable\"],\n * userId: [\"filterable\"],\n * },\n * allowDelete: true,\n * },\n * },\n * })\n * );\n *\n * // With authentication\n * export const api = onRequest(\n * createCrudServer({\n * basePath: \"/api\",\n * auth: {\n * type: \"basic\",\n * username: \"api\",\n * password: process.env.API_PASSWORD!,\n * },\n * repos: { ... },\n * })\n * );\n *\n * // With custom auth middleware\n * export const api = onRequest(\n * createCrudServer({\n * auth: async (req, res, next) => {\n * const token = req.headers?.authorization?.replace(\"Bearer \", \"\");\n * if (!token || !(await verifyToken(token))) {\n * res.status(401).json({ success: false, error: \"Unauthorized\" });\n * return;\n * }\n * next();\n * },\n * repos: { ... },\n * })\n * );\n *\n * // With custom validation\n * export const api = onRequest(\n * createCrudServer({\n * repos: {\n * posts: {\n * repo: repos.posts,\n * path: \"posts\",\n * validate: async (data, operation) => {\n * if (operation === \"create\" && !data.title) {\n * return \"Title is required\";\n * }\n * return undefined;\n * },\n * },\n * },\n * })\n * );\n * ```\n */\nexport function createCrudServer<\n TRepos extends Record<string, ConfiguredRepository<any>>,\n>(\n options: CrudServerOptions<TRepos>,\n): ((req: any, res: any) => Promise<void>) & { spec: () => OpenAPIDocument; httpsOptions?: HttpsOptions } {\n const {\n basePath = \"/\",\n repos,\n parseBody = true,\n auth,\n middleware: extraMiddleware = [],\n verbose = false,\n httpsOptions,\n } = options;\n\n // Normalise basePath: no trailing slash\n const base = basePath === \"/\" ? \"\" : basePath.replace(/\\/$/, \"\");\n\n // Build the registry\n const registry: CrudRepoRegistry = {};\n for (const [name, cfg] of Object.entries(repos)) {\n // Schema resolution: explicit cfg.schema > embedded in repo (createRepositoryConfig(schema))\n const resolvedSchema = cfg.schema ?? (cfg.repo as any).schema ?? null;\n if (!resolvedSchema) {\n throw new Error(\n `[createCrudServer] Repository \"${name}\" has no Zod schema. ` +\n `Either use createRepositoryConfig(schema)(config) or pass schema: explicitly.`,\n );\n }\n\n // Resolve fieldsConfig → separate arrays for runtime\n let filterableFields: string[] | undefined;\n let mutableFields: string[] | undefined;\n let createFields: string[] | undefined;\n if (cfg.fieldsConfig) {\n const fc = cfg.fieldsConfig as Record<string, readonly string[]>;\n filterableFields = [];\n mutableFields = [];\n createFields = [];\n for (const [field, roles] of Object.entries(fc)) {\n for (const role of roles) {\n if (role === \"filterable\") filterableFields.push(field);\n else if (role === \"mutable\") mutableFields.push(field);\n else if (role === \"create\") createFields.push(field);\n }\n }\n if (filterableFields.length === 0) filterableFields = undefined;\n if (mutableFields.length === 0) mutableFields = undefined;\n if (createFields.length === 0) createFields = undefined;\n }\n\n // For collection-group repos, ensure parentKeys are included in createFields\n // so the validation accepts them in the request body.\n const parentKeys = (() => {\n const pk = (cfg.repo as any)._parentKeys as string[] | undefined;\n return pk && pk.length > 0 ? pk : undefined;\n })();\n if (parentKeys && createFields) {\n for (const pk of parentKeys) {\n if (!createFields.includes(pk)) createFields.push(pk);\n }\n }\n\n const entry: CrudRepoEntry = {\n name,\n path: cfg.path,\n repo: cfg.repo,\n schema: resolvedSchema,\n systemKeys: (cfg.repo as any)._systemKeys ?? [cfg.documentKey ?? \"docId\"],\n documentKey: cfg.documentKey ?? \"docId\",\n pathKey: (cfg.repo as any)._pathKey ?? undefined,\n isGroup: !!(cfg.repo as any)._isGroup,\n parentKeys,\n createdKey: (cfg.repo as any)._createdKey ?? undefined,\n pageSize: cfg.pageSize ?? 25,\n filterableFields,\n mutableFields,\n createFields,\n allowDelete: cfg.allowDelete ?? false,\n allowedIncludes: cfg.allowedIncludes as string[] | undefined,\n validate: cfg.validate as CrudRepoEntry[\"validate\"],\n };\n\n registry[name] = entry;\n }\n\n const handlers = createCrudHandlers(registry, base, verbose);\n\n // ── OpenAPI spec (cached) ─────────────────────────────────────────────\n const openapi = options.openapi;\n const openapiOpts = openapi && typeof openapi === \"object\" ? openapi : {};\n let _specCache: OpenAPIDocument | null = null;\n function getSpec(): OpenAPIDocument {\n if (!_specCache) {\n const authType =\n auth && typeof auth !== \"function\"\n ? (\"basic\" as const)\n : auth\n ? (\"bearer\" as const)\n : false;\n _specCache = generateOpenAPISpec(registry, base, {\n ...openapiOpts,\n auth: openapiOpts.auth ?? authType,\n });\n }\n return _specCache;\n }\n\n // ── Router ─────────────────────────────────────────────────────────────\n const router = new MiniRouter();\n\n // ── CORS middleware ─────────────────────────────────────────────────────\n router.use((req, res, next) => {\n res.set(\"Access-Control-Allow-Origin\", \"*\");\n res.set(\"Access-Control-Allow-Credentials\", \"true\");\n next();\n });\n\n // ── 1. Body-parsing middleware ──────────────────────────────────────────\n if (parseBody) {\n router.use(async (req, _res, next) => {\n const r = req as unknown as HttpRequest;\n const contentType = String(r.headers?.[\"content-type\"] ?? \"\");\n if (contentType.includes(\"application/json\")) {\n if (typeof r.body === \"string\") {\n try {\n (req as any).body = JSON.parse(r.body);\n } catch {\n /* keep as string */\n }\n } else if (Buffer.isBuffer((req as any).rawBody)) {\n try {\n const raw = await readRawBody(r);\n (req as any).body = JSON.parse(raw);\n } catch {\n /* keep as is */\n }\n }\n }\n await next();\n });\n }\n\n // ── 2. Auth middleware ──────────────────────────────────────────────────\n if (auth) {\n if (typeof auth === \"function\") {\n // Custom middleware\n router.use(auth);\n } else {\n // HTTP Basic Auth\n const realm = auth.realm ?? \"API\";\n const expected =\n \"Basic \" +\n Buffer.from(`${auth.username}:${auth.password}`).toString(\"base64\");\n router.use((req, res, next) => {\n const authorization = (req as any).headers?.[\"authorization\"] ?? \"\";\n if (authorization !== expected) {\n res\n .status(401)\n .set(\"WWW-Authenticate\", `Basic realm=\"${realm}\"`)\n .set(\"Content-Type\", \"application/json\")\n .send(JSON.stringify({ success: false, error: \"Unauthorized\" }));\n return;\n }\n next();\n });\n }\n }\n\n // ── 3. Extra user middleware ────────────────────────────────────────────\n for (const mw of extraMiddleware) {\n router.use(mw);\n }\n\n // ── 4. Routes ─────────────────────────────────────────────────────────────\n\n // ── OpenAPI spec & docs endpoints (before auth so they're public) ────\n if (openapi !== false) {\n const specPath = `${base}/__spec.json`;\n const docsPath = `${base}/__docs`;\n\n router.get(specPath, (_req: any, res: any) => {\n const spec = getSpec();\n res\n .status(200)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(spec, null, 2));\n });\n\n router.get(docsPath, (req: any, res: any) => {\n // Rebuild spec URL with the correct prefix for the current context\n // (emulator, Cloud Functions URL, or custom domain).\n const specUrl = getLinkBase(req, base) + \"/__spec.json\";\n const html = scalarDocsHtml(openapiOpts.title ?? \"CRUD API\", specUrl);\n res\n .status(200)\n .set(\"Content-Type\", \"text/html; charset=utf-8\")\n .send(html);\n });\n }\n\n // OPTIONS for CORS preflight\n router.use((req, res, next) => {\n if (req.method === \"OPTIONS\") {\n handlers.handleOptions(req, res);\n return;\n }\n next();\n });\n\n // List: GET /:repoName\n router.get(`${base}/:repoName`, handlers.handleList);\n\n // Query: POST /:repoName/query (advanced filtering with body)\n router.post(`${base}/:repoName/query`, handlers.handleQuery);\n\n // Get: GET /:repoName/:id\n router.get(`${base}/:repoName/:id`, handlers.handleGet);\n\n // Create: POST /:repoName\n router.post(`${base}/:repoName`, handlers.handleCreate);\n\n // Update (full): PUT /:repoName/:id\n router.put(`${base}/:repoName/:id`, (req: any, res: any) =>\n handlers.handleUpdate(req, res, false),\n );\n\n // Update (partial): PATCH /:repoName/:id\n router.patch(`${base}/:repoName/:id`, (req: any, res: any) =>\n handlers.handleUpdate(req, res, true),\n );\n\n // Delete: DELETE /:repoName/:id\n router.delete(`${base}/:repoName/:id`, handlers.handleDelete);\n\n // ── Request handler ─────────────────────────────────────────────────────\n const handler = async (\n req: HttpRequest,\n res: HttpResponse,\n ): Promise<void> => {\n await router.handle(req as any, res as any);\n };\n\n // Attach spec getter so users can call server.spec() programmatically\n (handler as any).spec = getSpec;\n if (httpsOptions) (handler as any).httpsOptions = httpsOptions;\n\n return handler as ((req: any, res: any) => Promise<void>) & {\n /** Return the generated OpenAPI 3.1 document. */\n spec: () => OpenAPIDocument;\n /** Options to forward to `onRequest()` from firebase-functions. */\n httpsOptions?: HttpsOptions;\n };\n}\n\n// Re-exports for convenience\nexport { generateOpenAPISpec } from \"./openapi\";\nexport type { OpenAPIDocument, OpenAPISpecOptions } from \"./openapi\";\nexport type {\n ApiResponse,\n BasicAuthConfig,\n CrudRepoConfig,\n CrudRepoEntry,\n CrudRepoRegistry,\n CrudServerOptions,\n FieldRole,\n ListResponseData,\n Middleware,\n QueryRequestBody,\n RepoFieldPath,\n RepoRelationKeys,\n UserFieldPath,\n} from \"./types\";\n"]}