@lpdjs/firestore-repo-service 2.1.2 → 2.1.3

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/servers/crud/handlers.ts","../../../src/servers/crud/openapi.ts","../../../src/servers/crud/index.ts"],"names":["compilePath","path","paramNames","src","_match","name","extractPath","req","raw","idx","MiniRouter","_req","res","err","middleware","handler","method","pattern","matchedRoute","params","route","m","i","enrichedReq","finalHandler","index","next","mw","sendJson","data","status","sendSuccess","meta","sendError","error","_idChars","generateFirestoreId","id","pickSchemaFields","schema","fields","systemKeys","shape","picked","source","field","topLevel","z","validateData","partial","targetSchema","e","parseFilters","query","filterableFields","filters","allowedFields","opMap","key","rawVal","val","match","op","opKey","parsedVal","v","parseValue","num","serializeCursor","snapshot","deserializeCursor","entry","cursor","docId","colRef","createCrudHandlers","registry","basePath","verbose","getRepoEntry","repoName","extractPathArgs","doc","pathKey","fullPath","segments","args","fetchDocById","getterName","getter","handleList","pageSize","direction","orderBy","orderDir","selectStr","select","s","includes","inc","queryOptions","cursorObj","f","result","responseData","message","handleQuery","body","validIncludes","allowed","invalid","w","group","handleGet","handleCreate","validation","customError","created","missingKeys","k","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","readRawBody","createCrudServer","repos","parseBody","extraMiddleware","httpsOptions","cfg","resolvedSchema","mutableFields","createFields","fc","roles","role","parentKeys","pk","handlers","openapi","openapiOpts","_specCache","getSpec","authType","router","_res","r","realm","expected","specPath","docsPath","spec","html"],"mappings":"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","file":"index.js","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","/**\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 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 res\n .status(status)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(data));\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// 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 * 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 finalSchema = partial ? targetSchema.partial() : targetSchema;\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 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\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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to fetch documents\";\n sendError(res, message, 500);\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 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 // 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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to query documents\";\n sendError(res, message, 500);\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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to fetch document\";\n sendError(res, message, 500);\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/** Convert a Zod schema to a JSON Schema object suitable for OpenAPI 3.1. */\nfunction zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {\n try {\n return z.toJSONSchema(schema, { target: \"openapi-3.1\" }) as Record<\n string,\n unknown\n >;\n } catch {\n // Fallback for unsupported types\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(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 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 Firebase Functions prefix when running\n // in the emulator so Scalar can fetch the spec correctly.\n const specUrl = getLinkBase(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/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","sendJson","data","status","sendSuccess","meta","sendError","error","_idChars","generateFirestoreId","id","pickSchemaFields","schema","fields","systemKeys","shape","picked","source","field","topLevel","z","validateData","partial","targetSchema","e","parseFilters","query","filterableFields","filters","allowedFields","opMap","key","rawVal","val","match","op","opKey","parsedVal","v","parseValue","num","serializeCursor","snapshot","deserializeCursor","entry","cursor","docId","colRef","createCrudHandlers","registry","basePath","verbose","getRepoEntry","repoName","extractPathArgs","doc","pathKey","fullPath","segments","args","fetchDocById","getterName","getter","handleList","pageSize","direction","orderBy","orderDir","selectStr","select","s","includes","inc","queryOptions","cursorObj","f","result","responseData","message","handleQuery","body","validIncludes","allowed","invalid","w","group","handleGet","handleCreate","validation","customError","created","missingKeys","k","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","r","realm","expected","specPath","docsPath","spec","html"],"mappings":"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","file":"index.js","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","/**\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 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 res\n .status(status)\n .set(\"Content-Type\", \"application/json; charset=utf-8\")\n .send(JSON.stringify(data));\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// 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 * 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 finalSchema = partial ? targetSchema.partial() : targetSchema;\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 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\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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to fetch documents\";\n sendError(res, message, 500);\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 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 // 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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to query documents\";\n sendError(res, message, 500);\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 const message =\n verbose && err instanceof Error\n ? err.message\n : \"Failed to fetch document\";\n sendError(res, message, 500);\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/** Convert a Zod schema to a JSON Schema object suitable for OpenAPI 3.1. */\nfunction zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {\n try {\n return z.toJSONSchema(schema, { target: \"openapi-3.1\" }) as Record<\n string,\n unknown\n >;\n } catch {\n // Fallback for unsupported types\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"]}